<?php

declare(strict_types=1);

namespace App\Services;

use App\Core\Database;

/**
 * EmailVerificationService
 *
 * Handles both "verify the email address on a brand-new account" and
 * "verify a changed email address" with the same token mechanism
 * (`email_verification_tokens.new_email` is NULL for the first case,
 * set for the second). Sending the actual email is delegated to
 * MailService — this class only issues/validates tokens and flips
 * users.email_verified_at.
 */
final class EmailVerificationService
{
    public function __construct(private readonly MailService $mailer = new MailService())
    {
    }

    public function sendVerification(int $userId, string $email, ?string $newEmail = null): bool
    {
        $token = bin2hex(random_bytes(32));
        $tokenHash = hash('sha256', $token);

        Database::connection()->prepare(
            'INSERT INTO email_verification_tokens (user_id, token_hash, new_email, expires_at, created_at)
             VALUES (:user_id, :hash, :new_email, :expires, NOW())'
        )->execute([
            'user_id' => $userId,
            'hash' => $tokenHash,
            'new_email' => $newEmail,
            'expires' => date('Y-m-d H:i:s', time() + 86400), // 24 hours
        ]);

        $verifyUrl = base_url('verify-email/' . $token);
        $targetEmail = $newEmail ?? $email;

        return $this->mailer->send(
            $targetEmail,
            '',
            'Verify your email — A&B Hub',
            "<p>Please confirm this email address for your A&B Hub account:</p>"
            . "<p><a href=\"{$verifyUrl}\">{$verifyUrl}</a></p>"
            . '<p>This link expires in 24 hours.</p>'
        );
    }

    /** @return array{success:bool, message:string} */
    public function verify(string $rawToken): array
    {
        $pdo = Database::connection();
        $hash = hash('sha256', $rawToken);

        $stmt = $pdo->prepare(
            'SELECT * FROM email_verification_tokens WHERE token_hash = :hash AND verified_at IS NULL LIMIT 1'
        );
        $stmt->execute(['hash' => $hash]);
        $row = $stmt->fetch();

        if ($row === false) {
            return ['success' => false, 'message' => 'This verification link is invalid or has already been used.'];
        }

        if (strtotime($row['expires_at']) < time()) {
            return ['success' => false, 'message' => 'This verification link has expired. Please request a new one.'];
        }

        $pdo->beginTransaction();
        try {
            $pdo->prepare('UPDATE email_verification_tokens SET verified_at = NOW() WHERE id = :id')
                ->execute(['id' => $row['id']]);

            if ($row['new_email'] !== null) {
                $pdo->prepare('UPDATE users SET email = :email, email_verified_at = NOW() WHERE id = :id')
                    ->execute(['email' => $row['new_email'], 'id' => $row['user_id']]);
            } else {
                $pdo->prepare('UPDATE users SET email_verified_at = NOW() WHERE id = :id')
                    ->execute(['id' => $row['user_id']]);
            }

            $pdo->commit();
        } catch (\Throwable $e) {
            $pdo->rollBack();
            throw $e;
        }

        ActivityLogger::logAction('Email address verified', 'security', 'user', (int) $row['user_id']);

        return ['success' => true, 'message' => 'Email verified successfully.'];
    }

    public function isVerified(int $userId): bool
    {
        $stmt = Database::connection()->prepare('SELECT email_verified_at FROM users WHERE id = :id');
        $stmt->execute(['id' => $userId]);

        return $stmt->fetchColumn() !== null;
    }
}
