<?php

declare(strict_types=1);

require_once __DIR__ . '/../init.php';

/**
 * Respuesta JSON breve y cerrada. En hosting compartido evita que el navegador
 * permanezca en "Guardando..." aunque la consulta ya haya terminado.
 */
function af_edit_subscription_json(array $payload, int $status = 200): never
{
    if (session_status() === PHP_SESSION_ACTIVE) {
        session_write_close();
    }

    while (ob_get_level() > 0) {
        @ob_end_clean();
    }

    $json = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    if (!is_string($json)) {
        $status = 500;
        $json = '{"success":false,"message":"No fue posible generar la respuesta del servidor."}';
    }

    if (!headers_sent()) {
        http_response_code($status);
        header('Content-Type: application/json; charset=utf-8');
        header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
        header('X-Content-Type-Options: nosniff');
        header('Content-Length: ' . strlen($json));
        header('Connection: close');
    }

    echo $json;
    @flush();
    if (function_exists('fastcgi_finish_request')) {
        @fastcgi_finish_request();
    }
    exit;
}

Security::headers(true);

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

Security::requireCsrf();

$role = mb_strtolower(trim((string) ($_SESSION['admin']['rol'] ?? '')), 'UTF-8');
if ($role !== 'superadmin') {
    af_edit_subscription_json(['success' => false, 'message' => 'Solo el Superadmin puede editar estas suscripciones.'], 403);
}

// La sesión ya no es necesaria durante la transacción y no debe bloquear otras
// peticiones del mismo panel.
if (session_status() === PHP_SESSION_ACTIVE) {
    session_write_close();
}

@ignore_user_abort(true);
@set_time_limit(15);

$id = filter_input(INPUT_POST, 'id', FILTER_VALIDATE_INT) ?: 0;
$originalAdminId = filter_input(INPUT_POST, 'id_admin_original', FILTER_VALIDATE_INT) ?: 0;
$planId = filter_input(INPUT_POST, 'id_plan', FILTER_VALIDATE_INT) ?: 0;
$cycle = trim((string) ($_POST['ciclo_facturacion'] ?? ''));
$status = trim((string) ($_POST['estado'] ?? ''));
$startDate = trim((string) ($_POST['fecha_inicio'] ?? ''));
$endDate = trim((string) ($_POST['fecha_fin'] ?? ''));
$subscriberName = trim((string) ($_POST['nombre_usuario'] ?? ''));

try {
    if ($id <= 0 || $planId <= 0) {
        throw new InvalidArgumentException('La suscripción o el plan no son válidos.');
    }
    if (!in_array($cycle, ['mensual', 'anual', 'prueba', 'interno'], true)) {
        throw new InvalidArgumentException('Selecciona una modalidad válida.');
    }
    if (!in_array($status, ['activa', 'prueba', 'vencida', 'cancelada'], true)) {
        throw new InvalidArgumentException('Selecciona un estado válido.');
    }
    if (mb_strlen($subscriberName, 'UTF-8') < 2 || mb_strlen($subscriberName, 'UTF-8') > 100) {
        throw new InvalidArgumentException('El nombre debe tener entre 2 y 100 caracteres.');
    }

    $start = DateTimeImmutable::createFromFormat('!Y-m-d', $startDate);
    $end = DateTimeImmutable::createFromFormat('!Y-m-d', $endDate);
    if (!$start || !$end || $start->format('Y-m-d') !== $startDate || $end->format('Y-m-d') !== $endDate) {
        throw new InvalidArgumentException('Las fechas recibidas no son válidas.');
    }
    if ($end < $start) {
        throw new InvalidArgumentException('La fecha final debe ser igual o posterior a la fecha inicial.');
    }

    $currentStmt = $pdo->prepare(
        "SELECT s.id, s.id_admin, s.id_plan, s.ciclo_facturacion,
                u.correo, u.nombre_usuario, r.nombre_rol
         FROM suscripciones s
         JOIN usuarios u ON u.id = s.id_admin
         JOIN roles r ON r.id = u.id_rol
         WHERE s.id = ?
         LIMIT 1"
    );
    $currentStmt->execute([$id]);
    $current = $currentStmt->fetch(PDO::FETCH_ASSOC);
    if (!$current) {
        throw new RuntimeException('La suscripción ya no existe.');
    }

    $adminId = (int) $current['id_admin'];
    if ($originalAdminId > 0 && $originalAdminId !== $adminId) {
        throw new RuntimeException('El suscriptor cambió mientras editabas. Recarga la página.');
    }
    if (mb_strtolower((string) $current['nombre_rol'], 'UTF-8') === 'superadmin') {
        throw new RuntimeException('La suscripción interna del Superadmin está protegida.');
    }

    $planStmt = $pdo->prepare(
        'SELECT id, activo, es_interno, permite_mensual, permite_anual
         FROM planes WHERE id = ? LIMIT 1'
    );
    $planStmt->execute([$planId]);
    $plan = $planStmt->fetch(PDO::FETCH_ASSOC);
    if (!$plan) {
        throw new RuntimeException('El plan seleccionado no existe.');
    }

    $samePlan = (int) $current['id_plan'] === $planId;
    if ((int) ($plan['activo'] ?? 0) !== 1 && !$samePlan && $status !== 'cancelada') {
        throw new RuntimeException('No se puede cambiar a un plan inactivo.');
    }
    if ((int) ($plan['es_interno'] ?? 0) === 1) {
        if (!$samePlan) {
            throw new RuntimeException('No se puede asignar un plan interno protegido.');
        }
        $cycle = 'interno';
    }
    if ($cycle === 'mensual' && (int) ($plan['permite_mensual'] ?? 0) !== 1
        && !($samePlan && (string) $current['ciclo_facturacion'] === 'mensual')) {
        throw new RuntimeException('Este plan no tiene modalidad mensual habilitada.');
    }
    if ($cycle === 'anual' && (int) ($plan['permite_anual'] ?? 0) !== 1
        && !($samePlan && (string) $current['ciclo_facturacion'] === 'anual')) {
        throw new RuntimeException('Este plan no tiene modalidad anual habilitada.');
    }

    // Evita esperas largas si otra operación está usando la misma suscripción.
    try {
        $pdo->exec('SET SESSION innodb_lock_wait_timeout = 5');
    } catch (Throwable $ignored) {
    }

    $pdo->beginTransaction();

    $updateSubscription = $pdo->prepare(
        'UPDATE suscripciones
         SET id_plan = ?, ciclo_facturacion = ?, fecha_inicio = ?, fecha_fin = ?, estado = ?
         WHERE id = ? AND id_admin = ?'
    );
    $updateSubscription->execute([
        $planId,
        $cycle,
        $start->format('Y-m-d 00:00:00'),
        $end->format('Y-m-d 23:59:59'),
        $status,
        $id,
        $adminId,
    ]);

    $updateUser = $pdo->prepare('UPDATE usuarios SET nombre_usuario = ?, id_plan = ? WHERE id = ?');
    $updateUser->execute([$subscriberName, $planId, $adminId]);

    $verifyStmt = $pdo->prepare(
        "SELECT s.id_plan, s.ciclo_facturacion, DATE(s.fecha_inicio) AS fecha_inicio,
                DATE(s.fecha_fin) AS fecha_fin, s.estado, u.nombre_usuario, u.correo
         FROM suscripciones s
         JOIN usuarios u ON u.id = s.id_admin
         WHERE s.id = ? AND s.id_admin = ? LIMIT 1"
    );
    $verifyStmt->execute([$id, $adminId]);
    $saved = $verifyStmt->fetch(PDO::FETCH_ASSOC);

    if (!$saved
        || (int) $saved['id_plan'] !== $planId
        || (string) $saved['ciclo_facturacion'] !== $cycle
        || (string) $saved['fecha_inicio'] !== $startDate
        || (string) $saved['fecha_fin'] !== $endDate
        || (string) $saved['estado'] !== $status
        || (string) $saved['nombre_usuario'] !== $subscriberName
    ) {
        throw new RuntimeException('La base de datos no confirmó todos los cambios.');
    }

    $pdo->commit();

    af_edit_subscription_json([
        'success' => true,
        'message' => 'Suscripción actualizada correctamente. El correo se conservó sin cambios.',
        'data' => [
            'id' => $id,
            'id_admin' => $adminId,
            'id_plan' => $planId,
            'ciclo_facturacion' => $cycle,
            'fecha_inicio' => $startDate,
            'fecha_fin' => $endDate,
            'estado' => $status,
            'nombre_usuario' => $subscriberName,
            'correo' => (string) $saved['correo'],
        ],
    ]);
} catch (PDOException $e) {
    if ($pdo->inTransaction()) {
        $pdo->rollBack();
    }
    error_log('[editar_suscripcion] ' . $e->getMessage());
    $message = APP_DEBUG
        ? $e->getMessage()
        : (((string) $e->getCode() === '40001' || (string) $e->getCode() === 'HY000')
            ? 'La suscripción estaba siendo utilizada por otra operación. Espera unos segundos y vuelve a guardar.'
            : 'No fue posible guardar los cambios en la base de datos.');
    af_edit_subscription_json(['success' => false, 'message' => $message], 409);
} catch (Throwable $e) {
    if ($pdo->inTransaction()) {
        $pdo->rollBack();
    }
    error_log('[editar_suscripcion] ' . $e->getMessage());
    af_edit_subscription_json(['success' => false, 'message' => $e->getMessage()], 422);
}
