<?php

namespace App\Commands;

use App\Models\Control\TenantRecordModel;
use App\Models\FolioLineItemModel;
use App\Models\GuestModel;
use App\Models\GuestPreferenceModel;
use App\Models\PosOrderItemModel;
use CodeIgniter\CLI\BaseCommand;
use CodeIgniter\CLI\CLI;

/**
 * Batch job (same per-tenant loop shape as SendPreArrivalReminders/
 * GenerateDailyBriefing) meant to run periodically via cron:
 *   php spark ai:infer-guest-preferences
 *
 * For each guest with real folio charges, real POS orders, or a staff
 * note on file, assembles that real history and asks Claude to
 * summarize likely preferences/patterns — staff-facing only (see
 * AddAiInferredSummaryToGuestPreferences's migration comment: never
 * shown to the guest, never used to justify a price). A guest with none
 * of that real data is skipped entirely — no API call, no fabricated
 * "no preferences on file" summary for someone with zero history.
 */
class InferGuestPreferences extends BaseCommand
{
    protected $group       = 'AI';
    protected $name        = 'ai:infer-guest-preferences';
    protected $description = 'Updates each guest\'s AI-inferred preference summary from their real folio/POS/notes history.';

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

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

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

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

            return;
        }

        $guests = (new GuestModel())->findAll();
        $updated = 0;
        $skipped = 0;

        foreach ($guests as $guest) {
            if ($this->inferForGuest($guest)) {
                $updated++;
            } else {
                $skipped++;
            }
        }

        CLI::write("{$tenant->slug}: updated {$updated} guest(s), skipped {$skipped} with no real history.", 'green');
    }

    private function inferForGuest(object $guest): bool
    {
        $charges = (new FolioLineItemModel())
            ->select('folio_line_items.category, folio_line_items.description, folio_line_items.created_at')
            ->join('folios', 'folios.id = folio_line_items.folio_id')
            ->where('folios.guest_id', $guest->id)
            ->findAll();

        $orderedItems = (new PosOrderItemModel())
            ->select('pos_order_items.item_name, pos_order_items.quantity, pos_order_items.created_at')
            ->join('pos_orders', 'pos_orders.id = pos_order_items.pos_order_id')
            ->where('pos_orders.guest_id', $guest->id)
            ->findAll();

        if ($charges === [] && $orderedItems === [] && empty($guest->notes)) {
            return false; // nothing real to infer from — no API call.
        }

        // Idempotency for repeated cron runs: skip (no API call) if this
        // guest already has a summary generated after their most recent
        // real activity — nothing new to re-summarize. A guest with no
        // prior summary, or with real activity newer than their last
        // summary, still gets (re-)processed.
        $prefs    = new GuestPreferenceModel();
        $existing = $prefs->forGuest($guest->id);

        if ($existing !== null && $existing->ai_inferred_at !== null) {
            $latestActivity = null;

            foreach (array_merge($charges, $orderedItems) as $row) {
                if ($latestActivity === null || $row->created_at > $latestActivity) {
                    $latestActivity = $row->created_at;
                }
            }

            if ($latestActivity === null || $existing->ai_inferred_at >= $latestActivity) {
                return false; // already summarized, nothing new since.
            }
        }

        $data = [
            'guest_notes'    => $guest->notes,
            'folio_charges'  => array_map(static fn ($c) => ['category' => $c->category, 'description' => $c->description], $charges),
            'ordered_items'  => array_map(static fn ($i) => ['item' => $i->item_name, 'quantity' => (int) $i->quantity], $orderedItems),
        ];

        $systemPrompt = 'You review one hotel guest\'s real folio charges, real F&B orders, and any staff notes on '
            . 'file, and write a short (1-3 sentence) staff-facing summary of likely preferences or patterns — e.g. '
            . '"orders vegetarian most visits", "has booked 2 large events", "prefers high floor per notes". Only '
            . 'state what the data actually supports — never guess a preference not evidenced by the data given. If '
            . 'the data is too sparse to say anything meaningful, say that plainly instead of inventing a pattern.';

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

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

            return false;
        }

        $row = ['ai_inferred_summary' => $result['text'], 'ai_inferred_at' => date('Y-m-d H:i:s')];

        if ($existing !== null) {
            $prefs->update($existing->id, $row);
        } else {
            $row['guest_id'] = $guest->id;
            $prefs->insert($row);
        }

        return true;
    }
}
