<?php

namespace App\Domain\Facility\Services;

use App\Domain\Facility\Enums\FacilityStatus;
use App\Domain\Facility\Enums\ReservationStatus;
use App\Domain\Facility\Models\Facility;
use App\Domain\Facility\Models\SpaceReservation;
use App\Domain\Facility\Models\SpaceSegment;
use App\Domain\Shared\Exceptions\DomainException;

class SpaceCollisionService
{
    /**
     * Check for confirmed reservation collisions.
     *
     * Returns array of conflicts (empty = no collision).
     */
    public function check(
        int $facilityId,
        string $date,
        string $startTime,
        string $endTime,
        array $segmentIds,
        ?int $excludeId = null
    ): array {
        // 1. Find confirmed reservations for facility on date that overlap in time
        $query = SpaceReservation::where('facility_id', $facilityId)
            ->where('reservation_date', $date)
            ->where('status', ReservationStatus::Confirmed)
            ->where('start_time', '<', $endTime)
            ->where('end_time', '>', $startTime);

        // 3. Exclude self (if editing)
        if ($excludeId) {
            $query->where('id', '!=', $excludeId);
        }

        $overlapping = $query->get();

        if ($overlapping->isEmpty()) {
            return [];
        }

        // 4-5. Decode segments JSON and intersect with requested
        $conflicts = [];
        foreach ($overlapping as $reservation) {
            $occupiedSegments = $reservation->segment_ids ?? [];
            $intersection = array_intersect($segmentIds, $occupiedSegments);

            // 6-7. Non-empty intersection = conflict
            if (!empty($intersection)) {
                $conflicts[] = [
                    'reservation_id' => $reservation->id,
                    'title' => $reservation->title,
                    'time' => $reservation->start_time . ' - ' . $reservation->end_time,
                    'conflicting_segments' => array_values($intersection),
                ];
            }
        }

        return $conflicts;
    }

    /**
     * Get tentative reservation warnings (non-blocking).
     */
    public function getTentativeWarnings(
        int $facilityId,
        string $date,
        string $startTime,
        string $endTime,
        array $segmentIds
    ): array {
        $query = SpaceReservation::where('facility_id', $facilityId)
            ->where('reservation_date', $date)
            ->where('status', ReservationStatus::Tentative)
            ->where('start_time', '<', $endTime)
            ->where('end_time', '>', $startTime);

        $overlapping = $query->get();
        $warnings = [];

        foreach ($overlapping as $reservation) {
            $occupiedSegments = $reservation->segment_ids ?? [];
            $intersection = array_intersect($segmentIds, $occupiedSegments);

            if (!empty($intersection)) {
                $warnings[] = [
                    'reservation_id' => $reservation->id,
                    'title' => $reservation->title,
                    'time' => $reservation->start_time . ' - ' . $reservation->end_time,
                    'conflicting_segments' => array_values($intersection),
                ];
            }
        }

        return $warnings;
    }

    /**
     * Validate that all requested segments belong to the facility being
     * reserved and are marked as available.
     *
     * The membership half is not decoration. The ids arrive from the booking
     * form and were only ever checked for availability, so a segment id from
     * another facility — another branch's facility — passed and was stored
     * verbatim in the reservation's segment_ids JSON, which made collision
     * detection meaningless for both facilities. Since SpaceSegment is scoped
     * through its layout, a foreign id now returns no row at all and would pass
     * this check silently; comparing the count is what turns that into a
     * refusal.
     */
    public function validateSegmentsAvailable(array $segmentIds, ?int $facilityId = null): void
    {
        $segmentIds = array_values(array_unique(array_map('intval', $segmentIds)));

        if ($segmentIds === []) {
            return;
        }

        $segments = SpaceSegment::whereIn('id', $segmentIds)
            ->when($facilityId, fn ($q) => $q->whereHas(
                'layout',
                fn ($l) => $l->where('facility_id', $facilityId)
            ))
            ->get(['id', 'space_layout_id', 'name_ar', 'is_available']);

        if ($facilityId && $segments->count() !== count($segmentIds)) {
            throw new DomainException('بعض الأجزاء المحددة لا تخص هذه المنشأة');
        }

        $unavailable = $segments->where('is_available', false)->pluck('name_ar');

        if ($unavailable->isNotEmpty()) {
            throw new DomainException('الأجزاء التالية غير متاحة: ' . $unavailable->implode(', '));
        }
    }

    /**
     * Validate that the facility is active and the time is within operating hours.
     */
    public function validateFacilityCanBeReserved(Facility $facility, string $startTime, string $endTime): void
    {
        if ($facility->status !== FacilityStatus::Active) {
            throw new DomainException('المنشأة غير نشطة حالياً: ' . $facility->status->label());
        }

        if ($facility->operating_start && $startTime < $facility->operating_start) {
            throw new DomainException('وقت البداية قبل ساعات العمل (' . $facility->operating_start . ')');
        }

        if ($facility->operating_end && $endTime > $facility->operating_end) {
            throw new DomainException('وقت النهاية بعد ساعات العمل (' . $facility->operating_end . ')');
        }
    }
}
