<?php

namespace App\Libraries;

use Config\Database;
use RuntimeException;

/**
 * Issues unique, gap-less folios per (year, kind).
 *
 * Format: SIM-YYYY-#### for kind=inspection
 *         SIM-R-YYYY-#### for kind=report
 *         SIM-A-YYYY-#### for kind=delivery_note
 */
class FolioService
{
    public function next(string $kind): string
    {
        if (! in_array($kind, ['inspection', 'report', 'delivery_note'], true)) {
            throw new RuntimeException("Folio kind inválido: {$kind}");
        }

        $db = Database::connect();
        $year = (int) date('Y');

        $db->transStart();
        $db->transStrict(true);

        $row = $db->query(
            'SELECT id, last_number FROM folio_sequences WHERE year = ? AND kind = ? FOR UPDATE',
            [$year, $kind]
        )->getRowArray();

        if ($row === null) {
            $db->table('folio_sequences')->insert([
                'year' => $year,
                'kind' => $kind,
                'last_number' => 1,
            ]);
            $next = 1;
        } else {
            $next = ((int) $row['last_number']) + 1;
        }

        $prefix = match ($kind) {
            'report' => 'SIM-R',
            'delivery_note' => 'SIM-A',
            default => 'SIM',
        };

        while ($this->folioExists($kind, sprintf('%s-%d-%04d', $prefix, $year, $next))) {
            $next++;
        }

        $db->table('folio_sequences')
            ->where('year', $year)
            ->where('kind', $kind)
            ->update(['last_number' => $next]);

        $db->transComplete();

        if ($db->transStatus() === false) {
            throw new RuntimeException('No fue posible reservar folio.');
        }

        return sprintf('%s-%d-%04d', $prefix, $year, $next);
    }

    private function folioExists(string $kind, string $folio): bool
    {
        $table = match ($kind) {
            'inspection' => 'inspections',
            'report' => 'reports',
            'delivery_note' => 'delivery_notes',
        };

        return Database::connect()
            ->table($table)
            ->where('folio', $folio)
            ->countAllResults() > 0;
    }

    public static function token(): string
    {
        return bin2hex(random_bytes(16));
    }
}
