<?php
/**
 * SalonPro X - Authentication & Authorization Handler
 */

namespace core;

class Auth
{
    private static ?object $user = null;
    private static bool $initialized = false;

    /**
     * Initialize auth state from session
     */
    public static function init(): void
    {
        if (self::$initialized) {
            return;
        }
        self::$initialized = true;

        $userId = Session::get('user_id');
        if ($userId) {
            $db = Database::getInstance();
            $user = $db->table('users')
                ->select('users.*, roles.name as role_name, roles.slug as role_slug')
                ->leftJoin('roles', 'users.role_id', '=', 'roles.id')
                ->where('users.id', $userId)
                ->where('users.status', 'active')
                ->first();

            if ($user) {
                self::$user = $user;
                Session::set('user_role', $user->role_slug);
                Session::set('user_name', $user->first_name . ' ' . $user->last_name);
            } else {
                self::logout();
            }
        }
    }

    /**
     * Attempt login with email and password
     */
    public static function attempt(string $email, string $password, bool $remember = false): bool
    {
        $db = Database::getInstance();
        $user = $db->table('users')
            ->select('users.*, roles.name as role_name, roles.slug as role_slug')
            ->leftJoin('roles', 'users.role_id', '=', 'roles.id')
            ->where('users.email', $email)
            ->first();

        if (!$user || !Security::verifyPassword($password, $user->password)) {
            return false;
        }

        if ($user->status !== 'active') {
            return false;
        }

        self::loginUser($user, $remember);
        return true;
    }

    /**
     * Log the user in by setting session
     */
    public static function loginUser(object $user, bool $remember = false): void
    {
        Session::regenerate();
        Session::set('user_id', $user->id);
        Session::set('user_role', $user->role_slug);
        Session::set('user_name', $user->first_name . ' ' . $user->last_name);
        Session::set('user_email', $user->email);
        Session::set('branch_id', $user->branch_id);
        Session::set('employee_id', $user->employee_id);
        Session::set('is_superadmin', (bool)$user->is_superadmin);
        Session::updateActivity();

        self::$user = $user;

        // Update last login
        $db = Database::getInstance();
        $db->table('users')
            ->where('id', $user->id)
            ->update([
                'last_login' => date(DATE_TIME_FORMAT),
                'ip_address' => Request::capture()->ip(),
            ]);

        // Log activity
        self::logActivity('login', 'User logged in: ' . $user->email);

        // Set remember token
        if ($remember) {
            $token = Security::generateRememberToken();
            $db->table('users')->where('id', $user->id)->update(['remember_token' => Security::hashPassword($token)]);
            setcookie('remember_token', $token, time() + 86400 * 30, '/', '', false, true);
        }
    }

    /**
     * Log the user out
     */
    public static function logout(): void
    {
        $userId = Session::get('user_id');
        if ($userId) {
            self::logActivity('logout', 'User logged out');
        }

        Session::destroy();
        self::$user = null;
        self::$initialized = false;

        setcookie('remember_token', '', time() - 3600, '/');
    }

    /**
     * Get currently authenticated user
     */
    public static function user(): ?object
    {
        if (!self::$initialized) {
            self::init();
        }
        return self::$user;
    }

    /**
     * Get user ID
     */
    public static function id(): ?int
    {
        $user = self::user();
        return $user->id ?? null;
    }

    /**
     * Check if user is logged in
     */
    public static function check(): bool
    {
        return self::user() !== null;
    }

    /**
     * Check if user is a guest (not logged in)
     */
    public static function guest(): bool
    {
        return !self::check();
    }

    /**
     * Check if user is superadmin
     */
    public static function isSuperadmin(): bool
    {
        return Session::get('is_superadmin', false);
    }

    /**
     * Get current branch ID
     */
    public static function branchId(): ?int
    {
        return Session::get('branch_id');
    }

    /**
     * Get current employee ID
     */
    public static function employeeId(): ?int
    {
        return Session::get('employee_id');
    }

    private static ?int $cachedVersion = null;

    /**
     * Check if user has a specific permission
     */
    public static function hasPermission(string $permission): bool
    {
        if (self::isSuperadmin()) {
            return true;
        }

        $userId = self::id();
        if (!$userId) {
            return false;
        }

        if (self::$cachedVersion === null) {
            $db = Database::getInstance();
            $row = $db->table('settings')->where('key', 'permissions_version')->first();
            self::$cachedVersion = (int)($row->value ?? 0);
        }

        $cachedVersion = Session::get('permissions_version', 0);
        if ($cachedVersion < self::$cachedVersion) {
            Session::remove("permissions_{$userId}");
            Session::set('permissions_version', self::$cachedVersion);
        }

        $cacheKey = "permissions_{$userId}";
        $permissions = Session::get($cacheKey);

        if ($permissions === null) {
            $db = Database::getInstance();
            $results = $db->query(
                "SELECT p.slug FROM permissions p
                 INNER JOIN role_permissions rp ON p.id = rp.permission_id
                 INNER JOIN users u ON u.role_id = rp.role_id
                 WHERE u.id = :user_id",
                [':user_id' => $userId]
            );

            $permissions = array_map(fn($row) => $row->slug, $results);
            Session::set($cacheKey, $permissions);
        }

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

    /**
     * Clear cached permissions for the current user
     */
    public static function clearPermissionCache(?int $userId = null): void
    {
        $userId = $userId ?? self::id();
        if ($userId) {
            Session::remove("permissions_{$userId}");
        }
    }

    /**
     * Bump global permissions version to invalidate all user caches
     */
    public static function bumpPermissionsVersion(): void
    {
        $db = Database::getInstance();
        $existing = $db->table('settings')->where('key', 'permissions_version')->first();
        $version = time();
        if ($existing) {
            $db->table('settings')->where('key', 'permissions_version')->update(['value' => $version]);
        } else {
            $db->table('settings')->insert(['key' => 'permissions_version', 'value' => $version]);
        }
        self::$cachedVersion = $version;
    }

    /**
     * Check if user has any of the given permissions
     */
    public static function hasAnyPermission(array $permissions): bool
    {
        foreach ($permissions as $permission) {
            if (self::hasPermission($permission)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Check if user has all of the given permissions
     */
    public static function hasAllPermissions(array $permissions): bool
    {
        foreach ($permissions as $permission) {
            if (!self::hasPermission($permission)) {
                return false;
            }
        }
        return true;
    }

    /**
     * Check user role
     */
    public static function hasRole(string $role): bool
    {
        return Session::get('user_role') === $role;
    }

    /**
     * Require authentication - redirects if not logged in
     */
    public static function requireAuth(): void
    {
        if (self::guest()) {
            Session::setAlert('warning', 'Please login to continue.');
            $response = new Response();
            $response->redirect(APP_URL . '/login');
            exit;
        }
    }

    /**
     * Require specific permission - shows 403 if not authorized
     */
    public static function requirePermission(string $permission): void
    {
        self::requireAuth();
        if (!self::hasPermission($permission)) {
            http_response_code(403);
            include PATH_LAYOUTS . DS . 'error.php';
            exit;
        }
    }

    /**
     * Log user activity
     */
    public static function logActivity(string $action, string $description): void
    {
        try {
            $db = Database::getInstance();
            $db->table('activity_logs')->insert([
                'user_id' => self::id() ?: 0,
                'action' => $action,
                'description' => $description,
                'ip_address' => Request::capture()->ip(),
                'created_at' => date(DATE_TIME_FORMAT),
            ]);
        } catch (\Throwable $e) {
            // Silently fail for activity logging
        }
    }

    /**
     * Log audit trail
     */
    public static function logAudit(string $action, string $table, mixed $recordId, ?array $oldValues = null, ?array $newValues = null): void
    {
        try {
            $db = Database::getInstance();
            $db->table('audit_logs')->insert([
                'user_id' => self::id() ?: 0,
                'action' => $action,
                'table_name' => $table,
                'record_id' => $recordId,
                'old_values' => $oldValues ? json_encode($oldValues) : null,
                'new_values' => $newValues ? json_encode($newValues) : null,
                'ip_address' => Request::capture()->ip(),
                'user_agent' => Request::capture()->userAgent(),
                'created_at' => date(DATE_TIME_FORMAT),
            ]);
        } catch (\Throwable $e) {
            // Silently fail for audit logging
        }
    }
}
