<?php

namespace App\Controllers;

use App\Models\BookingModel;
use App\Models\FolioLineItemModel;
use App\Models\GuestModel;
use App\Models\RoomModel;

/**
 * Reports & Analytics — was a disabled "Soon" nav item, built for real
 * from a user-supplied mockup. Every KPI is a standard hotel metric
 * (Occupancy, ADR, RevPAR) computed from real bookings/folio data, not
 * placeholder numbers — see the private helpers below for exactly how
 * each is derived. "Top Room Types by Revenue" is booked revenue
 * (rate_applied × nights from real bookings in range), labeled as such
 * rather than implying it's collected/paid revenue, since folio charges
 * don't carry a room-type reference to compute that directly.
 */
class ReportController extends BaseController
{
    public function index()
    {
        $report = $this->buildReport();

        return view('reports/index', array_merge($report, [
            'pageTitle'    => 'Reports',
            'pageSubtitle' => 'Analyze your hotel performance and operations.',
        ]));
    }

    /**
     * Same figures as index(), same date range — just written out as a
     * CSV instead of rendered as charts. No new computation, so it can
     * never disagree with what's on screen.
     */
    public function exportCsv()
    {
        $report = $this->buildReport();

        $csv = fopen('php://temp', 'w+');

        fputcsv($csv, ['MaxYields Report', $report['dateFrom'] . ' to ' . $report['dateTo']]);
        fputcsv($csv, []);
        fputcsv($csv, ['KPI', 'Value']);
        fputcsv($csv, ['Average Occupancy', $report['kpis']['occupancy'] . '%']);
        fputcsv($csv, ['ADR', format_money($report['kpis']['adr'])]);
        fputcsv($csv, ['RevPAR', format_money($report['kpis']['revpar'])]);
        fputcsv($csv, ['Total Revenue (charges posted)', format_money($report['kpis']['revenue'])]);
        fputcsv($csv, ['Repeat Guest Rate (all-time)', $report['kpis']['repeatGuestRate'] . '%']);
        fputcsv($csv, ['Average Spend per Guest (all-time)', format_money($report['kpis']['avgSpendPerGuest'])]);
        fputcsv($csv, []);

        fputcsv($csv, ['Date', 'Occupancy %', 'Revenue']);
        foreach ($report['occupancyTrend'] as $day => $pct) {
            fputcsv($csv, [$day, $pct, format_money($report['revenueTrend'][$day] ?? '0.00')]);
        }
        fputcsv($csv, []);

        fputcsv($csv, ['Revenue by Category', 'Amount']);
        foreach ($report['categoryTotals'] as $category => $amount) {
            fputcsv($csv, [ucfirst(str_replace('_', ' ', $category)), format_money($amount)]);
        }
        fputcsv($csv, []);

        fputcsv($csv, ['Room Type', 'Booked Revenue']);
        foreach ($report['roomTypeRevenue'] as $type => $amount) {
            fputcsv($csv, [$type, format_money($amount)]);
        }

        rewind($csv);
        $contents = stream_get_contents($csv);
        fclose($csv);

        $this->logActivity('reports', "Exported report CSV for {$report['dateFrom']} to {$report['dateTo']}.");

        return $this->response
            ->setHeader('Content-Type', 'text/csv')
            ->setHeader('Content-Disposition', 'attachment; filename="report-' . $report['dateFrom'] . '-to-' . $report['dateTo'] . '.csv"')
            ->setBody($contents);
    }

    /**
     * Same figures again, this time through the same pdfService/dompdf
     * pairing already used for folio invoices and corporate statements —
     * no new PDF machinery introduced, just another view rendered through it.
     */
    public function exportPdf()
    {
        $report = $this->buildReport();
        $tenant = service('tenantContext')->getTenant();

        $html = view('reports/pdf', array_merge($report, [
            'tenant'   => $tenant,
            'issuedAt' => date('Y-m-d H:i'),
        ]));

        $pdf = service('pdfService')->renderHtml($html, 'landscape');

        $this->logActivity('reports', "Exported report PDF for {$report['dateFrom']} to {$report['dateTo']}.");

        return $this->response
            ->setHeader('Content-Type', 'application/pdf')
            ->setHeader('Content-Disposition', 'inline; filename="report-' . $report['dateFrom'] . '-to-' . $report['dateTo'] . '.pdf"')
            ->setBody($pdf);
    }

    /**
     * "Summarize this" on the Reports page. Hands Claude the exact same
     * $report['kpis'] array already rendered on screen, plus the prior
     * comparable period's kpis (same span, immediately before dateFrom)
     * for a real vs-previous-period comparison — never recomputed or
     * estimated by Claude itself, only narrated. No tools: everything
     * Claude needs is already in the prompt, so there's nothing to look
     * up. See ClaudeService's class doc comment for the shared call
     * path/logging every AI feature goes through.
     */
    public function narrateKpis()
    {
        if (service('settings')->get('ai.kpi_narration_enabled', '1') !== '1') {
            return $this->response->setJSON(['success' => false, 'error' => 'KPI narration is turned off — enable it under Settings > AI Features.', 'csrf_hash' => csrf_hash()]);
        }

        $current = $this->buildReport();

        $spanDays  = max(1, (int) round((strtotime($current['dateTo']) - strtotime($current['dateFrom'])) / 86400) + 1);
        $priorTo   = date('Y-m-d', strtotime($current['dateFrom'] . ' -1 day'));
        $priorFrom = date('Y-m-d', strtotime($priorTo . ' -' . ($spanDays - 1) . ' days'));
        $prior     = $this->buildReport($priorFrom, $priorTo);

        $systemPrompt = 'You are a hotel performance analyst writing a short internal note for a hotel manager. '
            . 'You will be given this period\'s real, already-computed KPI figures and the prior comparable period\'s '
            . 'real figures for the same hotel. Write 2-4 sentences of plain-language narration highlighting the '
            . 'notable changes. Only ever reference the numbers given to you — never invent, estimate, or round a '
            . 'figure that was not provided. If a KPI is 0 or a comparison is not meaningful (e.g. the prior period '
            . 'has no data), say so plainly rather than fabricating a trend.';

        $userMessage = sprintf(
            "Current period (%s to %s):\n%s\n\nPrior comparable period (%s to %s):\n%s",
            $current['dateFrom'],
            $current['dateTo'],
            json_encode($current['kpis']),
            $prior['dateFrom'],
            $prior['dateTo'],
            json_encode($prior['kpis']),
        );

        $result = service('claudeService')->chat(
            'kpi_narration',
            $systemPrompt,
            [['role' => 'user', 'content' => $userMessage]],
            [],
            null,
            'kpi_report',
            null,
            $this->currentUserId(),
        );

        if (! $result['success']) {
            return $this->response->setJSON(['success' => false, 'error' => $result['error'], 'csrf_hash' => csrf_hash()]);
        }

        $this->logActivity('reports', "Generated an AI narration for the {$current['dateFrom']} to {$current['dateTo']} KPI report.");

        return $this->response->setJSON(['success' => true, 'text' => $result['text'], 'csrf_hash' => csrf_hash()]);
    }

    /**
     * Shared by index()/exportCsv()/exportPdf() so the three never drift
     * apart — same date-range resolution, same KPI math, computed once.
     * Optional explicit $dateFrom/$dateTo (added for narrateKpis() below,
     * to compute the prior comparable period's real figures) default to
     * the same GET-param resolution every other caller already relies on.
     */
    private function buildReport(?string $dateFrom = null, ?string $dateTo = null): array
    {
        $dateFrom = $dateFrom ?? ($this->request->getGet('date_from') ?: date('Y-m-d', strtotime('-6 days')));
        $dateTo   = $dateTo ?? ($this->request->getGet('date_to') ?: date('Y-m-d'));

        $totalRooms = (new RoomModel())->countAllResults();
        $lineItems  = new FolioLineItemModel();

        $days = [];
        for ($d = $dateFrom; $d <= $dateTo; $d = date('Y-m-d', strtotime($d . ' +1 day'))) {
            $days[] = $d;
        }

        $occupancyTrend = [];
        $roomNightsSold = 0;

        foreach ($days as $day) {
            $occupied = $day === date('Y-m-d')
                ? (new RoomModel())->where('status', 'occupied')->countAllResults()
                : (new BookingModel())->occupiedRoomCountOn($day);

            $roomNightsSold += $occupied;
            $occupancyTrend[$day] = $totalRooms > 0 ? round($occupied / $totalRooms * 100, 1) : 0;
        }

        $revenueTrend = [];
        $totalRevenue = '0.00';

        foreach ($days as $day) {
            $dayTotal = $lineItems->totalOn($day);
            $revenueTrend[$day] = $dayTotal;
            $totalRevenue = money_add($totalRevenue, $dayTotal);
        }

        $categoryTotals = $lineItems->byCategoryBetween($dateFrom, $dateTo);
        $roomRevenue    = $categoryTotals['room_charge'] ?? '0.00';

        $adr    = $roomNightsSold > 0 ? money_divide($roomRevenue, (string) $roomNightsSold) : '0.00';
        $revpar = $totalRooms > 0 && count($days) > 0 ? money_divide($roomRevenue, (string) ($totalRooms * count($days))) : '0.00';
        $avgOccupancy = count($occupancyTrend) > 0 ? round(array_sum($occupancyTrend) / count($occupancyTrend), 1) : 0;

        $guests = new GuestModel();
        $spendTotals = $guests->lifetimeSpendTotals();
        $avgSpendPerGuest = $spendTotals['guestCount'] > 0
            ? money_divide($spendTotals['total'], (string) $spendTotals['guestCount'])
            : '0.00';

        return [
            'dateFrom' => $dateFrom,
            'dateTo'   => $dateTo,
            'kpis'     => [
                'occupancy'        => $avgOccupancy,
                'adr'              => $adr,
                'revpar'           => $revpar,
                'revenue'          => $totalRevenue,
                'repeatGuestRate'  => $guests->repeatGuestRate(),
                'avgSpendPerGuest' => $avgSpendPerGuest,
            ],
            'occupancyTrend'  => $occupancyTrend,
            'revenueTrend'    => $revenueTrend,
            'categoryTotals'  => $categoryTotals,
            'roomTypeRevenue' => $this->roomTypeRevenue($dateFrom, $dateTo),
        ];
    }

    /**
     * Booked revenue (rate × nights) per room type for bookings whose
     * stay overlaps the selected range — labeled "booked", not
     * "collected", in the view. Real bookings data, no placeholder.
     */
    private function roomTypeRevenue(string $dateFrom, string $dateTo): array
    {
        $bookings = (new BookingModel())->withDetails()
            ->whereIn('bookings.status', ['confirmed', 'checked_in', 'checked_out'])
            ->where('bookings.check_in_date <', $dateTo)
            ->where('bookings.check_out_date >', $dateFrom)
            ->findAll();

        $byType = [];

        foreach ($bookings as $b) {
            $nights = max(1, (strtotime($b->check_out_date) - strtotime($b->check_in_date)) / 86400);
            $amount = money_multiply($b->rate_applied, (int) $nights);
            $byType[$b->room_type_name] = money_add($byType[$b->room_type_name] ?? '0.00', $amount);
        }

        arsort($byType);

        return $byType;
    }
}
