<?php
/**
 * API para generar códigos de prueba - Versión Corregida
 * Con mejor manejo de errores y debugging
 */

// Habilitar reporte de errores para debugging (comentar en producción)
error_reporting(E_ALL);
ini_set('display_errors', '0'); // No mostrar errores en pantalla
ini_set('log_errors', '1');
ini_set('error_log', __DIR__ . '/php_errors.log');

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

// Iniciar sesión
if (session_status() === PHP_SESSION_NONE) {
    session_start();
}

/**
 * Función para enviar respuesta JSON
 */
function sendResponse($data, $httpCode = 200) {
    http_response_code($httpCode);
    echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    exit;
}

/**
 * Función para registrar en log
 */
function writeLog($message, $data = []) {
    $logFile = __DIR__ . '/api_debug.log';
    $timestamp = date('Y-m-d H:i:s');
    $dataStr = !empty($data) ? ' | ' . json_encode($data, JSON_UNESCAPED_UNICODE) : '';
    $logLine = "[{$timestamp}] {$message}{$dataStr}\n";
    @file_put_contents($logFile, $logLine, FILE_APPEND | LOCK_EX);
}

// Log de inicio
writeLog('API Request Started', [
    'method' => $_SERVER['REQUEST_METHOD'],
    'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
]);

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

// Cooldown anti-spam
$cooldownSeconds = 5;
$lastGenTime = $_SESSION['trial_last_gen'] ?? 0;
$timeSinceLastGen = time() - $lastGenTime;

if ($timeSinceLastGen < $cooldownSeconds) {
    $waitTime = $cooldownSeconds - $timeSinceLastGen;
    writeLog('Cooldown active', ['wait' => $waitTime]);
    sendResponse([
        'ok' => false,
        'success' => false,
        'reason' => 'cooldown',
        'message' => "Por favor, espera {$waitTime} segundos antes de generar otro código."
    ], 429);
}

// Obtener datos del formulario
$name = trim($_POST['name'] ?? '');
$phone = preg_replace('/\D+/', '', $_POST['phone'] ?? '');

writeLog('Form data received', [
    'name_length' => strlen($name),
    'phone_length' => strlen($phone)
]);

// Validar nombre
if (strlen($name) < 2 || strlen($name) > 60) {
    writeLog('Invalid name');
    sendResponse([
        'ok' => false,
        'success' => false,
        'reason' => 'invalid_name',
        'message' => 'El nombre debe tener entre 2 y 60 caracteres.'
    ], 400);
}

// Validar teléfono
if (strlen($phone) < 10 || strlen($phone) > 15) {
    writeLog('Invalid phone');
    sendResponse([
        'ok' => false,
        'success' => false,
        'reason' => 'invalid_phone',
        'message' => 'El teléfono debe tener entre 10 y 15 dígitos.'
    ], 400);
}

// Sanitizar datos
$name = htmlspecialchars($name, ENT_QUOTES, 'UTF-8');
$clientIP = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
$userAgent = substr($_SERVER['HTTP_USER_AGENT'] ?? 'Unknown', 0, 300);

// Verificar base de datos
$dbFile = __DIR__ . '/.db.db';
if (!file_exists($dbFile)) {
    writeLog('Database not found', ['path' => $dbFile]);
    sendResponse([
        'ok' => false,
        'success' => false,
        'reason' => 'db_not_found',
        'message' => 'Error de configuración. Contacte al administrador.'
    ], 500);
}

try {
    // Conectar a base de datos
    $pdo = new PDO('sqlite:' . $dbFile);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    
    writeLog('Database connected');
    
    // Crear tablas si no existen
    $pdo->exec("CREATE TABLE IF NOT EXISTS settings (key TEXT PRIMARY KEY, value TEXT)");
    $pdo->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
    )");
    
    // Obtener configuración
    $stmt = $pdo->prepare('SELECT value FROM settings WHERE key = :key LIMIT 1');
    
    $stmt->execute([':key' => 'trial_days']);
    $row = $stmt->fetch(PDO::FETCH_ASSOC);
    $trialDays = $row ? (int)$row['value'] : 1;
    
    $stmt->execute([':key' => 'trial_hours']);
    $row = $stmt->fetch(PDO::FETCH_ASSOC);
    $trialHours = $row ? (int)$row['value'] : 0;
    
    $stmt->execute([':key' => 'app_download_url']);
    $row = $stmt->fetch(PDO::FETCH_ASSOC);
    $downloadUrl = $row ? $row['value'] : '';
    
    writeLog('Settings loaded', [
        'days' => $trialDays,
        'hours' => $trialHours
    ]);
    
    // Validar y calcular tiempo
    $trialDays = max(0, min(365, $trialDays));
    $trialHours = max(0, min(240, $trialHours));
    $totalSeconds = ($trialDays * 86400) + ($trialHours * 3600);
    if ($totalSeconds <= 0) $totalSeconds = 3600;
    
    $expiresAt = date('Y-m-d H:i:s', time() + $totalSeconds);
    
    // Archivo de registro
    $registryFile = __DIR__ . '/trial_cadastros.json';
    $blockSeconds = max(30 * 86400, $totalSeconds);
    $currentTime = time();
    
    // Abrir archivo con lock
    $fp = @fopen($registryFile, 'c+');
    if (!$fp) {
        writeLog('Failed to open registry file');
        sendResponse([
            'ok' => false,
            'success' => false,
            'reason' => 'file_error',
            'message' => 'Error al procesar la solicitud.'
        ], 500);
    }
    
    if (!flock($fp, LOCK_EX)) {
        fclose($fp);
        writeLog('Failed to lock registry file');
        sendResponse([
            'ok' => false,
            'success' => false,
            'reason' => 'lock_error',
            'message' => 'Sistema ocupado. Intenta en unos segundos.'
        ], 503);
    }
    
    // Leer registros
    $registryData = [];
    $fileSize = filesize($registryFile);
    if ($fileSize > 0) {
        $content = fread($fp, $fileSize);
        if ($content) {
            $decoded = json_decode($content, true);
            if (is_array($decoded)) {
                $registryData = $decoded;
            }
        }
    }
    
    writeLog('Registry loaded', ['records' => count($registryData)]);
    
    // Verificar bloqueo
    foreach ($registryData as $record) {
        $recordPhone = $record['phone'] ?? '';
        $recordIP = $record['ip'] ?? '';
        $blockUntil = (int)($record['block_until_ts'] ?? 0);
        
        if ($blockUntil > $currentTime && ($recordPhone === $phone || $recordIP === $clientIP)) {
            $blockDate = date('d/m/Y H:i', $blockUntil);
            flock($fp, LOCK_UN);
            fclose($fp);
            
            writeLog('User blocked', ['phone' => $phone, 'ip' => $clientIP]);
            
            sendResponse([
                'ok' => false,
                'success' => false,
                'reason' => 'blocked',
                'message' => "Ya generaste un código. Podrás generar otro después del {$blockDate}."
            ], 429);
        }
    }
    
    // Generar código único
    $newCode = null;
    for ($i = 0; $i < 100; $i++) {
        $candidate = str_pad((string)random_int(100000, 999999), 6, '0', STR_PAD_LEFT);
        
        $stmt = $pdo->prepare('SELECT id FROM codes WHERE code = :code LIMIT 1');
        $stmt->execute([':code' => $candidate]);
        
        if (!$stmt->fetch()) {
            $newCode = $candidate;
            break;
        }
    }
    
    if (!$newCode) {
        flock($fp, LOCK_UN);
        fclose($fp);
        
        writeLog('Failed to generate unique code');
        sendResponse([
            'ok' => false,
            'success' => false,
            'reason' => 'code_generation_failed',
            'message' => 'No se pudo generar un código. Intenta nuevamente.'
        ], 500);
    }
    
    writeLog('Code generated', ['code' => $newCode]);
    
    // Insertar en base de datos
    $stmt = $pdo->prepare('INSERT INTO codes (code, expires_at, owner) VALUES (:code, :expires, :owner)');
    $stmt->execute([
        ':code' => $newCode,
        ':expires' => $expiresAt,
        ':owner' => 'trial'
    ]);
    
    // Registrar en JSON
    $registryData[] = [
        'name' => $name,
        'phone' => $phone,
        'ip' => $clientIP,
        '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
    ];
    
    // Limpiar registros antiguos
    $oneYearAgo = $currentTime - (365 * 86400);
    $registryData = array_filter($registryData, function($r) use ($oneYearAgo) {
        return ($r['created_ts'] ?? 0) > $oneYearAgo;
    });
    $registryData = array_values($registryData);
    
    // Escribir archivo
    rewind($fp);
    ftruncate($fp, 0);
    fwrite($fp, json_encode($registryData, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
    flock($fp, LOCK_UN);
    fclose($fp);
    
    // Actualizar sesión
    $_SESSION['trial_last_gen'] = $currentTime;
    
    writeLog('Success', ['code' => $newCode]);
    
    // Respuesta exitosa
    sendResponse([
        'ok' => true,
        'success' => true,
        'code' => $newCode,
        'expires_at' => $expiresAt,
        'expiry' => $expiresAt,
        'duration' => [
            'days' => $trialDays,
            'hours' => $trialHours,
            'seconds' => $totalSeconds
        ],
        'download_url' => $downloadUrl,
        'message' => 'Código generado exitosamente'
    ], 200);
    
} catch (PDOException $e) {
    writeLog('Database error', ['error' => $e->getMessage()]);
    sendResponse([
        'ok' => false,
        'success' => false,
        'reason' => 'database_error',
        'message' => 'Error de base de datos. Contacte al administrador.'
    ], 500);
    
} catch (Exception $e) {
    writeLog('Unexpected error', ['error' => $e->getMessage()]);
    sendResponse([
        'ok' => false,
        'success' => false,
        'reason' => 'server_error',
        'message' => 'Error interno. Intenta nuevamente.'
    ], 500);
}