<?php

declare(strict_types=1);

namespace App\Services;

use App\Core\Database;
use PDO;

final class AuthService
{
    public function createResetToken(string $email): ?string
    {
        $pdo = Database::connection();
        $stmt = $pdo->prepare('SELECT id FROM users WHERE email = :email AND status = :status LIMIT 1');
        $stmt->execute(['email' => strtolower(trim($email)), 'status' => 'active']);
        $userId = $stmt->fetchColumn();
        if ($userId === false) return null;

        $pdo->prepare('UPDATE password_reset_tokens SET used_at = NOW() WHERE user_id = :user_id AND used_at IS NULL')->execute(['user_id' => (int) $userId]);

        $token = bin2hex(random_bytes(32));
        $hash = hash('sha256', $token);
        $stmt = $pdo->prepare('INSERT INTO password_reset_tokens (user_id, token_hash, expires_at) VALUES (:user_id, :token_hash, DATE_ADD(NOW(), INTERVAL 60 MINUTE))');
        $stmt->execute(['user_id' => (int) $userId, 'token_hash' => $hash]);
        return $token;
    }

    public function resetPassword(string $token, string $password): bool
    {
        $hash = hash('sha256', $token);
        $pdo = Database::connection();
        $stmt = $pdo->prepare('SELECT id, user_id FROM password_reset_tokens WHERE token_hash = :hash AND used_at IS NULL AND expires_at > NOW() LIMIT 1');
        $stmt->execute(['hash' => $hash]);
        $row = $stmt->fetch();
        if (!is_array($row)) return false;

        $pdo->beginTransaction();
        try {
            $update = $pdo->prepare('UPDATE users SET password_hash = :password_hash, updated_at = NOW() WHERE id = :user_id');
            $update->execute(['password_hash' => password_hash($password, PASSWORD_DEFAULT), 'user_id' => (int) $row['user_id']]);
            $invalidate = $pdo->prepare('UPDATE password_reset_tokens SET used_at = NOW() WHERE user_id = :user_id AND used_at IS NULL');
            $invalidate->execute(['user_id' => (int) $row['user_id']]);
            $pdo->commit();
            return true;
        } catch (\Throwable $e) {
            $pdo->rollBack();
            throw $e;
        }
    }

    public function setupAvailable(): bool
    {
        $pdo = Database::connection();
        $stmt = $pdo->query("SELECT COUNT(*) FROM user_roles ur INNER JOIN roles r ON r.id = ur.role_id WHERE r.name = 'SUPER_ADMIN'");
        return (int) $stmt->fetchColumn() === 0;
    }

    public function createInitialSuperAdmin(string $name, string $email, string $password): int
    {
        $pdo = Database::connection();
        if (!$this->setupAvailable()) {
            throw new \RuntimeException('Initial setup has already been completed.', 403);
        }

        $pdo->beginTransaction();
        try {
            $stmt = $pdo->prepare('INSERT INTO users (full_name, email, password_hash, status) VALUES (:name, :email, :password_hash, :status)');
            $stmt->execute([
                'name' => trim($name),
                'email' => strtolower(trim($email)),
                'password_hash' => password_hash($password, PASSWORD_DEFAULT),
                'status' => 'active',
            ]);
            $userId = (int) $pdo->lastInsertId();

            $role = $pdo->prepare('SELECT id FROM roles WHERE name = :name LIMIT 1');
            $role->execute(['name' => 'SUPER_ADMIN']);
            $roleId = $role->fetchColumn();
            if ($roleId === false) {
                $insertRole = $pdo->prepare("INSERT INTO roles (name, display_name) VALUES ('SUPER_ADMIN', 'Super Administrator')");
                $insertRole->execute();
                $roleId = (int) $pdo->lastInsertId();
            }

            $assign = $pdo->prepare('INSERT INTO user_roles (user_id, role_id) VALUES (:user_id, :role_id)');
            $assign->execute(['user_id' => $userId, 'role_id' => (int) $roleId]);
            $pdo->commit();
            return $userId;
        } catch (\Throwable $e) {
            $pdo->rollBack();
            throw $e;
        }
    }
}
