<?php

namespace App\Commands;

use App\Models\AiAlertModel;
use App\Models\BookingModel;
use App\Models\Control\TenantRecordModel;
use App\Models\FolioLineItemModel;
use App\Models\RoomModel;
use CodeIgniter\CLI\BaseCommand;
use CodeIgniter\CLI\CLI;

/**
 * Batch job (same per-tenant loop shape as SendPreArrivalReminders/
 * GenerateDailyBriefing/InferGuestPreferences) meant to run periodically
 * via cron:
 *   php spark ai:flag-anomalies
 *
 * Compares this week's real occupancy/revenue/no-show/cancellation/
 * room-type-booking figures against the prior week's real figures for
 * the same tenant, and asks Claude to identify which deviations (if
 * any) are genuinely worth a manager's attention — flags land in
 * ai_alerts (see AiAlertModel). Flags only: nothing here ever touches a
 * booking, rate, or anything else. Claude is given the real current-vs-
 * prior numbers and only ever narrates/prioritizes them; it never
 * computes a number itself.
 */
class FlagAnomalies extends BaseCommand
{
    protected $group       = 'AI';
    protected $name        = 'ai:flag-anomalies';
    protected $description = 'Compares this week\'s real KPIs against last week\'s and flags notable deviations for manager review.';

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

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

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

        if (service('settings')->get('ai.anomaly_flagging_enabled', '1') !== '1') {
            CLI::write("{$tenant->slug}: anomaly flagging is turned off, skipping.", 'white');

            return;
        }

        $data = $this->assembleComparison();

        $systemPrompt = 'You review a hotel\'s real week-over-week operating figures (this week vs the prior week) '
            . 'and identify genuinely notable deviations worth a manager\'s attention — e.g. a room type suddenly '
            . 'underbooked, a spike in no-shows, unusual cancellation clustering, an unexplained revenue or '
            . 'occupancy swing. Respond with ONLY a single JSON object (no markdown fences, no extra text): '
            . '{"anomalies": [{"title": "short title", "severity": "low|medium|high", "description": "1-2 sentence '
            . 'explanation referencing the real numbers"}]}. Only include a genuinely notable deviation — small, '
            . 'ordinary week-to-week noise is not an anomaly. If nothing is notable, return {"anomalies": []}. '
            . 'Never reference a number not present in the data given to you.';

        $result = service('claudeService')->chat(
            'anomaly_flagging',
            $systemPrompt,
            [['role' => 'user', 'content' => json_encode($data)]],
            [],
            null,
            'weekly_comparison',
            null,
            null,
        );

        if (! $result['success']) {
            CLI::error("{$tenant->slug}: {$result['error']}");

            return;
        }

        $text = trim((string) $result['text']);
        $text = preg_replace('/^```(?:json)?\s*|\s*```$/', '', $text);
        $parsed = json_decode($text, true);

        if (! is_array($parsed) || ! isset($parsed['anomalies']) || ! is_array($parsed['anomalies'])) {
            CLI::error("{$tenant->slug}: AI response was not in the expected format, no alerts recorded this run.");

            return;
        }

        $alerts = new AiAlertModel();
        $today  = date('Y-m-d');
        $count  = 0;

        foreach ($parsed['anomalies'] as $anomaly) {
            $severity = in_array($anomaly['severity'] ?? null, ['low', 'medium', 'high'], true) ? $anomaly['severity'] : 'low';

            $alerts->insert([
                'alert_date'    => $today,
                'title'         => substr((string) ($anomaly['title'] ?? 'Anomaly detected'), 0, 150),
                'description'   => (string) ($anomaly['description'] ?? ''),
                'severity'      => $severity,
                'data_snapshot' => json_encode($data),
            ]);

            $count++;
        }

        CLI::write("{$tenant->slug}: {$count} anomaly/anomalies flagged.", $count > 0 ? 'yellow' : 'green');
    }

    /**
     * This week (last 7 days including today) vs the prior 7-day week —
     * every figure computed directly from real bookings/folio_line_items
     * data, no placeholder numbers.
     */
    private function assembleComparison(): array
    {
        $thisWeekStart = date('Y-m-d', strtotime('-6 days'));
        $today         = date('Y-m-d');
        $lastWeekEnd   = date('Y-m-d', strtotime($thisWeekStart . ' -1 day'));
        $lastWeekStart = date('Y-m-d', strtotime($lastWeekEnd . ' -6 days'));

        $bookings   = new BookingModel();
        $totalRooms = (new RoomModel())->countAllResults();

        return [
            'this_week' => $this->weekFigures($bookings, $thisWeekStart, $today, $totalRooms),
            'last_week' => $this->weekFigures($bookings, $lastWeekStart, $lastWeekEnd, $totalRooms),
        ];
    }

    private function weekFigures(BookingModel $bookings, string $from, string $to, int $totalRooms): array
    {
        $lineItems = new FolioLineItemModel();

        $occupiedDays = 0;
        $days = 0;

        for ($d = $from; $d <= $to; $d = date('Y-m-d', strtotime($d . ' +1 day'))) {
            $occupiedDays += $bookings->occupiedRoomCountOn($d);
            $days++;
        }

        $revenue = $lineItems->totalBetween($from, $to);

        $noShows = $bookings->where('status', 'no_show')
            ->where('check_in_date >=', $from)->where('check_in_date <=', $to)
            ->countAllResults();

        $cancellations = $bookings->where('status', 'cancelled')
            ->where('updated_at >=', $from . ' 00:00:00')->where('updated_at <=', $to . ' 23:59:59')
            ->countAllResults();

        $byRoomType = $bookings->select('room_types.name as room_type_name, COUNT(*) as booking_count')
            ->join('rooms', 'rooms.id = bookings.room_id')
            ->join('room_types', 'room_types.id = rooms.room_type_id')
            ->where('bookings.check_in_date >=', $from)->where('bookings.check_in_date <=', $to)
            ->whereIn('bookings.status', ['pending_deposit', 'confirmed', 'checked_in', 'checked_out'])
            ->groupBy('room_types.id')
            ->findAll();

        return [
            'date_range'          => "{$from} to {$to}",
            'avg_occupancy_pct'   => $totalRooms > 0 && $days > 0 ? round($occupiedDays / $days / $totalRooms * 100, 1) : 0,
            'total_revenue'       => $revenue,
            'no_shows'            => $noShows,
            'cancellations'       => $cancellations,
            'bookings_by_room_type' => array_map(static fn ($r) => ['room_type' => $r->room_type_name, 'bookings' => (int) $r->booking_count], $byRoomType),
        ];
    }
}
