<?php

namespace App\Services;

use App\Models\Appointment;
use App\Models\BarberBlock;
use Carbon\Carbon;
use Illuminate\Validation\ValidationException;

/**
 * Prevents overlapping appointments for the same barber on the same calendar day,
 * and blocks manual bookings that fall inside barber "block" intervals (same rules as SlotService).
 * Call {@see assertBarberIntervalAvailable} inside DB::transaction so locks are effective.
 */
class AppointmentConflictService
{
    /**
     * @param  Carbon  $intervalStart  Full datetime (date + time) for the new appointment start
     * @param  Carbon  $intervalEnd  Full datetime for the new appointment end (exclusive overlap uses standard interval check)
     */
    public function assertBarberIntervalAvailable(
        int $barberId,
        string $appointmentDateYmd,
        Carbon $intervalStart,
        Carbon $intervalEnd,
        ?int $excludeAppointmentId = null
    ): void {
        if (! $intervalStart->lt($intervalEnd)) {
            throw ValidationException::withMessages([
                'start_time' => ['El horario de fin debe ser posterior al inicio.'],
            ]);
        }

        $query = Appointment::query()
            ->where('barber_id', $barberId)
            ->where('appointment_date', $appointmentDateYmd)
            ->whereNotIn('status', ['cancelled']);

        if ($excludeAppointmentId !== null) {
            $query->where('id', '!=', $excludeAppointmentId);
        }

        $locked = $query->lockForUpdate()->get();

        foreach ($locked as $apt) {
            $aptStart = Carbon::parse("{$appointmentDateYmd} {$apt->start_time}");
            $aptEnd = Carbon::parse("{$appointmentDateYmd} {$apt->end_time}");

            if ($intervalStart->lt($aptEnd) && $intervalEnd->gt($aptStart)) {
                throw ValidationException::withMessages([
                    'start_time' => ['Ese horario se solapa con otra cita del mismo barbero.'],
                ]);
            }
        }

        $blocks = BarberBlock::query()
            ->where('barber_id', $barberId)
            ->where('block_date', $appointmentDateYmd)
            ->where('kind', 'block')
            ->get();

        foreach ($blocks as $block) {
            $bStart = Carbon::parse("{$appointmentDateYmd} {$block->start_time}");
            $bEnd = Carbon::parse("{$appointmentDateYmd} {$block->end_time}");

            if ($intervalStart->lt($bEnd) && $intervalEnd->gt($bStart)) {
                throw ValidationException::withMessages([
                    'start_time' => ['Ese horario cae dentro de un bloqueo de agenda del barbero.'],
                ]);
            }
        }
    }
}
