<?php
/**
 * User - User model and management library
 */

class User
{
    private Database $db;
    private Logger   $log;

    public function __construct()
    {
        $this->db  = Database::getInstance();
        $this->log = Logger::getInstance();
    }

    // -------------------------------------------------------------------------
    // Retrieval
    // -------------------------------------------------------------------------

    public function findById(int $id): ?array
    {
        $u = $this->db->fetchOne("SELECT * FROM users WHERE id = ?", [$id]);
        if ($u) $u['roles'] = $this->getRoles($id);
        return $u;
    }

    public function findByUsername(string $username): ?array
    {
        $u = $this->db->fetchOne("SELECT * FROM users WHERE LOWER(username) = LOWER(?)", [$username]);
        if ($u) $u['roles'] = $this->getRoles((int)$u['id']);
        return $u;
    }

    public function findByEmail(string $email): ?array
    {
        $u = $this->db->fetchOne("SELECT * FROM users WHERE LOWER(email) = LOWER(?)", [$email]);
        if ($u) $u['roles'] = $this->getRoles((int)$u['id']);
        return $u;
    }

    public function findAll(): array
    {
        $rows = $this->db->fetchAll("SELECT * FROM users ORDER BY username");
        foreach ($rows as &$u) {
            $u['roles'] = $this->getRoles((int)$u['id']);
        }
        return $rows;
    }

    // -------------------------------------------------------------------------
    // Creation / Update / Deletion
    // -------------------------------------------------------------------------

    public function create(array $data): int|false
    {
        if (empty($data['username'])) {
            return false;
        }
        $data['username'] = strtolower(trim($data['username']));

        $hash = null;
        if (!empty($data['password'])) {
            $hash = password_hash($data['password'], PASSWORD_BCRYPT);
        }

        try {
            $this->db->execute(
                "INSERT INTO users (username, email, password_hash, auth_method, language, theme, active)
                 VALUES (?, ?, ?, ?, ?, ?, ?)",
                [
                    $data['username'],
                    $data['email']       ?? null,
                    $hash,
                    $data['auth_method'] ?? AUTH_LOCAL,
                    $data['language']    ?? null,
                    $data['theme']       ?? null,
                    isset($data['active']) ? (int)$data['active'] : 1,
                ]
            );
            $id = (int)$this->db->lastInsertId();
            $this->log->info("User created", ['username' => $data['username'], 'id' => $id]);
            return $id;
        } catch (PDOException $e) {
            $this->log->error("Failed to create user", ['error' => $e->getMessage()]);
            return false;
        }
    }

    public function update(int $id, array $data): bool
    {
        $fields = [];
        $params = [];

        $allowed = ['email', 'auth_method', 'language', 'theme', 'active',
                    'oracle_landing', 'oracle_report_format', 'postgresql_landing'];
        foreach ($allowed as $field) {
            if (array_key_exists($field, $data)) {
                $fields[] = "$field = ?";
                $params[] = $data[$field];
            }
        }

        if (!empty($data['password'])) {
            $fields[] = "password_hash = ?";
            $params[] = password_hash($data['password'], PASSWORD_BCRYPT);
        }

        if (empty($fields)) {
            return false;
        }

        $params[] = $id;

        try {
            $this->db->execute(
                "UPDATE users SET " . implode(', ', $fields) . " WHERE id = ?",
                $params
            );
            $this->log->info("User updated", ['id' => $id]);
            return true;
        } catch (PDOException $e) {
            $this->log->error("Failed to update user", ['error' => $e->getMessage()]);
            return false;
        }
    }

    public function delete(int $id): bool
    {
        try {
            $user = $this->findById($id);
            $this->db->execute("DELETE FROM users WHERE id = ?", [$id]);
            $this->log->info("User deleted", ['id' => $id, 'username' => $user['username'] ?? '']);
            return true;
        } catch (PDOException $e) {
            $this->log->error("Failed to delete user", ['error' => $e->getMessage()]);
            return false;
        }
    }

    public function updateLastLogin(int $id): void
    {
        $this->db->execute(
            "UPDATE users SET last_login = datetime('now') WHERE id = ?",
            [$id]
        );
    }

    // -------------------------------------------------------------------------
    // Roles
    // -------------------------------------------------------------------------

    /** @return string[] noms des rôles affectés à l'utilisateur */
    public function getRoles(int $userId): array
    {
        $rows = $this->db->fetchAll(
            "SELECT r.name FROM user_roles ur
             JOIN roles r ON r.id = ur.role_id
             WHERE ur.user_id = ?
             ORDER BY r.name",
            [$userId]
        );
        return array_column($rows, 'name');
    }

    /** Remplace l'ensemble des rôles de l'utilisateur par la liste fournie. */
    public function setRoles(int $userId, array $roleNames): void
    {
        $this->db->execute("DELETE FROM user_roles WHERE user_id = ?", [$userId]);
        foreach (array_unique($roleNames) as $name) {
            if ($name === '') continue;
            $this->db->execute(
                "INSERT OR IGNORE INTO user_roles (user_id, role_id)
                 SELECT ?, id FROM roles WHERE name = ?",
                [$userId, $name]
            );
        }
        $this->log->info('User roles updated', ['user_id' => $userId, 'roles' => $roleNames]);
    }

    public function addRole(int $userId, string $roleName): void
    {
        $this->db->execute(
            "INSERT OR IGNORE INTO user_roles (user_id, role_id)
             SELECT ?, id FROM roles WHERE name = ?",
            [$userId, $roleName]
        );
    }

    public function removeRole(int $userId, string $roleName): void
    {
        $this->db->execute(
            "DELETE FROM user_roles
             WHERE user_id = ?
               AND role_id = (SELECT id FROM roles WHERE name = ?)",
            [$userId, $roleName]
        );
    }

    /** @return array<int,array{name:string,label:string,module:?string,is_system:int}> */
    public function getAllRoles(): array
    {
        return $this->db->fetchAll(
            "SELECT id, name, label, module, is_system FROM roles ORDER BY module IS NOT NULL, module, name"
        );
    }

    // -------------------------------------------------------------------------
    // Helpers
    // -------------------------------------------------------------------------

    public function isAdmin(array $user): bool
    {
        return in_array(ROLE_APP_ADMIN, $user['roles'] ?? [], true);
    }

    public function getEffectiveTheme(array $user, string $appDefaultTheme): string
    {
        return $user['theme'] ?: $appDefaultTheme;
    }

    public function getEffectiveLanguage(array $user): ?string
    {
        return $user['language'] ?: null;
    }
}
