<?php

declare(strict_types=1);

/**
 * Genera IDs de consulta legibles y diferenciados por rol.
 *
 * Revendedor:   REV-K7M2P941
 * Cliente Final: CLIE-R4N8X205
 */
final class ConsultationCodeService
{
    private const LETTERS = 'ABCDEFGHJKLMNPQRSTUVWXYZ';
    private const DIGITS = '23456789';

    public static function prefixForRole(string $roleName): string
    {
        $normalized = mb_strtolower(trim($roleName), 'UTF-8');

        return match ($normalized) {
            'revendedor' => 'REV',
            'cliente final' => 'CLIE',
            default => throw new InvalidArgumentException('El rol no admite un ID de consulta.'),
        };
    }

    public static function generate(PDO $pdo, string $roleName): string
    {
        $prefix = self::prefixForRole($roleName);
        $exists = $pdo->prepare('SELECT 1 FROM usuarios WHERE codigo_consulta = ? LIMIT 1');

        for ($attempt = 0; $attempt < 40; $attempt++) {
            $suffix = self::randomLetter()
                . self::randomDigit()
                . self::randomLetter()
                . self::randomDigit()
                . self::randomLetter()
                . self::randomDigit()
                . self::randomDigit()
                . self::randomDigit();
            $code = $prefix . '-' . $suffix;

            $exists->execute([$code]);
            if (!$exists->fetchColumn()) {
                return $code;
            }
        }

        throw new RuntimeException('No fue posible generar un ID de consulta único.');
    }

    public static function matchesRole(?string $code, string $roleName): bool
    {
        if ($code === null || trim($code) === '') {
            return false;
        }

        $prefix = preg_quote(self::prefixForRole($roleName), '/');
        return preg_match('/^' . $prefix . '-[A-Z0-9]{8}$/', strtoupper(trim($code))) === 1;
    }

    private static function randomLetter(): string
    {
        return self::LETTERS[random_int(0, strlen(self::LETTERS) - 1)];
    }

    private static function randomDigit(): string
    {
        return self::DIGITS[random_int(0, strlen(self::DIGITS) - 1)];
    }
}
