<?php

namespace App\Services;

use App\Models\RoomAvailability;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Illuminate\Validation\ValidationException;

class RoomAvailabilityService
{
    public function isAvailable(int $roomId, string $startDate, string $endDate): bool
    {
        return !RoomAvailability::query()
            ->forRoom($roomId)
            ->active()
            ->overlapping($startDate, $endDate)
            ->exists();
    }


    // -------------------------------------------------------------------------- Booking Related

    public function holdAvailability(int $roomId,  int $bookingId,  string $startDate,  string $endDate): RoomAvailability
    {

        $this->ensureNoOverlap($roomId, $startDate, $endDate);

        // avoid duplicate hold
        $existing = RoomAvailability::where(['room_id' => $roomId, 'booking_id' => $bookingId, 'status' => 'pending'])->first();

        if ($existing) {
            return $existing;
        }

        return RoomAvailability::create([
            'room_id' => $roomId,
            'booking_id' => $bookingId,
            'start_date' => $startDate,
            'end_date' => $endDate,
            'status' => 'pending',
        ]);
    }

    public function confirmAvailability(int $roomId, int $bookingId, string $startDate, string $endDate): RoomAvailability
    {
        $this->ensureNoOverlap($roomId, $startDate, $endDate);

        $record = RoomAvailability::where(['room_id' => $roomId, 'booking_id' => $bookingId])->first();

        if ($record) {
            $record->update(['status' => 'booked']);
            return $record->fresh();
        }

        return RoomAvailability::create([
            'room_id' => $roomId,
            'booking_id' => $bookingId,
            'start_date' => $startDate,
            'end_date' => $endDate,
            'status' => 'booked',
        ]);
    }

    public function cancelAvailability(int $bookingId): void
    {
        RoomAvailability::where('booking_id', $bookingId)->delete();
    }

    public function checkedoutAvailability(int $bookingId): void
    {
        RoomAvailability::where('booking_id', $bookingId)->update(['status' => 'checked_out']);
    }

    // --------------------------------------------------------------------------

    public function createRecord(int $roomId, ?int $bookingId, string $startDate, string $endDate, string $status = 'pending', string $source = 'website', string $message, ?int $createdBy = null): RoomAvailability
    {
        $this->ensureNoOverlap($roomId, $startDate, $endDate);
        return RoomAvailability::create([
            'room_id' => $roomId,
            'booking_id' => $bookingId,
            'start_date' => $startDate,
            'end_date' => $endDate,
            'status' => $status,
            'source' => $source,
            'message' => $message,
            'created_by' => $createdBy,
        ]);
    }

    public function updateAvailability(int $id,  int $roomId,  string $startDate,  string $endDate,  string $status,  ?string $message = null,  ?int $updatedBy = null): RoomAvailability
    {

        $availability = RoomAvailability::findOrFail($id);

        // Ignore current record during overlap check
        $this->ensureNoOverlap($roomId, $startDate, $endDate, $id);

        $availability->update([
            'room_id' => $roomId,
            'start_date' => $startDate,
            'end_date' => $endDate,
            'status' => $status,
            'message' => $message,
            'updated_by' => $updatedBy,
        ]);

        return $availability->fresh();
    }

    public function getCalendar(int $roomId, string $startDate, string $endDate): Collection
    {
        return RoomAvailability::query()
            ->forRoom($roomId)
            ->overlapping($startDate, $endDate)
            ->orderBy('start_date')
            ->get();
    }

    public function getPaginatedList(int $roomId, int $perPage = 10): LengthAwarePaginator
    {
        return RoomAvailability::query()
            ->forRoom($roomId)
            ->latest()
            ->paginate($perPage);
    }

    public function delete(int $id): bool
    {
        return RoomAvailability::where('id', $id)->delete();
    }

    private function ensureNoOverlap(int $roomId, string $startDate, string $endDate, ?int $ignoreId = null): void
    {
        $query  = RoomAvailability::query()->forRoom($roomId)->overlapping($startDate, $endDate);

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

        $exists = $query->exists();

        if ($exists) {
            throw ValidationException::withMessages(['room_id' => ['This room already has an overlapping availability record.']]);
        }
    }
}
