<?php

namespace App\Models;

use Config\Lookups;

class RoomModel extends TenantModel
{
    protected $table            = 'rooms';
    protected $primaryKey       = 'id';
    protected $useAutoIncrement = true;
    protected $returnType       = 'object';
    protected $useTimestamps    = true;

    protected $allowedFields = [
        'room_number',
        'room_type_id',
        'floor',
        'status',
    ];

    public function __construct()
    {
        parent::__construct();

        $statuses = array_keys((new Lookups())->roomStatuses);

        $this->validationRules = [
            'room_number'  => 'required|max_length[20]|tenant_unique[rooms.room_number,id,{id}]',
            'room_type_id' => 'required|integer',
            'status'       => 'permit_empty|in_list[' . implode(',', $statuses) . ']',
        ];
    }

    /**
     * All rooms joined with their type name — the board and the booking
     * search both want this, so it lives here rather than being repeated.
     */
    public function withType()
    {
        return $this->select('rooms.*, room_types.name as room_type_name, room_types.base_rate as base_rate, room_types.capacity as capacity')
            ->join('room_types', 'room_types.id = rooms.room_type_id');
    }

    public function setStatus(int $roomId, string $status): bool
    {
        return $this->update($roomId, ['status' => $status]);
    }

    /**
     * Rooms of the given type (or any type, if null) with no booking that
     * overlaps [checkIn, checkOut). Used by the "create booking" search.
     */
    public function findAvailable(string $checkIn, string $checkOut, ?int $roomTypeId = null): array
    {
        $builder = $this->withType();

        if ($roomTypeId !== null) {
            $builder->where('rooms.room_type_id', $roomTypeId);
        }

        // pending_deposit holds the room just like confirmed does — kept
        // in sync with BookingModel::hasOverlap()'s status list.
        $builder->whereNotIn('rooms.id', function ($subQuery) use ($checkIn, $checkOut) {
            return $subQuery->select('room_id')
                ->from('bookings')
                ->whereIn('status', ['pending_deposit', 'confirmed', 'checked_in'])
                ->where('check_in_date <', $checkOut)
                ->where('check_out_date >', $checkIn);
        });

        return $builder->orderBy('rooms.room_number')->findAll();
    }

    /**
     * Same as findAvailable(), for the "Change Room" flow — a booking
     * being reassigned shouldn't count its own reservation as blocking
     * anything, so its id is excluded from the overlap check (mirrors
     * BookingModel::hasOverlap()'s $excludeBookingId). $currentRoomId is
     * excluded outright too — the room a booking is already in isn't an
     * "other" option to move into, and excluding the booking's own id
     * from the overlap check above would otherwise make that room look
     * "free" and list itself as a choice.
     */
    public function findAvailableExcludingBooking(string $checkIn, string $checkOut, int $excludeBookingId, int $currentRoomId): array
    {
        return $this->withType()
            ->where('rooms.id !=', $currentRoomId)
            ->whereNotIn('rooms.id', function ($subQuery) use ($checkIn, $checkOut, $excludeBookingId) {
                return $subQuery->select('room_id')
                    ->from('bookings')
                    ->whereIn('status', ['pending_deposit', 'confirmed', 'checked_in'])
                    ->where('id !=', $excludeBookingId)
                    ->where('check_in_date <', $checkOut)
                    ->where('check_out_date >', $checkIn);
            })
            ->orderBy('rooms.room_number')
            ->findAll();
    }
}
