<?php

declare(strict_types=1);

namespace App\Models;

use Core\Database;
use Core\Session;
use PDO;
use PDOException;

final class AdminUser
{
    private static ?bool $roleColumnExists = null;

    public static function all(): array
    {
        $orderBy = self::hasRoleColumn()
            ? "ORDER BY CASE role WHEN 'admin' THEN 0 ELSE 1 END, name ASC, email ASC"
            : 'ORDER BY name ASC, email ASC';

        $st = Database::connection()->query('SELECT * FROM admin_users ' . $orderBy);
        $rows = $st->fetchAll(PDO::FETCH_ASSOC);

        return array_map(fn (array $row): array => self::normalizeRow($row), $rows);
    }

    public static function findByEmail(string $email): ?array
    {
        $st = Database::connection()->prepare('SELECT * FROM admin_users WHERE email = :email LIMIT 1');
        $st->execute(['email' => strtolower(trim($email))]);
        $row = $st->fetch();

        return $row ? self::normalizeRow($row) : null;
    }

    public static function find(int $id): ?array
    {
        $st = Database::connection()->prepare('SELECT * FROM admin_users WHERE id = :id LIMIT 1');
        $st->execute(['id' => $id]);
        $row = $st->fetch();

        return $row ? self::normalizeRow($row) : null;
    }

    public static function emailExists(string $email, ?int $ignoreId = null): bool
    {
        $email = strtolower(trim($email));
        $sql = 'SELECT id FROM admin_users WHERE email = :email';
        $bindings = ['email' => $email];

        if ($ignoreId !== null) {
            $sql .= ' AND id != :id';
            $bindings['id'] = $ignoreId;
        }

        $sql .= ' LIMIT 1';

        $st = Database::connection()->prepare($sql);
        $st->execute($bindings);

        return (bool) $st->fetchColumn();
    }

    public static function countActive(): int
    {
        $st = Database::connection()->query('SELECT COUNT(*) FROM admin_users WHERE is_active = 1');
        return (int) $st->fetchColumn();
    }

    public static function countActiveAdmins(): int
    {
        if (!self::hasRoleColumn()) {
            return self::countActive();
        }

        $st = Database::connection()->query("SELECT COUNT(*) FROM admin_users WHERE is_active = 1 AND role = 'admin'");
        return (int) $st->fetchColumn();
    }

    public static function create(array $data): int
    {
        $bindings = [
            'name' => trim((string) $data['name']),
            'email' => strtolower(trim((string) $data['email'])),
            'password_hash' => (string) $data['password_hash'],
            'is_active' => (int) ($data['is_active'] ?? 1),
        ];

        if (self::hasRoleColumn()) {
            $st = Database::connection()->prepare(
                'INSERT INTO admin_users (name, email, password_hash, role, is_active, created_at, updated_at)
                 VALUES (:name, :email, :password_hash, :role, :is_active, NOW(), NOW())'
            );
            $bindings['role'] = self::normalizeRole((string) ($data['role'] ?? 'operador'));
        } else {
            $st = Database::connection()->prepare(
                'INSERT INTO admin_users (name, email, password_hash, is_active, created_at, updated_at)
                 VALUES (:name, :email, :password_hash, :is_active, NOW(), NOW())'
            );
        }

        $st->execute($bindings);

        return (int) Database::connection()->lastInsertId();
    }

    public static function updateProfile(int $id, array $data): void
    {
        $bindings = [
            'id' => $id,
            'name' => trim((string) $data['name']),
            'email' => strtolower(trim((string) $data['email'])),
            'is_active' => (int) ($data['is_active'] ?? 1),
        ];

        $sql = 'UPDATE admin_users SET name = :name, email = :email';

        if (self::hasRoleColumn()) {
            $sql .= ', role = :role';
            $bindings['role'] = self::normalizeRole((string) ($data['role'] ?? 'operador'));
        }

        $sql .= ', is_active = :is_active';

        if (!empty($data['password_hash'])) {
            $sql .= ', password_hash = :password_hash';
            $bindings['password_hash'] = (string) $data['password_hash'];
        }

        $sql .= ', updated_at = NOW() WHERE id = :id';

        $st = Database::connection()->prepare($sql);
        $st->execute($bindings);
    }

    public static function updateStatus(int $id, bool $isActive): void
    {
        $st = Database::connection()->prepare('UPDATE admin_users SET is_active = :is_active, updated_at = NOW() WHERE id = :id');
        $st->execute([
            'id' => $id,
            'is_active' => $isActive ? 1 : 0,
        ]);
    }

    public static function authenticate(string $email, string $password): bool
    {
        $user = self::findByEmail($email);

        if (!$user || !(bool) $user['is_active']) {
            return false;
        }

        if (!password_verify($password, $user['password_hash'])) {
            return false;
        }

        Session::regenerate(true);
        Session::set('admin_user_id', (int) $user['id']);
        unset($_SESSION['_csrf_token']);
        return true;
    }

    public static function current(): ?array
    {
        $id = (int) Session::get('admin_user_id');

        if ($id <= 0) {
            return null;
        }

        $user = self::find($id);

        if (!$user || !(bool) $user['is_active']) {
            self::logout();
            return null;
        }

        return $user;
    }

    public static function currentRole(): string
    {
        $user = self::current();

        return self::normalizeRole((string) ($user['role'] ?? 'admin'));
    }

    public static function currentIsAdmin(): bool
    {
        return self::currentRole() === 'admin';
    }

    public static function isAdmin(?array $user): bool
    {
        if (!$user) {
            return false;
        }

        return self::normalizeRole((string) ($user['role'] ?? 'admin')) === 'admin';
    }

    public static function roleOptions(): array
    {
        $config = require dirname(__DIR__, 2) . '/config/app.php';

        return $config['user_roles'] ?? [
            'admin' => 'Administrador',
            'operador' => 'Operador RH',
        ];
    }

    public static function roleLabel(string $role): string
    {
        $options = self::roleOptions();
        $role = self::normalizeRole($role);

        return $options[$role] ?? ucfirst($role);
    }

    public static function normalizeRole(string $role): string
    {
        $role = strtolower(trim($role));
        $valid = array_keys(self::roleOptions());

        if (!in_array($role, $valid, true)) {
            return in_array('operador', $valid, true)
                ? 'operador'
                : ($valid[0] ?? 'admin');
        }

        return $role;
    }

    public static function hasRoleColumn(): bool
    {
        if (self::$roleColumnExists !== null) {
            return self::$roleColumnExists;
        }

        try {
            $st = Database::connection()->query("SHOW COLUMNS FROM admin_users LIKE 'role'");
            self::$roleColumnExists = (bool) $st->fetch();
        } catch (PDOException) {
            self::$roleColumnExists = false;
        }

        return self::$roleColumnExists;
    }

    public static function logout(): void
    {
        Session::invalidate();
    }

    private static function normalizeRow(array $row): array
    {
        $row['role'] = self::normalizeRole((string) ($row['role'] ?? 'admin'));
        return $row;
    }
}
