<?php

declare(strict_types=1);

namespace App\Core;

use App\Services\SaasAccessService;

class Auth
{
    public static function check(): bool
    {
        return Session::has('user_id');
    }

    public static function id(): ?int
    {
        return self::check() ? (int) Session::get('user_id') : null;
    }

    public static function user(): ?array
    {
        if (!self::check()) {
            return null;
        }

        $cached = Session::get('auth_user');
        if (is_array($cached)) {
            return $cached;
        }

        $stmt = Database::connection()->prepare(
            'SELECT u.id, u.nombre, u.email, u.activo, u.empresa_id, r.slug AS role_slug, r.nombre AS role_name
             FROM usuarios u
             LEFT JOIN roles r ON r.id = u.rol_id
             WHERE u.id = :id LIMIT 1'
        );
        $stmt->execute([':id' => self::id()]);
        $user = $stmt->fetch() ?: null;

        if ($user === null) {
            self::logout();
            return null;
        }

        Session::put('auth_user', $user);
        return $user;
    }

    public static function isSuperadmin(): bool
    {
        $user = self::user();
        return is_array($user)
            && ($user['role_slug'] ?? null) === 'superadmin'
            && ($user['empresa_id'] ?? null) === null;
    }

    public static function hasPermission(string $permission): bool
    {
        if (!self::check()) {
            return false;
        }

        if (self::isSuperadmin()) {
            return true;
        }

        $cacheKey = 'permissions_cache';
        $cached = Session::get($cacheKey);

        if (!is_array($cached)) {
            $cached = self::loadPermissionsForUser((int) self::id());
            Session::put($cacheKey, $cached);
        }

        if (in_array($permission, $cached, true)) {
            return true;
        }

        $fresh = self::loadPermissionsForUser((int) self::id());
        Session::put($cacheKey, $fresh);

        return in_array($permission, $fresh, true);
    }

    public static function attempt(string $email, string $password): array
    {
        if (self::isLocked()) {
            $until = (int) Session::get('login_locked_until', 0);
            return [false, 'Cuenta bloqueada temporalmente. Intenta nuevamente a las ' . date('H:i', $until) . '.'];
        }

        $stmt = Database::connection()->prepare(
            'SELECT u.id, u.nombre, u.email, u.password, u.activo, u.empresa_id, r.slug AS role_slug
             FROM usuarios u
             LEFT JOIN roles r ON r.id = u.rol_id
             WHERE u.email = :email LIMIT 1'
        );
        $stmt->execute([':email' => strtolower(trim($email))]);
        $user = $stmt->fetch();

        if (!$user || !(bool) $user['activo'] || !password_verify($password, (string) $user['password'])) {
            self::registerFailedAttempt();
            return [false, 'Credenciales inválidas o usuario inactivo.'];
        }

        if (($user['role_slug'] ?? '') === 'superadmin' && ($user['empresa_id'] ?? null) !== null) {
            self::registerFailedAttempt();
            return [false, 'Configuración de seguridad inválida para este usuario. Contacta a soporte.'];
        }

        if (($user['role_slug'] ?? '') !== 'superadmin') {
            $companyId = $user['empresa_id'] !== null ? (int) $user['empresa_id'] : 0;
            $access = (new SaasAccessService())->evaluateCompanyAccess($companyId, true);
            if (!(bool) ($access['allowed'] ?? false)) {
                return [false, (string) ($access['message'] ?? 'La empresa no está habilitada para operar.')];
            }
        }

        self::clearFailedAttempts();

        Session::regenerate();
        Session::put('user_id', (int) $user['id']);
        Session::put('company_id', $user['empresa_id'] !== null ? (int) $user['empresa_id'] : null);
        Session::forget('permissions_cache');
        Session::forget('auth_user');

        return [true, 'Autenticación exitosa'];
    }

    public static function logout(): void
    {
        $_SESSION = [];
        if (session_status() === PHP_SESSION_ACTIVE) {
            session_regenerate_id(true);
        }
    }

    private static function registerFailedAttempt(): void
    {
        $count = (int) Session::get('login_attempts', 0) + 1;
        $max = (int) config('app.max_login_attempts', 5);

        Session::put('login_attempts', $count);

        if ($count >= $max) {
            $lockMinutes = (int) config('app.login_lock_minutes', 15);
            Session::put('login_locked_until', time() + ($lockMinutes * 60));
            Session::put('login_attempts', 0);
        }
    }

    private static function isLocked(): bool
    {
        $until = (int) Session::get('login_locked_until', 0);
        if ($until === 0) {
            return false;
        }

        if (time() >= $until) {
            Session::forget('login_locked_until');
            return false;
        }

        return true;
    }

    private static function clearFailedAttempts(): void
    {
        Session::forget('login_attempts');
        Session::forget('login_locked_until');
    }

    private static function loadPermissionsForUser(int $userId): array
    {
        if ($userId <= 0) {
            return [];
        }

        $stmt = Database::connection()->prepare(
            'SELECT p.codigo
             FROM permisos p
             INNER JOIN rol_permiso rp ON rp.permiso_id = p.id
             INNER JOIN usuarios u ON u.rol_id = rp.rol_id
             INNER JOIN roles r ON r.id = u.rol_id
             WHERE u.id = :user_id
               AND (r.slug <> :superadmin_slug OR u.empresa_id IS NULL)'
        );
        $stmt->execute([
            ':user_id' => $userId,
            ':superadmin_slug' => 'superadmin',
        ]);

        return array_map(
            static fn (array $row): string => (string) $row['codigo'],
            $stmt->fetchAll() ?: []
        );
    }
}
