<?php

namespace Modules\Booking\Services;

use Modules\Booking\Models\Booking;

class BookingRefGenerator
{
    private const PREFIX = 'EVB';

    private const CHARS = '123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';

    /**
     * Must be called inside the same DB::transaction() as the booking insert
     * — the row lock on the latest booking is what keeps concurrent callers
     * from generating the same ref, and it only holds for the transaction's
     * lifetime.
     */
    public function generate(): string
    {
        // Lock the latest row so concurrent transactions can't read the same ref.
        $latest = Booking::lockForUpdate()->orderByDesc('id')->value('booking_ref');

        // If the latest ref doesn't match the expected format, start the sequence fresh.
        if ($latest && preg_match('/^[A-Z]+-[A-Z0-9]+$/', $latest)) {
            return $this->incrementRef($latest);
        }

        return self::PREFIX.'-AAAAA1';
    }

    private function incrementRef(string $ref): string
    {
        preg_match('/^(.*)-([A-Z0-9]+)$/', $ref, $matches);

        $prefix = $matches[1];
        $suffix = str_split($matches[2]);
        $base = strlen(self::CHARS);
        $i = count($suffix) - 1;
        $carry = true;

        while ($i >= 0 && $carry) {
            $idx = strpos(self::CHARS, $suffix[$i]);

            if ($idx + 1 < $base) {
                $suffix[$i] = self::CHARS[$idx + 1];
                $carry = false;
            } else {
                $suffix[$i] = self::CHARS[0];
            }
            $i--;
        }

        if ($carry) {
            array_unshift($suffix, self::CHARS[0]);
        }

        return $prefix.'-'.implode('', $suffix);
    }
}
