<?php

namespace App\Services\Ai;

use App\Models\CostCap;
use Illuminate\Support\Facades\DB;

/**
 * Pre-flight cap check. Called by Router::complete() BEFORE making the
 * actual provider call. If a 'block' cap is exceeded for the requested
 * purpose, returns a refusal so the caller surfaces "AI quota exceeded"
 * instead of incurring more cost.
 *
 * 'warn' caps don't block — they just log + flag in the dashboard so
 * staff can see they crossed a soft threshold.
 *
 * Lookup is cheap: a single SUM(cost_usd) per period × scope, computed
 * lazily and cached for 30s.
 */
class CapEnforcer
{
    /**
     * Returns ['allow' => bool, 'reason' => string|null,
     *          'matched_cap' => array|null, 'spent' => float, 'limit' => float]
     */
    public function check(string $purpose): array
    {
        $caps = CostCap::where('enabled', true)->get();
        if ($caps->isEmpty()) {
            return ['allow' => true, 'reason' => null];
        }

        foreach ($caps as $cap) {
            if ($cap->scope === CostCap::SCOPE_PURPOSE && $cap->purpose !== $purpose) {
                continue;
            }
            $spent = $this->spentForCap($cap, $purpose);
            if ($spent < (float) $cap->amount_usd) continue;

            // Cap hit.
            $reason = sprintf(
                '%s cap reached: $%.4f spent against $%.2f limit (%s, %s scope)',
                $cap->label, $spent, $cap->amount_usd, $cap->period,
                $cap->scope === CostCap::SCOPE_PURPOSE ? "purpose=$purpose" : 'fleet-wide',
            );
            if ($cap->action === CostCap::ACTION_BLOCK) {
                return [
                    'allow'  => false,
                    'reason' => $reason,
                    'matched_cap' => $cap->only(['id','label','scope','purpose','period','amount_usd','action']),
                    'spent'  => $spent,
                    'limit'  => (float) $cap->amount_usd,
                ];
            }
            // 'warn' — allow but flag.
            \Log::warning('hsap.ai.cap_warn', ['cap' => $cap->id, 'reason' => $reason]);
        }
        return ['allow' => true, 'reason' => null];
    }

    /**
     * Spend in USD for the cap's window/scope. Day caps reset at midnight,
     * month caps at the 1st. Computed against the live ai_costs table —
     * the rollup ai_daily_spend is for fast dashboard queries, not for
     * cap enforcement (we want second-resolution accuracy here).
     */
    private function spentForCap(CostCap $cap, string $requestedPurpose): float
    {
        $since = $cap->period === CostCap::PERIOD_MONTH
            ? now()->startOfMonth()
            : now()->startOfDay();

        $q = DB::table('ai_costs')->where('called_at', '>=', $since);

        if ($cap->scope === CostCap::SCOPE_PURPOSE) {
            // For purpose-scoped caps we sum only that purpose (the live
            // call's purpose will match by virtue of the earlier filter).
            $q->where('purpose', $cap->purpose);
        }
        // Cache micro: same query within 30s should reuse.
        $key = sprintf('hsap.cap.%d.%s', $cap->id, $since->timestamp);
        return (float) cache()->remember($key, 30, fn () => (float) $q->sum('cost_usd'));
    }
}
