<?php

namespace MintyPHP\Service\Auth;

use MintyPHP\Http\Request;
use MintyPHP\I18n;
use MintyPHP\Repository\Auth\EmailVerificationRepositoryInterface;
use MintyPHP\Repository\User\UserReadRepositoryInterface;
use MintyPHP\Repository\User\UserWriteRepositoryInterface;
use MintyPHP\Service\Mail\MailService;

class EmailVerificationService
{
    private const CODE_LENGTH = 6;
    private const EXPIRY_MINUTES = 30;
    private const MAX_ATTEMPTS = 5;

    public function __construct(
        private readonly UserReadRepositoryInterface $userReadRepository,
        private readonly UserWriteRepositoryInterface $userWriteRepository,
        private readonly EmailVerificationRepositoryInterface $emailVerificationRepository,
        private readonly MailService $mailService
    ) {
    }

    public function sendVerification(int $userId, ?string $locale = null): array
    {
        $user = $this->userReadRepository->find($userId);
        if (!$user || !isset($user['id'])) {
            return ['ok' => false, 'error' => 'user_not_found'];
        }

        $email = (string) ($user['email'] ?? '');
        if ($email === '') {
            return ['ok' => false, 'error' => 'email_required'];
        }

        $this->emailVerificationRepository->invalidateForUser($userId);

        $code = $this->generateCode();
        $codeHash = password_hash($code, PASSWORD_DEFAULT);
        $expiresAt = gmdate('Y-m-d H:i:s', time() + (self::EXPIRY_MINUTES * 60));

        $verificationId = $this->emailVerificationRepository->create($userId, $codeHash, $expiresAt);
        if (!$verificationId) {
            return ['ok' => false, 'error' => 'create_failed'];
        }

        $locale = $locale ?: ($user['locale'] ?? null) ?: (I18n::$locale ?? I18n::$defaultLocale);
        $name = trim(($user['first_name'] ?? '') . ' ' . ($user['last_name'] ?? ''));
        $isGerman = strpos((string) $locale, 'de') === 0;
        $greeting = $isGerman ? 'Hallo' : 'Hello';
        if ($name !== '') {
            $greeting .= ' ' . $name;
        }
        $greeting .= ',';
        $verifyPath = Request::withLocale('verify-email', $locale);
        $verifyUrl = appUrl($verifyPath);
        $previousLocale = I18n::$locale ?? null;
        I18n::$locale = $locale;
        $subject = t('Email verification code');
        I18n::$locale = $previousLocale;

        $vars = [
            'app_name' => appTitle(),
            'app_logo_url' => appLogoUrlAbsolute(128),
            'imprint_url' => appUrl(Request::withLocale('imprint', $locale)),
            'privacy_url' => appUrl(Request::withLocale('privacy', $locale)),
            'code' => $code,
            'expires_minutes' => self::EXPIRY_MINUTES,
            'verify_url' => $verifyUrl,
            'greeting' => $greeting,
        ];
        $this->mailService->sendTemplate('email_verification', $vars, $email, $subject, $locale);

        return ['ok' => true];
    }

    public function verifyCode(string $email, string $code): array
    {
        $email = trim($email);
        $code = trim($code);
        if ($email === '' || $code === '') {
            return ['ok' => false, 'error' => 'invalid'];
        }

        $user = $this->userReadRepository->findByEmail($email);
        if (!$user || !isset($user['id'])) {
            return ['ok' => false, 'error' => 'invalid'];
        }

        $userId = (int) $user['id'];

        // Check if already verified
        if (!empty($user['email_verified_at'])) {
            return ['ok' => false, 'error' => 'already_verified'];
        }

        $verification = $this->emailVerificationRepository->findActiveByUserId($userId);
        if (!$verification || !isset($verification['id'])) {
            return ['ok' => false, 'error' => 'invalid'];
        }

        $attempts = (int) ($verification['attempts'] ?? 0);
        if ($attempts >= self::MAX_ATTEMPTS) {
            return ['ok' => false, 'error' => 'too_many_attempts'];
        }

        $hash = (string) ($verification['code_hash'] ?? '');
        if ($hash === '' || !password_verify($code, $hash)) {
            $this->emailVerificationRepository->incrementAttempts((int) $verification['id']);
            return ['ok' => false, 'error' => 'invalid'];
        }

        // Mark verification as used
        $this->emailVerificationRepository->markUsed((int) $verification['id']);

        // Mark user email as verified
        $this->userWriteRepository->setEmailVerified($userId);

        return ['ok' => true, 'user_id' => $userId];
    }

    public function resendVerification(string $email, ?string $locale = null): array
    {
        $email = trim($email);
        if ($email === '') {
            return ['ok' => false, 'error' => 'email_required'];
        }

        $user = $this->userReadRepository->findByEmail($email);
        if (!$user || !isset($user['id'])) {
            // Don't reveal if user exists
            return ['ok' => true];
        }

        // Check if already verified
        if (!empty($user['email_verified_at'])) {
            return ['ok' => false, 'error' => 'already_verified'];
        }

        return $this->sendVerification((int) $user['id'], $locale);
    }

    public function getExpiryMinutes(): int
    {
        return self::EXPIRY_MINUTES;
    }

    private function generateCode(): string
    {
        $max = (10 ** self::CODE_LENGTH) - 1;
        $code = (string) random_int(0, $max);
        return str_pad($code, self::CODE_LENGTH, '0', STR_PAD_LEFT);
    }
}
