<?php

namespace App\Commands;

use App\Models\AiMessageDraftModel;
use App\Models\BookingModel;
use App\Models\BookingServiceItemModel;
use App\Models\Control\TenantRecordModel;
use App\Models\EventModel;
use App\Models\FolioLineItemModel;
use App\Models\GuestModel;
use App\Models\HotelServiceModel;
use App\Models\RatePlanModel;
use App\Models\RoomModel;
use App\Models\RoomTypeModel;
use CodeIgniter\CLI\BaseCommand;
use CodeIgniter\CLI\CLI;

/**
 * Batch job (same per-tenant loop shape as SendPreArrivalReminders/
 * the other scheduled AI commands this session) meant to run daily via
 * cron:
 *   php spark ai:pre-arrival-upsell
 *
 * For each booking arriving exactly `ai.pre_arrival_upsell_days_ahead`
 * days from now (default 2, tenant-configurable), drafts a short
 * WhatsApp/SMS suggesting 1-2 genuinely relevant add-ons — either from
 * the hotel's real Services & Extras catalog (real names, real prices —
 * see HotelServiceModel) or a room upgrade, and a room upgrade is only
 * ever suggested after a real check_room_availability tool call
 * confirms it's actually free for those exact dates, never assumed.
 * Drafts queue into the same staff-review inbox as Post-Checkout
 * Thank-You (see GuestMessageDraftController) — nothing sends without a
 * manager clicking Send, unless the tenant has opted this category into
 * autosend (default off).
 */
class PreArrivalUpsell extends BaseCommand
{
    protected $group       = 'AI';
    protected $name        = 'ai:pre-arrival-upsell';
    protected $description = 'Drafts a pre-arrival upsell suggestion for bookings arriving in N days (tenant-configurable), if not already drafted.';

    public function run(array $params)
    {
        $tenants = (new TenantRecordModel())->where('status', 'active')->findAll();

        foreach ($tenants as $tenant) {
            $this->upsellForTenant($tenant);
        }
    }

    private function upsellForTenant(object $tenant): void
    {
        service('tenantContext')->setTenant($tenant);

        if (service('settings')->get('ai.pre_arrival_upsell_enabled', '0') !== '1') {
            CLI::write("{$tenant->slug}: pre-arrival upsell is turned off, skipping.", 'white');

            return;
        }

        $daysAhead  = max(1, (int) (service('settings')->get('ai.pre_arrival_upsell_days_ahead') ?: 2));
        $targetDate = date('Y-m-d', strtotime("+{$daysAhead} days"));

        $bookings = (new BookingModel())->withDetails()
            ->where('bookings.check_in_date', $targetDate)
            ->where('bookings.status', 'confirmed')
            ->findAll();

        if ($bookings === []) {
            CLI::write("{$tenant->slug}: no bookings arriving {$targetDate}.", 'white');

            return;
        }

        $drafted = 0;
        $skipped = 0;

        foreach ($bookings as $booking) {
            if ($this->draftForBooking($booking)) {
                $drafted++;
            } else {
                $skipped++;
            }
        }

        CLI::write("{$tenant->slug}: {$drafted} drafted for {$targetDate} arrivals, {$skipped} skipped.", $drafted > 0 ? 'green' : 'white');
    }

    private function draftForBooking(object $booking): bool
    {
        // Idempotent against a re-run the same day (or a stray second
        // cron firing) — one draft per booking for this category.
        $existing = (new AiMessageDraftModel())
            ->where('category', 'pre_arrival_upsell')
            ->where('booking_id', $booking->id)
            ->countAllResults();

        if ($existing > 0) {
            return false;
        }

        $guest = (new GuestModel())->find($booking->guest_id);

        if ($guest === null || empty($guest->phone)) {
            return false;
        }

        $nights = max(1, (int) round((strtotime($booking->check_out_date) - strtotime($booking->check_in_date)) / 86400));

        $alreadySelected = array_map(
            static fn ($s) => $s->service_name,
            (new BookingServiceItemModel())->forBooking($booking->id),
        );

        $catalog = array_filter(
            array_map(
                static fn ($s) => ['name' => $s->name, 'price' => $s->price, 'unit' => $s->unit_label],
                (new HotelServiceModel())->active(),
            ),
            static fn ($s) => ! in_array($s['name'], $alreadySelected, true),
        );

        $otherRoomTypes = array_map(
            static fn ($rt) => ['name' => $rt->name, 'base_rate' => $rt->base_rate, 'capacity' => (int) $rt->capacity],
            (new RoomTypeModel())->where('is_active', 1)->where('id !=', $booking->room_type_id)->findAll(),
        );

        $restaurantVisits = (new FolioLineItemModel())
            ->select('folio_line_items.id')
            ->join('folios', 'folios.id = folio_line_items.folio_id')
            ->where('folios.guest_id', $guest->id)
            ->where('folio_line_items.category', 'restaurant')
            ->countAllResults();

        $eventCount = count((new EventModel())->historyForGuest($guest->id));

        $data = [
            'guest_name'          => $guest->full_name,
            'check_in'            => $booking->check_in_date,
            'check_out'           => $booking->check_out_date,
            'nights'              => $nights,
            'adults'              => (int) $booking->adults,
            'children'            => (int) $booking->children,
            'current_room_type'   => $booking->room_type_name,
            'available_services'  => array_values($catalog),
            'other_room_types'    => $otherRoomTypes,
            'guest_past_restaurant_visits' => $restaurantVisits,
            'guest_past_event_attendance'  => $eventCount,
        ];

        $tools = [[
            'name'        => 'check_room_availability',
            'description' => 'Checks whether a specific room type has any room genuinely free for these exact check-in/check-out dates, and its real nightly rate. Call this before ever suggesting a room upgrade.',
            'input_schema' => [
                'type'       => 'object',
                'properties' => ['room_type_name' => ['type' => 'string']],
                'required'   => ['room_type_name'],
            ],
        ]];

        $checkIn  = $booking->check_in_date;
        $checkOut = $booking->check_out_date;

        $executor = function (string $name, array $input) use ($checkIn, $checkOut) {
            if ($name !== 'check_room_availability') {
                return json_encode(['error' => 'Unknown tool.']);
            }

            $match = (new RoomTypeModel())->like('name', $input['room_type_name'] ?? '')->first();

            if ($match === null) {
                return json_encode(['available' => false, 'note' => 'No matching room type.']);
            }

            $rooms = (new RoomModel())->findAvailable($checkIn, $checkOut, $match->id);

            if ($rooms === []) {
                return json_encode(['available' => false, 'room_type' => $match->name]);
            }

            $rate = (new RatePlanModel())->resolveRate($match->id, $checkIn, $match->base_rate);

            return json_encode(['available' => true, 'room_type' => $match->name, 'rooms_free' => count($rooms), 'nightly_rate' => $rate]);
        };

        $systemPrompt = 'You draft a short (2-3 sentence), friendly pre-arrival WhatsApp/SMS message for a hotel '
            . "guest arriving in {$nights} night(s). Suggest AT MOST ONE OR TWO genuinely relevant add-ons, chosen "
            . 'only from: (a) the real available_services list given to you (use their real prices), or (b) a room '
            . 'upgrade — but ONLY after calling check_room_availability and it confirms a real, currently-free room '
            . '(never suggest an upgrade without checking first, and never suggest one the tool says is unavailable). '
            . 'Base relevance on the real context given (party size, past restaurant/event history) — e.g. don\'t '
            . 'suggest a baby cot with no children on the booking. Never invent a service, a price, or availability '
            . 'not given to you by the data or the tool. If nothing is genuinely relevant, write a short warm '
            . 'pre-arrival note with no upsell rather than forcing one.';

        $result = service('claudeService')->chat(
            'pre_arrival_upsell',
            $systemPrompt,
            [['role' => 'user', 'content' => json_encode($data)]],
            $tools,
            $executor,
            'booking',
            $booking->id,
            null,
        );

        if (! $result['success']) {
            CLI::error("  booking #{$booking->id}: {$result['error']}");

            return false;
        }

        $drafts  = new AiMessageDraftModel();
        $draftId = $drafts->insert([
            'category'      => 'pre_arrival_upsell',
            'guest_id'      => $guest->id,
            'booking_id'    => $booking->id,
            'to_phone'      => $guest->phone,
            'draft_text'    => trim((string) $result['text'], " \t\n\r\0\x0B\""),
            'status'        => 'pending',
            'data_snapshot' => json_encode($data),
        ]);

        if (service('settings')->get('ai.autosend_pre_arrival_upsell', '0') === '1') {
            \App\Controllers\GuestMessageDraftController::reallySend($drafts->find($draftId), null);
        }

        return true;
    }
}
