<?php
if (count(get_included_files()) === 1) {
    http_response_code(403);
    exit('Forbidden');
}

require_once dirname(__DIR__) . '/services/google_oauth.php';
require_once dirname(__DIR__) . '/services/token_crypto.php';
require_once dirname(__DIR__) . '/services/mailer.php';

$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);

if (!GoogleOAuth::isConfigured()) {
    http_response_code(503);
    exit('Login con Google no configurado (faltan GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET en el .env).');
}

// ──────────────────────────────────────────────────────────────
// GET /auth/google — genera state en DB y redirige a Google
// ──────────────────────────────────────────────────────────────
if ($uri === '/auth/google') {
    $state = bin2hex(random_bytes(32));
    $db = DB::connect();

    // Guardar state en DB (anti-CSRF, sobrevive load balancers)
    try {
        $db->prepare("INSERT INTO oauth_states (state) VALUES (:s)")->execute([':s' => $state]);
        // Limpiar states viejos (> 20 min) de paso
        $db->exec("DELETE FROM oauth_states WHERE created_at < DATE_SUB(NOW(), INTERVAL 20 MINUTE)");
    } catch (Throwable $e) {
        // Fallback a SESSION si la tabla no existe aún
        $_SESSION['oauth_state'] = $state;
    }

    $_SESSION['oauth_mode'] = 'login';

    // Guardar selecciones del wizard para después del round-trip OAuth
    $validChannels = ['web', 'whatsapp', 'sms'];
    if (isset($_GET['lang']) && in_array($_GET['lang'], ['es', 'en'], true)) {
        $_SESSION['pending_lang'] = $_GET['lang'];
    }
    if (!empty($_GET['country'])) {
        $_SESSION['pending_country'] = strtoupper(substr(preg_replace('/[^A-Za-z]/', '', $_GET['country']), 0, 2));
    }
    if (isset($_GET['channel']) && in_array($_GET['channel'], $validChannels, true)) {
        $_SESSION['pending_channel'] = $_GET['channel'];
    }
    $validPlans = ['free', 'growth', 'pro'];
    if (isset($_GET['plan']) && in_array($_GET['plan'], $validPlans, true)) {
        $_SESSION['pending_plan'] = $_GET['plan'];
    }

    $forceConsent = (isset($_GET['force_consent']) && $_GET['force_consent'] === '1');
    header('Location: ' . GoogleOAuth::authUrl($state, GoogleOAuth::CALENDAR_SCOPES, $forceConsent));
    exit;
}

// ──────────────────────────────────────────────────────────────
// GET /auth/google/calendar — reconectar Calendar (usuario ya logueado)
// ──────────────────────────────────────────────────────────────
if ($uri === '/auth/google/calendar') {
    if (!isset($_SESSION['user_id'])) {
        header('Location: /login');
        exit;
    }
    $state = bin2hex(random_bytes(32));
    $db = DB::connect();
    try {
        $db->prepare("INSERT INTO oauth_states (state) VALUES (:s)")->execute([':s' => $state]);
    } catch (Throwable $e) {
        $_SESSION['oauth_state'] = $state;
    }
    $_SESSION['oauth_mode'] = 'calendar';
    header('Location: ' . GoogleOAuth::authUrl($state, GoogleOAuth::CALENDAR_SCOPES, true));
    exit;
}

// ──────────────────────────────────────────────────────────────
// GET /auth/google/callback — Google vuelve con code + state
// ──────────────────────────────────────────────────────────────
if ($uri === '/auth/google/callback') {

    // 1. Validar state (DB primero, SESSION como fallback)
    $state = $_GET['state'] ?? '';
    if ($state === '') {
        http_response_code(400);
        exit('Estado OAuth ausente. Volvé a intentar.');
    }

    $db = DB::connect();
    $stateValid = false;
    try {
        $stmt = $db->prepare(
            "SELECT id FROM oauth_states
             WHERE state = :s AND created_at > DATE_SUB(NOW(), INTERVAL 20 MINUTE)"
        );
        $stmt->execute([':s' => $state]);
        if ($stmt->fetch()) {
            $stateValid = true;
            $db->prepare("DELETE FROM oauth_states WHERE state = :s")->execute([':s' => $state]);
        }
    } catch (Throwable $e) {
        // Fallback SESSION
        if (isset($_SESSION['oauth_state']) && $_SESSION['oauth_state'] === $state) {
            $stateValid = true;
        }
    }
    unset($_SESSION['oauth_state']);

    if (!$stateValid) {
        http_response_code(403);
        exit('Estado OAuth inválido. Volvé a intentar.');
    }

    // 2. Error enviado por Google
    if (!empty($_GET['error']) || empty($_GET['code'])) {
        header('Location: /login');
        exit;
    }

    // 3. Intercambiar code por tokens
    try {
        $tokens = GoogleOAuth::exchangeCode($_GET['code']);
        $claims = GoogleOAuth::decodeIdToken($tokens['id_token'] ?? '');
    } catch (Throwable $e) {
        error_log('Google OAuth exchangeCode failed: ' . $e->getMessage());
        http_response_code(502);
        exit('No se pudo autenticar con Google. Intentá de nuevo.');
    }

    $sub     = $claims['sub']     ?? '';
    $email   = $claims['email']   ?? '';
    $name    = $claims['name']    ?? ($email ?: 'Dueño');
    $picture = $claims['picture'] ?? null;

    if ($sub === '') {
        http_response_code(502);
        exit('Respuesta de Google inválida.');
    }

    $newRefreshToken = $tokens['refresh_token'] ?? null;

    // ── Modo Calendar (reconexión incremental) ──────────────────
    if (($_SESSION['oauth_mode'] ?? '') === 'calendar') {
        unset($_SESSION['oauth_mode']);
        if (isset($_SESSION['user_id']) && $newRefreshToken) {
            $userId = (int) $_SESSION['user_id'];
            $db->beginTransaction();
            try {
                $enc = TokenCrypto::encrypt($newRefreshToken);
                $db->prepare(
                    "INSERT INTO google_tokens (user_id, refresh_token) VALUES (:u, :r)
                     ON DUPLICATE KEY UPDATE refresh_token = VALUES(refresh_token), revoked_at = NULL, updated_at = NOW()"
                )->execute([':u' => $userId, ':r' => $enc]);
                // Backward compat: también en tenants
                $db->prepare(
                    "UPDATE tenants SET google_refresh_token = :r, google_email = :ge,
                     google_calendar_connected = 1, google_connected_at = NOW()
                     WHERE id = :t"
                )->execute([':r' => $newRefreshToken, ':ge' => $email, ':t' => (int) $_SESSION['tenant_id']]);
                $db->commit();
            } catch (Throwable $e) {
                if ($db->inTransaction()) $db->rollBack();
                error_log('Calendar token save failed: ' . $e->getMessage());
            }
            // Crear el calendario secundario si aún no existe para este tenant.
            try {
                require_once dirname(__DIR__) . '/services/google_calendar.php';
                $tenantIdCal = (int) $_SESSION['tenant_id'];
                $rowCal      = $db->prepare(
                    "SELECT t.timezone, vc.calendar_id
                     FROM tenants t LEFT JOIN vale_config vc ON vc.tenant_id = t.id
                     WHERE t.id = :t AND t.deleted_at IS NULL LIMIT 1"
                );
                $rowCal->execute([':t' => $tenantIdCal]);
                $calRow = $rowCal->fetch();
                if ($calRow && empty($calRow['calendar_id'])) {
                    $tz       = $calRow['timezone'] ?: 'America/Managua';
                    error_log("[google_auth/calendar] tenant=$tenantIdCal tz=$tz: calling createCalendar");
                    $newCalId = GoogleCalendar::createCalendar($newRefreshToken, 'Vale AI', $tz);
                    error_log("[google_auth/calendar] tenant=$tenantIdCal: createCalendar => " . var_export($newCalId, true));
                    if ($newCalId) {
                        $db->prepare(
                            "INSERT INTO vale_config (tenant_id, calendar_id) VALUES (:t, :c)
                             ON DUPLICATE KEY UPDATE calendar_id = VALUES(calendar_id)"
                        )->execute([':t' => $tenantIdCal, ':c' => $newCalId]);
                    }
                }
            } catch (Throwable $e) {
                error_log('[google_auth/calendar] createCalendar: ' . $e->getMessage());
            }
        }
        // Si el usuario sigue en onboarding, volvé al onboarding (no a ajustes)
        $inOnboarding = false;
        if (isset($_SESSION['tenant_id'])) {
            try {
                $obStmt = $db->prepare("SELECT onboarding_status FROM tenants WHERE id = :t AND deleted_at IS NULL LIMIT 1");
                $obStmt->execute([':t' => (int)$_SESSION['tenant_id']]);
                $inOnboarding = ($obStmt->fetchColumn() !== 'live');
            } catch (Throwable $e) { /* no-op */ }
        }
        header('Location: ' . ($inOnboarding ? '/onboarding?calendar=ok' : '/ajustes?calendar=ok'));
        exit;
    }
    unset($_SESSION['oauth_mode']);

    // ── Buscar usuario existente ────────────────────────────────
    $stmt = $db->prepare(
        "SELECT u.*, t.name AS tenant_name, t.onboarding_status
         FROM users u
         JOIN tenants t ON u.tenant_id = t.id AND t.deleted_at IS NULL
         WHERE u.google_sub = :sub OR u.email = :email LIMIT 1"
    );
    $stmt->execute([':sub' => $sub, ':email' => $email]);
    $user = $stmt->fetch();

    // Helper: guardar refresh_token cifrado
    $saveToken = function (int $userId, string $rawToken) use ($db): void {
        try {
            $enc = TokenCrypto::encrypt($rawToken);
            $db->prepare(
                "INSERT INTO google_tokens (user_id, refresh_token) VALUES (:u, :r)
                 ON DUPLICATE KEY UPDATE refresh_token = VALUES(refresh_token), revoked_at = NULL, updated_at = NOW()"
            )->execute([':u' => $userId, ':r' => $enc]);
        } catch (Throwable $e) {
            error_log('Token save failed: ' . $e->getMessage());
        }
    };

    // Helper: redirect para forzar consent una sola vez
    $forceConsentOnce = function (): void {
        if (isset($_SESSION['force_consent_attempted'])) {
            return; // ya lo intentamos, no loopear
        }
        $_SESSION['force_consent_attempted'] = true;
        $q = http_build_query(array_filter([
            'force_consent' => '1',
            'lang'          => $_SESSION['pending_lang']    ?? '',
            'country'       => $_SESSION['pending_country'] ?? '',
            'channel'       => $_SESSION['pending_channel'] ?? '',
            'plan'          => $_SESSION['pending_plan']    ?? '',
        ]));
        header('Location: /auth/google?' . $q);
        exit;
    };

    // ── USUARIO EXISTENTE (login de retorno) ────────────────────
    if ($user) {
        $userId   = (int) $user['id'];
        $tenantId = (int) $user['tenant_id'];

        // Vincular google_sub si entró antes por email
        if (empty($user['google_sub'])) {
            $db->prepare("UPDATE users SET google_sub = :s, avatar_url = :a WHERE id = :id")
               ->execute([':s' => $sub, ':a' => $picture, ':id' => $userId]);
        }

        // Manejo del refresh_token
        if ($newRefreshToken) {
            $saveToken($userId, $newRefreshToken);
            unset($_SESSION['force_consent_attempted']);
            // Backward compat en tenants
            try {
                $db->prepare(
                    "UPDATE tenants SET google_refresh_token = :r, google_email = :ge,
                     google_calendar_connected = 1, google_connected_at = NOW() WHERE id = :t"
                )->execute([':r' => $newRefreshToken, ':ge' => $email, ':t' => $tenantId]);
            } catch (Throwable $e) {
                error_log('Tenant token compat update failed: ' . $e->getMessage());
            }
        } else {
            // No vino refresh_token — ¿tenemos uno guardado?
            try {
                $stmt2 = $db->prepare(
                    "SELECT refresh_token FROM google_tokens WHERE user_id = :u AND revoked_at IS NULL"
                );
                $stmt2->execute([':u' => $userId]);
                $storedEnc = $stmt2->fetchColumn();
            } catch (Throwable $e) {
                $storedEnc = false;
            }

            if (!$storedEnc) {
                // No tenemos token → forzar consent una sola vez para conseguirlo
                $forceConsentOnce();
            }
            // Si llegamos acá tenemos token guardado, todo bien
        }

        // Actualizar last_login_at
        try {
            $db->prepare("UPDATE users SET last_login_at = NOW() WHERE id = :u")
               ->execute([':u' => $userId]);
        } catch (Throwable $e) { /* columna puede no existir aún */ }

        // Iniciar sesión
        session_regenerate_id(true);
        $_SESSION['user_id']     = $userId;
        $_SESSION['tenant_id']   = $tenantId;
        $_SESSION['tenant_name'] = $user['tenant_name'];
        $_SESSION['user_name']   = $user['name'];
        $_SESSION['user_email']  = $email;
        $_SESSION['user_role']   = $user['role'];
        unset($_SESSION['force_consent_attempted']);

        header('Location: ' . (($user['onboarding_status'] ?? '') === 'live' ? '/inicio' : '/onboarding'));
        exit;
    }

    // ── USUARIO NUEVO (primer registro) ────────────────────────
    if (!$newRefreshToken) {
        // Sin refresh_token en el primer registro → forzar consent
        $forceConsentOnce();
    }

    $db->beginTransaction();
    try {
        $publicKey      = bin2hex(random_bytes(16));
        $pendingName    = mb_substr(trim($_SESSION['pending_name'] ?? ''), 0, 160) ?: 'Mi Negocio';
        $pendingType    = mb_substr(trim($_SESSION['pending_type'] ?? ''), 0, 80)  ?: '';
        $pendingCountry = $_SESSION['pending_country'] ?? 'NI';
        $db->prepare("INSERT INTO tenants (name, web_public_key, google_email, business_type, country) VALUES (:n, :k, :ge, :bt, :c)")
           ->execute([':n' => $pendingName, ':k' => $publicKey, ':ge' => $email, ':bt' => $pendingType ?: null, ':c' => $pendingCountry ?: 'NI']);
        $tenantId = (int) $db->lastInsertId();

        // Rubro e especialidad del wizard
        $pendingRubro = mb_substr(trim($_SESSION['pending_rubro'] ?? ''), 0, 20) ?: null;
        $pendingEspec = mb_substr(trim($_SESSION['pending_especialidad'] ?? ''), 0, 120) ?: null;
        if ($pendingRubro) {
            $db->prepare("UPDATE tenants SET rubro = :r, especialidad = :e WHERE id = :t")
               ->execute([':r' => $pendingRubro, ':e' => $pendingEspec, ':t' => $tenantId]);
        }

        // Saludo inicial
        $greeting = '¡Hola! Soy Vale, tu asistente virtual. ¿En qué te puedo ayudar?';
        $db->prepare("INSERT INTO vale_config (tenant_id, greeting) VALUES (:t, :g)")
           ->execute([':t' => $tenantId, ':g' => $greeting]);

        // Horarios del wizard
        $pendingHours = trim($_SESSION['pending_horarios'] ?? '');
        if ($pendingHours) {
            $db->prepare(
                "INSERT INTO vale_config (tenant_id, hours_json) VALUES (:t, :h)
                 ON DUPLICATE KEY UPDATE hours_json = VALUES(hours_json)"
            )->execute([':t' => $tenantId, ':h' => json_encode(['texto' => $pendingHours])]);
        }

        // Servicios seleccionados en el wizard
        $pendingSvcs = $_SESSION['pending_servicios'] ?? [];
        if (is_array($pendingSvcs)) {
            $svcStmt = $db->prepare(
                "INSERT IGNORE INTO services (tenant_id, name, duration_min, price_min, active)
                 VALUES (:t, :n, :d, :p, 1)"
            );
            foreach ($pendingSvcs as $svc) {
                $nombre = mb_substr(trim((string)($svc['nombre'] ?? '')), 0, 120);
                if (!$nombre) continue;
                $dur   = is_numeric($svc['duracion_min']    ?? null) ? (int)$svc['duracion_min']    : null;
                $price = is_numeric($svc['precio_sugerido'] ?? null) ? (float)$svc['precio_sugerido'] : null;
                $svcStmt->execute([':t' => $tenantId, ':n' => $nombre, ':d' => $dur, ':p' => $price]);
            }
        }

        // Limpiar session del wizard
        foreach (['pending_rubro','pending_especialidad','pending_horarios','pending_servicios'] as $k) {
            unset($_SESSION[$k]);
        }

        $db->prepare(
            "INSERT INTO users (tenant_id, email, google_sub, name, avatar_url, role, auth_provider)
             VALUES (:t, :e, :s, :n, :a, 'owner', 'google')"
        )->execute([':t' => $tenantId, ':e' => $email, ':s' => $sub, ':n' => $name, ':a' => $picture]);
        $userId = (int) $db->lastInsertId();

        // Guardar refresh_token cifrado
        if ($newRefreshToken) {
            $enc = TokenCrypto::encrypt($newRefreshToken);
            $db->prepare("INSERT INTO google_tokens (user_id, refresh_token) VALUES (:u, :r)")
               ->execute([':u' => $userId, ':r' => $enc]);
            // Backward compat en tenants
            $db->prepare(
                "UPDATE tenants SET google_refresh_token = :r, google_calendar_connected = 1,
                 google_connected_at = NOW() WHERE id = :t"
            )->execute([':r' => $newRefreshToken, ':t' => $tenantId]);
        }

        $db->commit();
    } catch (Throwable $e) {
        if ($db->inTransaction()) $db->rollBack();
        error_log('Google self-serve signup failed: ' . $e->getMessage());
        http_response_code(500);
        exit('No se pudo crear tu cuenta. Intentá de nuevo.');
    }

    // Crear el calendario secundario "Vale AI" y guardar su ID.
    if ($newRefreshToken) {
        try {
            require_once dirname(__DIR__) . '/services/google_calendar.php';
            $tzMap = [
                'NI' => 'America/Managua',     'MX' => 'America/Mexico_City',
                'CO' => 'America/Bogota',      'CR' => 'America/Costa_Rica',
                'GT' => 'America/Guatemala',   'PA' => 'America/Panama',
                'SV' => 'America/El_Salvador', 'HN' => 'America/Tegucigalpa',
                'DO' => 'America/Santo_Domingo','VE' => 'America/Caracas',
                'PE' => 'America/Lima',        'CL' => 'America/Santiago',
                'AR' => 'America/Argentina/Buenos_Aires',
                'US' => 'America/New_York',
            ];
            $calTz    = $tzMap[$pendingCountry ?? ''] ?? 'America/Managua';
            error_log("[google_auth/new] tenant=$tenantId tz=$calTz: calling createCalendar");
            $newCalId = GoogleCalendar::createCalendar($newRefreshToken, 'Vale AI', $calTz);
            error_log("[google_auth/new] tenant=$tenantId: createCalendar => " . var_export($newCalId, true));
            if ($newCalId) {
                $db->prepare(
                    "INSERT INTO vale_config (tenant_id, calendar_id) VALUES (:t, :c)
                     ON DUPLICATE KEY UPDATE calendar_id = VALUES(calendar_id)"
                )->execute([':t' => $tenantId, ':c' => $newCalId]);
            }
        } catch (Throwable $e) {
            error_log('[google_auth/new] createCalendar: ' . $e->getMessage());
        }
    }

    // Enviar email de bienvenida (no bloquea el flujo si falla)
    try {
        $slug = $db->prepare("SELECT public_slug FROM tenants WHERE id = :id AND deleted_at IS NULL LIMIT 1");
        $slug->execute([':id' => $tenantId]);
        $publicSlug = (string)($slug->fetchColumn() ?: '');
        Mailer::sendWelcome($db, $email, $name, $tenantId, $publicSlug);
    } catch (Throwable $e) {
        error_log('Welcome email failed: ' . $e->getMessage());
    }

    unset($_SESSION['force_consent_attempted'], $_SESSION['pending_name'], $_SESSION['pending_type']);

    // Sesión del nuevo usuario
    session_regenerate_id(true);
    $_SESSION['user_id']     = $userId;
    $_SESSION['tenant_id']   = $tenantId;
    $_SESSION['tenant_name'] = $pendingName;
    $_SESSION['user_name']   = $name;
    $_SESSION['user_email']  = $email;
    $_SESSION['user_role']   = 'owner';

    // ── Guardar memoria inicial del nuevo tenant ──────────────────────────────
    // Vale ya "conoce" estos datos desde el primer turno del onboarding.
    try {
        require_once dirname(__DIR__) . '/services/vale_memory_service.php';
        ValeMemoryService::set($db, $tenantId, 'origen',          'wizard');
        ValeMemoryService::set($db, $tenantId, 'fecha_registro',  date('Y-m-d'));
        if ($pendingRubro) {
            ValeMemoryService::set($db, $tenantId, 'rubro_confirmado', $pendingRubro);
        }
        if (!empty($pendingSvcs)) {
            ValeMemoryService::set($db, $tenantId, 'servicios_count', count($pendingSvcs));
        }
    } catch (Throwable $e) {
        error_log('[google_auth] vale_memory init: ' . $e->getMessage());
    }

    // ── Ir al onboarding (o directo al panel si ya está todo listo) ───────────
    $pendingLang = $_SESSION['pending_lang'] ?? 'es';
    unset($_SESSION['pending_lang'], $_SESSION['pending_country'], $_SESSION['pending_channel']);
    header('Location: /onboarding?lang=' . $pendingLang);
    exit;
}

http_response_code(404);
echo '404 Not Found';
