<?php

declare(strict_types=1);

namespace App\Services;

use App\Helpers\Session as SessionHelper;
use App\Models\User;
use App\Models\UserSession;
use App\Models\PasswordReset;
use App\Models\UserInvitation;

class AuthService
{
    private User $userModel;
    private UserSession $sessionModel;
    private PasswordReset $passwordResetModel;
    private UserInvitation $invitationModel;
    private MailService $mailService;

    public function __construct()
    {
        $this->userModel = new User();
        $this->sessionModel = new UserSession();
        $this->passwordResetModel = new PasswordReset();
        $this->invitationModel = new UserInvitation();
        $this->mailService = new MailService();
    }

    public function login(string $email, string $password, string $ipAddress, string $userAgent): array
    {
        $user = $this->userModel->findByEmail($email);

        if (!$user) {
            throw new \RuntimeException('Credenciales incorrectas', 401);
        }

        if ($user['status'] === 'inactive') {
            throw new \RuntimeException('Usuario inactivo. Contacte al administrador', 401);
        }

        if (!$this->userModel->verifyPassword($user, $password)) {
            throw new \RuntimeException('Credenciales incorrectas', 401);
        }

        if ($user['status'] === 'pending_activation') {
            $tempToken = $this->generateTempToken((int) $user['id']);

            return [
                'requires_activation' => true,
                'activation_token' => $tempToken,
                'message' => 'Debe activar su cuenta cambiando la contraseña',
            ];
        }

        $this->userModel->update((int) $user['id'], ['current_event_id' => null]);

        $accessToken = SessionHelper::generateToken($user, 28800);
        $refreshToken = SessionHelper::generateToken($user, 2592000);

        $this->sessionModel->createSession(
            (int) $user['id'],
            $accessToken,
            $ipAddress,
            $userAgent,
            28800
        );

        $this->sessionModel->cleanExpired();

        return [
            'access_token' => $accessToken,
            'refresh_token' => $refreshToken,
            'expires_in' => 28800,
            'user' => [
                'id' => (int) $user['id'],
                'name' => $user['name'],
                'email' => $user['email'],
                'role' => $user['role'],
                'event_id' => null,
            ],
        ];
    }

    public function logout(string $token): void
    {
        $this->sessionModel->invalidateToken($token);
    }

    public function forgotPassword(string $email): void
    {
        $user = $this->userModel->findByEmail($email);

        if ($user && $user['status'] !== 'inactive') {
            $this->passwordResetModel->invalidateExistingTokens((int) $user['id']);

            $token = bin2hex(random_bytes(32));
            $this->passwordResetModel->createToken((int) $user['id'], $token, 28800);

            $this->mailService->sendPasswordReset($user['email'], $token);
        }
    }

    public function resetPassword(string $token, string $newPassword): void
    {
        $reset = $this->passwordResetModel->findValidToken($token);

        if (!$reset) {
            throw new \RuntimeException('Token inválido o expirado', 400);
        }

        $this->userModel->updatePassword((int) $reset['user_id'], $newPassword);
        $this->passwordResetModel->markAsUsed((int) $reset['id']);

        $this->sessionModel->invalidateAllUserTokens((int) $reset['user_id']);
    }

    public function activateAccount(string $activationToken, string $newPassword): array
    {
        $decoded = SessionHelper::decodeToken($activationToken);

        if ($decoded) {
            $user = $this->userModel->findById((int) $decoded['sub']);
        } else {
            $invitation = $this->invitationModel->findByToken($activationToken);
            if (!$invitation) {
                throw new \RuntimeException('Token de activación inválido o expirado', 400);
            }
            $user = $this->userModel->findByEmail($invitation['email']);
        }

        if (!$user) {
            throw new \RuntimeException('Usuario no encontrado', 404);
        }

        if ($user['status'] !== 'pending_activation') {
            throw new \RuntimeException('La cuenta ya ha sido activada', 400);
        }

        $this->userModel->updatePassword((int) $user['id'], $newPassword);
        $this->userModel->activateUser((int) $user['id']);

        return [
            'message' => 'Cuenta activada correctamente',
        ];
    }

    public function register(string $name, string $email, string $password): array
    {
        if (empty(trim($name))) {
            throw new \RuntimeException('El nombre es obligatorio', 422);
        }
        if (empty(trim($email))) {
            throw new \RuntimeException('El correo es obligatorio', 422);
        }
        if (strlen($password) < 8) {
            throw new \RuntimeException('La contraseña debe tener al menos 8 caracteres', 422);
        }

        $existing = $this->userModel->findByEmail($email);
        if ($existing) {
            throw new \RuntimeException('El correo electrónico ya está registrado', 409);
        }

        $userId = $this->userModel->createUser([
            'name' => trim($name),
            'email' => trim($email),
            'password' => $password,
            'role' => 'super_admin',
            'status' => 'active',
        ]);

        return [
            'message' => 'Usuario registrado correctamente',
            'user' => [
                'id' => $userId,
                'name' => trim($name),
                'email' => trim($email),
                'role' => 'super_admin',
            ],
        ];
    }

    private function generateTempToken(int $userId): string
    {
        $config = require BASE_PATH . '/config/app.php';

        $payload = [
            'iss' => $config['app']['url'],
            'iat' => time(),
            'exp' => time() + 900,
            'sub' => $userId,
            'purpose' => 'activation',
        ];

        $secret = $config['jwt']['secret'];
        return \Firebase\JWT\JWT::encode($payload, $secret, 'HS256');
    }
}
