<?php
require_once __DIR__ . '/../core/config.php';
require_once __DIR__ . '/../core/database.php';
require_once __DIR__ . '/../core/session.php';

function google_auth_fail($isAjax, $message, $status = 400)
{
    http_response_code((int)$status);

    if ($isAjax) {
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode(
            array('success' => false, 'message' => $message),
            JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
        );
    } else {
        header('Location: /login.php?error=' . urlencode($message));
    }
    exit;
}

// El flujo vigente usa popup + POST JSON. Se conserva GET para callbacks
// configurados previamente, pero ambos exigen el mismo state de sesion.
$isAjax = strtoupper((string)($_SERVER['REQUEST_METHOD'] ?? 'GET')) === 'POST';
$input = $isAjax ? json_decode(file_get_contents('php://input'), true) : array();
$input = is_array($input) ? $input : array();
$code = $isAjax ? ($input['code'] ?? null) : ($_GET['code'] ?? null);
$state = $isAjax ? ($input['state'] ?? '') : ($_GET['state'] ?? '');

if ($isAjax && strcasecmp((string)($_SERVER['HTTP_X_REQUESTED_WITH'] ?? ''), 'XMLHttpRequest') !== 0) {
    error_log('ICYLAB Google OAuth rejected: missing X-Requested-With');
    google_auth_fail($isAjax, 'Solicitud de autenticacion no valida.', 400);
}

if (!$code) {
    if (isset($_GET['error'])) {
        google_auth_fail($isAjax, 'Acceso denegado por el usuario.');
    }
    google_auth_fail($isAjax, 'Codigo de autorizacion no recibido.');
}

$expectedState = $_SESSION['_csrf_token'] ?? '';
if (!is_string($expectedState)
    || strlen($expectedState) < 64
    || !is_string($state)
    || !hash_equals($expectedState, $state)) {
    error_log('ICYLAB Google OAuth rejected: invalid state');
    google_auth_fail(
        $isAjax,
        'La sesion de autenticacion expiro. Recarga la pagina e intenta nuevamente.',
        419
    );
}

$redirectUri = rtrim((string)APP_URL, '/') . '/auth/google_callback.php';
$postRedirectUri = $isAjax ? rtrim((string)APP_URL, '/') : $redirectUri;

// Intercambiar el codigo por tokens usando validacion TLS obligatoria.
$ch = curl_init('https://oauth2.googleapis.com/token');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(array(
    'client_id' => GOOGLE_CLIENT_ID,
    'client_secret' => GOOGLE_CLIENT_SECRET,
    'code' => $code,
    'grant_type' => 'authorization_code',
    'redirect_uri' => $postRedirectUri
)));
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($ch, CURLOPT_TIMEOUT, 20);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);

$response = curl_exec($ch);
$curlError = curl_error($ch);
$tokenHttpCode = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($response === false || $tokenHttpCode < 200 || $tokenHttpCode >= 300) {
    error_log('ICYLAB Google OAuth token exchange failed: HTTP ' . $tokenHttpCode . '; cURL=' . $curlError);
    google_auth_fail($isAjax, 'No fue posible validar el acceso con Google. Intenta nuevamente.', 502);
}

$tokenData = json_decode($response, true);
if (!is_array($tokenData) || empty($tokenData['access_token'])) {
    error_log('ICYLAB Google OAuth rejected: access token missing');
    google_auth_fail($isAjax, 'Sesion expirada o codigo invalido. Intenta nuevamente.');
}

// Obtener la identidad directamente desde Google. Asi no se confia en un JWT
// simplemente decodificado sin verificar firma, issuer o audience.
$identityCh = curl_init('https://openidconnect.googleapis.com/v1/userinfo');
curl_setopt($identityCh, CURLOPT_RETURNTRANSFER, true);
curl_setopt($identityCh, CURLOPT_HTTPHEADER, array(
    'Authorization: Bearer ' . $tokenData['access_token'],
    'Accept: application/json'
));
curl_setopt($identityCh, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($identityCh, CURLOPT_TIMEOUT, 20);
curl_setopt($identityCh, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($identityCh, CURLOPT_SSL_VERIFYHOST, 2);

$identityResponse = curl_exec($identityCh);
$identityError = curl_error($identityCh);
$identityHttpCode = (int)curl_getinfo($identityCh, CURLINFO_HTTP_CODE);
curl_close($identityCh);

if ($identityResponse === false || $identityHttpCode < 200 || $identityHttpCode >= 300) {
    error_log('ICYLAB Google OAuth userinfo failed: HTTP ' . $identityHttpCode . '; cURL=' . $identityError);
    google_auth_fail($isAjax, 'No fue posible verificar la identidad con Google. Intenta nuevamente.', 502);
}

$identity = json_decode($identityResponse, true);
$identity = is_array($identity) ? $identity : array();
$email = strtolower(trim((string)($identity['email'] ?? '')));
$googleId = trim((string)($identity['sub'] ?? ''));
$name = trim((string)($identity['name'] ?? ''));
$hostedDomain = strtolower(trim((string)($identity['hd'] ?? '')));
$allowedDomain = strtolower(trim((string)GOOGLE_ALLOWED_DOMAIN));
$emailDomain = strpos($email, '@') !== false ? substr(strrchr($email, '@'), 1) : '';

if ($googleId === ''
    || $email === ''
    || empty($identity['email_verified'])
    || $allowedDomain === ''
    || $hostedDomain !== $allowedDomain
    || $emailDomain !== $allowedDomain) {
    error_log('ICYLAB Google OAuth rejected: identity or hosted domain mismatch');
    google_auth_fail($isAjax, 'Acceso restringido a cuentas corporativas.');
}

$pdo = db();
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = ? LIMIT 1');
$stmt->execute(array($email));
$user = $stmt->fetch();

// Una cuenta ya vinculada no puede reasignarse silenciosamente a otro sub.
if ($user && !empty($user['google_id']) && !hash_equals((string)$user['google_id'], $googleId)) {
    error_log('ICYLAB Google OAuth rejected: attempted account rebind for user ' . (int)$user['id']);
    google_auth_fail($isAjax, 'La identidad Google no coincide con la cuenta registrada. Contacta a administracion.');
}

// Usuarios nuevos quedan inactivos hasta aprobacion administrativa.
if (!$user) {
    $stmt = $pdo->prepare("
        INSERT INTO users
        (google_id, name, email, role, area, is_active, status, last_login)
        VALUES (?, ?, ?, NULL, NULL, 0, 'inactive', NOW())
    ");
    $stmt->execute(array($googleId, $name, $email));
    $userId = $pdo->lastInsertId();

    $stmt = $pdo->prepare('SELECT * FROM users WHERE id = ? LIMIT 1');
    $stmt->execute(array($userId));
    $user = $stmt->fetch();
}

if ((int)$user['is_active'] !== 1 || $user['status'] !== 'active') {
    google_auth_fail($isAjax, 'Cuenta no autorizada. Contacta a administracion.');
}

$expiresIn = max(60, (int)($tokenData['expires_in'] ?? 3599));
$expiresAt = date('Y-m-d H:i:s', time() + $expiresIn);
$refreshTokenSql = isset($tokenData['refresh_token']) ? ', google_refresh_token = ?' : '';

$stmt = $pdo->prepare("
    UPDATE users
    SET google_id = ?,
        name = ?,
        google_access_token = ?,
        google_token_expires = ?,
        last_login = NOW()
        $refreshTokenSql
    WHERE id = ?
");

$params = array($googleId, $name, $tokenData['access_token'], $expiresAt);
if (isset($tokenData['refresh_token'])) {
    $params[] = $tokenData['refresh_token'];
}
$params[] = $user['id'];
$stmt->execute($params);

session_regenerate_id(true);
$_SESSION['user'] = array(
    'id' => $user['id'],
    'name' => $name !== '' ? $name : $user['name'],
    'email' => $user['email'],
    'role' => $user['role'],
    'status' => $user['status']
);

if ($isAjax) {
    header('Content-Type: application/json; charset=utf-8');
    echo json_encode(array('success' => true));
} else {
    header('Location: /modules/dashboard/');
}
exit;
