<?php

namespace App\Models;

use Config\Lookups;

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

    protected $allowedFields = [
        'guest_id',
        'walk_in_name',
        'walk_in_phone',
        'party_size',
        'reservation_date',
        'reservation_time',
        'dining_table_id',
        'status',
    ];

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

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

        $this->validationRules = [
            'party_size'       => 'required|integer|greater_than[0]',
            'reservation_date' => 'required|valid_date[Y-m-d]',
            'reservation_time' => 'required',
            'status'           => 'permit_empty|in_list[' . implode(',', $statuses) . ']',
        ];
    }

    public function withDetails()
    {
        return $this->select("restaurant_reservations.*,
                guests.full_name as guest_name, guests.phone as guest_phone, guests.segment as guest_segment,
                dining_memberships.tier as dining_tier,
                dining_tables.table_number as table_number, dining_areas.name as area_name")
            ->join('guests', 'guests.id = restaurant_reservations.guest_id', 'left')
            ->join('dining_memberships', "dining_memberships.guest_id = restaurant_reservations.guest_id AND dining_memberships.status = 'active'", 'left')
            ->join('dining_tables', 'dining_tables.id = restaurant_reservations.dining_table_id', 'left')
            ->join('dining_areas', 'dining_areas.id = dining_tables.dining_area_id', 'left');
    }

    public function forDate(string $date): array
    {
        return $this->withDetails()
            ->where('restaurant_reservations.reservation_date', $date)
            ->orderBy('restaurant_reservations.reservation_time')
            ->findAll();
    }

    /**
     * Table IDs already booked/seated at this date+time — used to derive
     * the floor plan's available/booked/occupied view. "Occupied" (as
     * opposed to "booked") additionally checks for a status of `seated`.
     */
    public function tableIdsBookedAt(string $date, string $time): array
    {
        return array_column(
            $this->where('reservation_date', $date)
                ->where('reservation_time', $time)
                ->whereIn('status', ['booked', 'seated'])
                ->whereNotNull('dining_table_id')
                ->findAll(),
            'dining_table_id'
        );
    }

    /**
     * Same table, same date+time, still booked/seated. Reservations don't
     * track a duration/end time, so this is an exact-slot check rather
     * than the kind of range-overlap check bookings do — flagged as a
     * simplification in the Phase 2 notes.
     */
    public function hasConflict(int $tableId, string $date, string $time, ?int $excludeReservationId = null): bool
    {
        $builder = $this->where('dining_table_id', $tableId)
            ->where('reservation_date', $date)
            ->where('reservation_time', $time)
            ->whereIn('status', ['booked', 'seated']);

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

        return $builder->countAllResults() > 0;
    }

    /**
     * Persisted running count on the guest profile — see the
     * AddRestaurantNoShowCountToGuests migration for why this isn't just
     * computed with COUNT(*) at read time.
     */
    public function markNoShow(int $reservationId): void
    {
        $reservation = $this->find($reservationId);

        if ($reservation === null) {
            return;
        }

        $this->update($reservationId, ['status' => 'no_show']);

        if ($reservation->guest_id !== null) {
            $this->db->table('guests')
                ->where('id', $reservation->guest_id)
                ->set('restaurant_no_show_count', 'restaurant_no_show_count + 1', false)
                ->update();
        }
    }
}
