<?php

namespace App\Services;

use App\Enums\BookingStatus;
use App\Models\Booking;
use DomainException;

class BookingStatusService
{
    /**
     * @var array<string, list<string>>
     */
    private const TRANSITIONS = [
        'enquiry' => ['provisional', 'awaiting_approval', 'confirmed', 'cancelled'],
        'provisional' => ['awaiting_approval', 'awaiting_deposit', 'confirmed', 'cancelled'],
        'awaiting_approval' => ['awaiting_deposit', 'confirmed', 'cancelled'],
        'awaiting_deposit' => ['confirmed', 'cancelled'],
        'confirmed' => ['in_progress', 'postponed', 'cancelled'],
        'in_progress' => ['completed', 'cancelled', 'no_show'],
        'completed' => [],
        'postponed' => ['confirmed', 'cancelled'],
        'cancelled' => [],
        'no_show' => [],
    ];

    public function __construct(
        private readonly AuditService $auditService,
    ) {}

    public function canTransition(Booking $booking, BookingStatus $to): bool
    {
        $from = $booking->status->value;
        $allowed = self::TRANSITIONS[$from] ?? [];

        return in_array($to->value, $allowed, true);
    }

    public function transition(Booking $booking, BookingStatus $to, ?string $reason = null): void
    {
        if (! $this->canTransition($booking, $to)) {
            throw new DomainException(
                sprintf(
                    'Cannot transition booking from %s to %s.',
                    $booking->status->label(),
                    $to->label(),
                ),
            );
        }

        $oldStatus = $booking->status;
        $oldValues = ['status' => $oldStatus->value];

        $booking->update(['status' => $to]);

        $this->auditService->log(
            'booking.status_changed',
            $booking,
            $oldValues,
            ['status' => $to->value],
            $reason,
        );
    }

    /**
     * @return list<string>
     */
    public function allowedTransitions(Booking $booking): array
    {
        return self::TRANSITIONS[$booking->status->value] ?? [];
    }
}
