<?php

namespace App\Domain\Facility\Services;

use App\Domain\Facility\Enums\ReservationStatus;
use App\Domain\Facility\Models\Facility;
use App\Domain\Facility\Models\SpaceReservation;
use App\Domain\Shared\Exceptions\DomainException;
use App\Models\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;

class ReservationService
{
    public function __construct(
        private SpaceCollisionService $collisionService,
    ) {}

    /**
     * Create a new space reservation with collision detection.
     */
    public function reserve(array $data, User $creator, ?Model $reservable = null): SpaceReservation
    {
        return DB::transaction(function () use ($data, $creator, $reservable) {
            $facility = Facility::findOrFail($data['facility_id']);

            // Validate facility is active and within operating hours
            $this->collisionService->validateFacilityCanBeReserved(
                $facility, $data['start_time'], $data['end_time']
            );

            // Validate segments are available
            $segmentIds = $data['segment_ids'] ?? [];
            if (!empty($segmentIds)) {
                $this->collisionService->validateSegmentsAvailable($segmentIds, (int) $facility->id);
            }

            // Check for collisions (hard block for confirmed)
            $status = $data['status'] ?? ReservationStatus::Confirmed->value;
            if ($status === ReservationStatus::Confirmed->value) {
                $conflicts = $this->collisionService->check(
                    $data['facility_id'],
                    $data['reservation_date'],
                    $data['start_time'],
                    $data['end_time'],
                    $segmentIds
                );

                if (!empty($conflicts)) {
                    $conflictMsg = collect($conflicts)->map(fn ($c) =>
                        ($c['title'] ?? 'حجز') . ' (' . $c['time'] . ')'
                    )->implode(', ');
                    throw new DomainException('تعارض مع حجوزات مؤكدة: ' . $conflictMsg);
                }
            }

            return SpaceReservation::create([
                ...$data,
                'academy_id' => $creator->academy_id ?? $facility->academy_id,
                // The reservation's branch is the facility's, never the
                // caller's: a booking of a pitch cannot belong to a branch that
                // does not own the pitch. It also keeps auto-reservations filed
                // correctly — they are created from a listener, where the
                // creating hook has no request branch to stamp and would
                // otherwise leave branch_id null, making the booking invisible
                // on every branch's grid while still blocking the slot.
                'branch_id' => $facility->branch_id,
                'reservable_type' => $reservable ? $reservable->getMorphClass() : null,
                'reservable_id' => $reservable?->getKey(),
                'created_by' => $creator->id,
            ]);
        });
    }

    /**
     * Cancel a reservation and free the space.
     */
    public function cancel(SpaceReservation $reservation, User $actor, ?string $reason = null): SpaceReservation
    {
        return DB::transaction(function () use ($reservation, $actor, $reason) {
            if ($reservation->status === ReservationStatus::Cancelled) {
                throw new DomainException('الحجز ملغى بالفعل');
            }

            $reservation->update([
                'status' => ReservationStatus::Cancelled->value,
                'cancelled_by' => $actor->id,
                'cancelled_at' => now(),
                'notes' => $reason
                    ? ($reservation->notes ? $reservation->notes . "\n" . $reason : $reason)
                    : $reservation->notes,
            ]);

            return $reservation->fresh();
        });
    }

    /**
     * Confirm a tentative reservation (re-checks collisions).
     */
    public function confirm(SpaceReservation $reservation): SpaceReservation
    {
        return DB::transaction(function () use ($reservation) {
            if ($reservation->status !== ReservationStatus::Tentative) {
                throw new DomainException('يمكن تأكيد الحجوزات المبدئية فقط');
            }

            // Re-check collisions before confirming
            $conflicts = $this->collisionService->check(
                $reservation->facility_id,
                $reservation->reservation_date->format('Y-m-d'),
                $reservation->start_time,
                $reservation->end_time,
                $reservation->segment_ids ?? [],
                $reservation->id
            );

            if (!empty($conflicts)) {
                throw new DomainException('لا يمكن التأكيد — تعارض مع حجوزات أخرى');
            }

            $reservation->update(['status' => ReservationStatus::Confirmed->value]);
            return $reservation->fresh();
        });
    }

    /**
     * Cancel reservation linked to a training session.
     * Called when a session is cancelled.
     */
    public function cancelForSession(Model $session, User $actor): void
    {
        $reservations = SpaceReservation::where('reservable_type', $session->getMorphClass())
            ->where('reservable_id', $session->getKey())
            ->where('status', '!=', ReservationStatus::Cancelled->value)
            ->get();

        foreach ($reservations as $reservation) {
            $this->cancel($reservation, $actor, 'ملغى بسبب إلغاء الحصة');
        }
    }

    /**
     * Auto-reserve facility space for a newly created training session.
     */
    public function autoReserveForSession(\App\Domain\Training\Models\TrainingSession $session, \App\Domain\Training\Models\TrainingSchedule $schedule): void
    {
        $segmentIds = $schedule->space_reservation_template ?? [];
        if (!$schedule->facility_id || empty($segmentIds)) {
            return;
        }

        try {
            $this->reserve([
                'academy_id' => $session->academy_id,
                'facility_id' => $schedule->facility_id,
                'reservation_date' => $session->session_date->toDateString(),
                'start_time' => $session->start_time,
                'end_time' => $session->end_time,
                'segment_ids' => $segmentIds,
                'status' => 'confirmed',
                'notes' => "حجز تلقائي للحصة #{$session->session_number}",
            ], \App\Models\User::find($session->trainer_id) ?? \App\Models\User::first(), $session);
        } catch (\Throwable $e) {
            \Illuminate\Support\Facades\Log::warning("Auto-reservation failed for session {$session->id}: " . $e->getMessage());
        }
    }
}
