<?php
declare(strict_types=1);

require_once __DIR__ . '/bootstrap.php';
require_once __DIR__ . '/FreeTier.php';

/**
 * Tier-aware model routing for tools that expose the existing engine selector.
 *
 * Plus/trial users get the cost-controlled Azure mini path. Pro users get the
 * full Azure path. CaveauAI sessions keep the engine requested by their UI.
 */
final class ToolModels
{
    public const TIMELINE_QUICK_CHAR_LIMIT = 25000;
    public const TIMELINE_STANDARD_CHAR_LIMIT = 55000;
    public const TIMELINE_DEEP_CHAR_LIMIT = 128000;
    public const TIMELINE_QUICK_MAX_CHARS = 100000;
    public const TIMELINE_STANDARD_MAX_CHARS = 300000;
    public const TIMELINE_DEEP_MAX_CHARS = 600000;

    public static function engineForUser(int $userId, string $requestedEngine): string
    {
        $valid = ['nova_lite', 'azure_mini', 'azure_full', 'gpu', 'regex', 'claude_haiku', 'claude_sonnet'];
        $requestedEngine = in_array($requestedEngine, $valid, true) ? $requestedEngine : 'azure_mini';

        if ($userId <= 0) {
            return $requestedEngine;
        }

        return match (FreeTier::tier($userId)) {
            'pro'   => $requestedEngine,
            'plus'  => $requestedEngine === 'azure_full' ? 'azure_mini' : $requestedEngine,
            default => in_array($requestedEngine, ['nova_lite', 'regex'], true) ? $requestedEngine : 'nova_lite',
        };
    }

    public static function timelineRoute(int $userId, string $requestedEngine, string $text): array
    {
        $valid = ['nova_lite', 'azure_mini', 'azure_full', 'claude_haiku', 'claude_sonnet'];
        $requestedEngine = in_array($requestedEngine, $valid, true) ? $requestedEngine : 'azure_mini';
        $tierEngine = self::engineForUser($userId, $requestedEngine);
        $charCount = mb_strlen($text, 'UTF-8');

        if ($charCount > self::TIMELINE_DEEP_MAX_CHARS) {
            throw new DbnToolsHttpException(
                'This timeline input is too large after selected documents or My Case context were added. Split the file or use fewer selected documents.',
                413,
                'timeline_input_too_large',
                ['input_char_count' => $charCount, 'max_chars' => self::TIMELINE_DEEP_MAX_CHARS]
            );
        }

        $effectiveEngine = $tierEngine;
        if ($charCount > self::timelineEngineMaxChars($effectiveEngine)) {
            $effectiveEngine = $charCount <= self::TIMELINE_STANDARD_MAX_CHARS ? 'azure_mini' : 'azure_full';
        } elseif ($charCount > self::TIMELINE_STANDARD_CHAR_LIMIT && $effectiveEngine === 'nova_lite') {
            $effectiveEngine = $charCount <= self::TIMELINE_QUICK_MAX_CHARS ? 'nova_lite' : 'azure_mini';
        }

        if ($charCount > self::timelineEngineMaxChars($effectiveEngine)) {
            $effectiveEngine = 'azure_full';
        }

        $credits = self::timelineCreditsForSize($effectiveEngine, $charCount);
        $baseCredits = self::timelineAdvertisedCredits($requestedEngine);
        $requiresConfirmation = $credits > $baseCredits
            || self::timelineEngineRank($effectiveEngine) > self::timelineEngineRank($requestedEngine);
        $chunked = $charCount > self::timelineEngineLimit($effectiveEngine);

        return [
            'requested_engine' => $requestedEngine,
            'tier_engine' => $tierEngine,
            'effective_engine' => $effectiveEngine,
            'auto_upgraded_engine' => $effectiveEngine !== $tierEngine,
            'input_char_count' => $charCount,
            'engine_limit_chars' => self::timelineEngineLimit($effectiveEngine),
            'max_char_limit' => self::timelineEngineMaxChars($effectiveEngine),
            'chunked_timeline' => $chunked,
            'timeline_chunk_count' => $chunked ? (int)ceil($charCount / self::timelineChunkSize($effectiveEngine)) : 1,
            'estimated_credits' => $credits,
            'credits' => $credits,
            'base_credits' => $baseCredits,
            'requires_confirmation' => $requiresConfirmation,
        ];
    }

    public static function assertTimelineQuoteAccepted(array $route, array $input): void
    {
        if (empty($route['requires_confirmation'])) {
            return;
        }

        $accepted = !empty($input['accepted_timeline_quote'])
            && (int)($input['accepted_credits'] ?? 0) === (int)$route['credits']
            && (string)($input['accepted_effective_engine'] ?? '') === (string)$route['effective_engine'];

        if ($accepted) {
            return;
        }

        $engineLabel = self::timelineEngineLabel((string)$route['effective_engine']);
        throw new DbnToolsHttpException(
            'This timeline is larger than the selected engine can handle at the advertised price. Confirm the quoted engine and credits before running.',
            409,
            'timeline_quote_required',
            ['timeline_quote' => array_merge($route, [
                'effective_engine_label' => $engineLabel,
                'message' => 'Timeline will use ' . $engineLabel . ' for '
                    . number_format((int)$route['input_char_count'])
                    . ' characters across about ' . (int)$route['timeline_chunk_count']
                    . ' chunk(s), costing ' . (int)$route['credits'] . ' credit(s).',
            ])]
        );
    }

    public static function timelineCredits(string $engine): int
    {
        return self::timelineAdvertisedCredits($engine);
    }

    public static function timelineEngineLimit(string $engine): int
    {
        return match ($engine) {
            'nova_lite', 'claude_haiku' => self::TIMELINE_QUICK_CHAR_LIMIT,
            'azure_mini'                => self::TIMELINE_STANDARD_CHAR_LIMIT,
            default                     => self::TIMELINE_DEEP_CHAR_LIMIT,
        };
    }

    public static function timelineChunkSize(string $engine): int
    {
        return match ($engine) {
            'nova_lite', 'claude_haiku' => 10000,
            'azure_mini'                => 16000,
            default                     => 30000,
        };
    }

    public static function timelineEngineMaxChars(string $engine): int
    {
        return match ($engine) {
            'nova_lite', 'claude_haiku' => self::TIMELINE_QUICK_MAX_CHARS,
            'azure_mini'                => self::TIMELINE_STANDARD_MAX_CHARS,
            default                     => self::TIMELINE_DEEP_MAX_CHARS,
        };
    }

    public static function timelineCreditsForSize(string $engine, int $charCount): int
    {
        return match ($engine) {
            'nova_lite', 'claude_haiku'
                => $charCount <= self::TIMELINE_QUICK_CHAR_LIMIT ? 1 : 2,
            'azure_mini'
                => $charCount <= self::TIMELINE_STANDARD_CHAR_LIMIT ? 1 : ($charCount <= 180000 ? 2 : 3),
            default
                => $charCount <= self::TIMELINE_DEEP_CHAR_LIMIT ? 2 : ($charCount <= 350000 ? 4 : 6),
        };
    }

    public static function timelineAdvertisedCredits(string $engine): int
    {
        return in_array($engine, ['azure_full', 'claude_sonnet'], true) ? 2 : 1;
    }

    public static function timelineEngineLabel(string $engine): string
    {
        return match ($engine) {
            'nova_lite', 'claude_haiku'   => 'Quick',
            'azure_full', 'claude_sonnet' => 'Deep',
            default                       => 'Standard',
        };
    }

    private static function timelineEngineRank(string $engine): int
    {
        return match ($engine) {
            'nova_lite', 'claude_haiku'   => 1,
            'azure_mini'                  => 2,
            'azure_full', 'claude_sonnet' => 3,
            default                       => 0,
        };
    }
}
