<?php
error_reporting(E_ALL);
ini_set('display_errors', '0');
ini_set('log_errors', '1');
ini_set('error_log', __DIR__ . '/php_errors.log');

header('Content-Type: application/json; charset=utf-8');
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
header('Pragma: no-cache');

if (session_status() === PHP_SESSION_NONE) {
    session_start();
}

function sendResponse($data, $httpCode = 200) {
    http_response_code($httpCode);
    echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    exit;
}

function writeLog($message, $data = []) {
    $logFile = __DIR__ . '/api_debug.log';
    $timestamp = date('Y-m-d H:i:s');
    $payload = $data ? ' | ' . json_encode($data, JSON_UNESCAPED_UNICODE) : '';
    @file_put_contents($logFile, '[' . $timestamp . '] ' . $message . $payload . PHP_EOL, FILE_APPEND | LOCK_EX);
}

function detectClientIp() {
    foreach (['HTTP_CF_CONNECTING_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'REMOTE_ADDR'] as $key) {
        if (empty($_SERVER[$key])) continue;
        $value = trim((string)$_SERVER[$key]);
        if ($key === 'HTTP_X_FORWARDED_FOR') {
            $parts = explode(',', $value);
            $value = trim($parts[0]);
        }
        if (filter_var($value, FILTER_VALIDATE_IP)) return $value;
    }
    return '0.0.0.0';
}

function normalizeDeviceId($value) {
    $value = trim((string)$value);
    if ($value === '') return '';
    return hash('sha256', substr($value, 0, 500));
}

function sqliteSingleValue(SQLite3 $db, $key, $default = null) {
    $stmt = $db->prepare('SELECT value FROM settings WHERE key = :k LIMIT 1');
    $stmt->bindValue(':k', $key, SQLITE3_TEXT);
    $res = $stmt->execute();
    $row = $res ? $res->fetchArray(SQLITE3_ASSOC) : false;
    return ($row && array_key_exists('value', $row)) ? $row['value'] : $default;
}

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    sendResponse(['ok' => false, 'success' => false, 'reason' => 'method_not_allowed', 'message' => 'Método HTTP no permitido.'], 405);
}

$cooldownSeconds = 5;
$lastGenTime = $_SESSION['trial_last_gen'] ?? 0;
if ((time() - $lastGenTime) < $cooldownSeconds) {
    $waitTime = $cooldownSeconds - (time() - $lastGenTime);
    sendResponse(['ok' => false, 'success' => false, 'reason' => 'cooldown', 'message' => "Por favor, espera {$waitTime} segundos antes de intentar nuevamente."], 429);
}

$name = trim($_POST['name'] ?? '');
$phone = preg_replace('/\D+/', '', $_POST['phone'] ?? '');
$deviceId = normalizeDeviceId($_POST['device_id'] ?? '');
$clientIP = detectClientIp();
$userAgent = substr($_SERVER['HTTP_USER_AGENT'] ?? 'Unknown', 0, 300);

if (strlen($name) < 2 || strlen($name) > 60) {
    sendResponse(['ok' => false, 'success' => false, 'reason' => 'invalid_name', 'message' => 'El nombre debe tener entre 2 y 60 caracteres.'], 400);
}
if (strlen($phone) < 10 || strlen($phone) > 15) {
    sendResponse(['ok' => false, 'success' => false, 'reason' => 'invalid_phone', 'message' => 'El teléfono debe tener entre 10 y 15 dígitos.'], 400);
}

$dbFile = __DIR__ . '/.db.db';
if (!file_exists($dbFile)) {
    sendResponse(['ok' => false, 'success' => false, 'reason' => 'db_not_found', 'message' => 'Base de datos no encontrada.'], 500);
}

try {
    $db = new SQLite3($dbFile);
    $db->exec("CREATE TABLE IF NOT EXISTS settings (key TEXT PRIMARY KEY, value TEXT)");
    $db->exec("CREATE TABLE IF NOT EXISTS codes (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        code TEXT NOT NULL UNIQUE,
        expires_at TEXT NOT NULL,
        owner TEXT NOT NULL,
        created_at DATETIME DEFAULT CURRENT_TIMESTAMP
    )");
    foreach ([
        "ALTER TABLE codes ADD COLUMN client_name TEXT",
        "ALTER TABLE codes ADD COLUMN client_whatsapp TEXT",
        "ALTER TABLE codes ADD COLUMN status TEXT DEFAULT 'active'",
        "ALTER TABLE codes ADD COLUMN is_free_trial INTEGER DEFAULT 0",
        "ALTER TABLE codes ADD COLUMN trial_hours INTEGER DEFAULT 0",
        "ALTER TABLE codes ADD COLUMN client_ip TEXT",
        "ALTER TABLE codes ADD COLUMN device_id TEXT"
    ] as $sql) {
        try { @$db->exec($sql); } catch (Throwable $e) {}
    }

    $trialDays = (int)sqliteSingleValue($db, 'trial_days', '0');
    $trialHours = (int)sqliteSingleValue($db, 'trial_hours', '4');
    $downloadUrl = (string)sqliteSingleValue($db, 'app_download_url', '');
    $downloaderCode = (string)sqliteSingleValue($db, 'app_downloader_code', '');

    $trialDays = max(0, min(365, $trialDays));
    $trialHours = max(0, min(240, $trialHours));
    $totalSeconds = ($trialDays * 86400) + ($trialHours * 3600);
    if ($totalSeconds <= 0) $totalSeconds = 4 * 3600;
    $expiresAt = date('Y-m-d H:i:s', time() + $totalSeconds);
    $blockSeconds = max(30 * 86400, $totalSeconds);
    $currentTime = time();

    $registryFile = __DIR__ . '/trial_cadastros.json';
    $fp = @fopen($registryFile, 'c+');
    if (!$fp) {
        sendResponse(['ok' => false, 'success' => false, 'reason' => 'file_error', 'message' => 'No se pudo procesar la solicitud.'], 500);
    }
    if (!flock($fp, LOCK_EX)) {
        fclose($fp);
        sendResponse(['ok' => false, 'success' => false, 'reason' => 'lock_error', 'message' => 'Sistema ocupado. Intenta en unos segundos.'], 503);
    }

    $registryData = [];
    $content = stream_get_contents($fp);
    if ($content) {
        $decoded = json_decode($content, true);
        if (is_array($decoded)) $registryData = $decoded;
    }

    $matches = [];
    foreach ($registryData as $record) {
        $blockUntil = (int)($record['block_until_ts'] ?? 0);
        if ($blockUntil <= $currentTime) continue;
        if (($record['phone'] ?? '') === $phone) $matches[] = 'teléfono';
        if (($record['ip'] ?? '') === $clientIP) $matches[] = 'IP';
        if ($deviceId !== '' && ($record['device_id'] ?? '') === $deviceId) $matches[] = 'dispositivo';
        if ($matches) {
            $blockDate = date('d/m/Y H:i', $blockUntil);
            flock($fp, LOCK_UN);
            fclose($fp);
            sendResponse([
                'ok' => false,
                'success' => false,
                'reason' => 'blocked',
                'message' => 'Ya se generó una prueba para este teléfono, IP o dispositivo. Podrás solicitar otra después del ' . $blockDate . '.',
                'blocked_by' => array_values(array_unique($matches))
            ], 429);
        }
    }

    $where = "owner = 'trial' AND (client_whatsapp = :phone OR client_ip = :ip" . ($deviceId !== '' ? ' OR device_id = :device_id' : '') . ") AND created_at >= :limit ORDER BY created_at DESC LIMIT 1";
    $stmt = $db->prepare('SELECT created_at FROM codes WHERE ' . $where);
    $stmt->bindValue(':phone', $phone, SQLITE3_TEXT);
    $stmt->bindValue(':ip', $clientIP, SQLITE3_TEXT);
    if ($deviceId !== '') $stmt->bindValue(':device_id', $deviceId, SQLITE3_TEXT);
    $stmt->bindValue(':limit', date('Y-m-d H:i:s', $currentTime - $blockSeconds), SQLITE3_TEXT);
    $existing = $stmt->execute();
    $existingRow = $existing ? $existing->fetchArray(SQLITE3_ASSOC) : false;
    if ($existingRow) {
        flock($fp, LOCK_UN);
        fclose($fp);
        sendResponse(['ok' => false, 'success' => false, 'reason' => 'blocked', 'message' => 'Ya existe una prueba registrada para estos datos. Solo se permite una demo por teléfono, IP o dispositivo.'], 429);
    }

    $newCode = null;
    for ($i = 0; $i < 100; $i++) {
        $candidate = str_pad((string)random_int(100000, 999999), 6, '0', STR_PAD_LEFT);
        $stmt = $db->prepare('SELECT id FROM codes WHERE code = :code LIMIT 1');
        $stmt->bindValue(':code', $candidate, SQLITE3_TEXT);
        $res = $stmt->execute();
        if (!$res || !$res->fetchArray(SQLITE3_ASSOC)) {
            $newCode = $candidate;
            break;
        }
    }
    if (!$newCode) {
        flock($fp, LOCK_UN);
        fclose($fp);
        sendResponse(['ok' => false, 'success' => false, 'reason' => 'code_generation_failed', 'message' => 'No se pudo generar un código.'], 500);
    }

    $insert = $db->prepare('INSERT INTO codes (code, expires_at, owner, created_at, client_name, client_whatsapp, status, is_free_trial, trial_hours, client_ip, device_id) VALUES (:code, :expires_at, :owner, :created_at, :client_name, :client_whatsapp, :status, :is_free_trial, :trial_hours, :client_ip, :device_id)');
    $insert->bindValue(':code', $newCode, SQLITE3_TEXT);
    $insert->bindValue(':expires_at', $expiresAt, SQLITE3_TEXT);
    $insert->bindValue(':owner', 'trial', SQLITE3_TEXT);
    $insert->bindValue(':created_at', date('Y-m-d H:i:s'), SQLITE3_TEXT);
    $insert->bindValue(':client_name', htmlspecialchars($name, ENT_QUOTES, 'UTF-8'), SQLITE3_TEXT);
    $insert->bindValue(':client_whatsapp', $phone, SQLITE3_TEXT);
    $insert->bindValue(':status', 'active', SQLITE3_TEXT);
    $insert->bindValue(':is_free_trial', 1, SQLITE3_INTEGER);
    $insert->bindValue(':trial_hours', (int)round($totalSeconds / 3600), SQLITE3_INTEGER);
    $insert->bindValue(':client_ip', $clientIP, SQLITE3_TEXT);
    $insert->bindValue(':device_id', $deviceId, SQLITE3_TEXT);
    $ok = $insert->execute();
    if (!$ok) {
        flock($fp, LOCK_UN);
        fclose($fp);
        sendResponse(['ok' => false, 'success' => false, 'reason' => 'insert_failed', 'message' => 'No se pudo guardar la prueba.'], 500);
    }

    $registryData[] = [
        'name' => htmlspecialchars($name, ENT_QUOTES, 'UTF-8'),
        'phone' => $phone,
        'ip' => $clientIP,
        'device_id' => $deviceId,
        'user_agent' => $userAgent,
        'code' => $newCode,
        'created_at' => date('Y-m-d H:i:s'),
        'created_ts' => $currentTime,
        'expires_at' => $expiresAt,
        'block_until_ts' => $currentTime + $blockSeconds
    ];
    $registryData = array_values(array_filter($registryData, function ($r) use ($currentTime) {
        return ((int)($r['created_ts'] ?? 0)) > ($currentTime - (365 * 86400));
    }));

    rewind($fp);
    ftruncate($fp, 0);
    fwrite($fp, json_encode($registryData, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
    flock($fp, LOCK_UN);
    fclose($fp);

    $_SESSION['trial_last_gen'] = $currentTime;
    writeLog('trial_created', ['code' => $newCode, 'phone' => $phone, 'ip' => $clientIP, 'device' => $deviceId !== '']);

    sendResponse([
        'ok' => true,
        'success' => true,
        'code' => $newCode,
        'expires_at' => $expiresAt,
        'duration' => [
            'days' => $trialDays,
            'hours' => (int)round($totalSeconds / 3600),
            'seconds' => $totalSeconds
        ],
        'download_url' => $downloadUrl,
        'downloader_code' => $downloaderCode,
        'message' => 'Código generado exitosamente',
        'one_demo_rule' => ['phone', 'ip', 'device']
    ], 200);
} catch (Throwable $e) {
    writeLog('error', ['message' => $e->getMessage()]);
    sendResponse(['ok' => false, 'success' => false, 'reason' => 'server_error', 'message' => 'Error interno. Intenta nuevamente.'], 500);
}
