<?php
declare(strict_types=1);

/**
 * Authoritative pricing, entitlement, and credit catalog for DBN Tools.
 *
 * Current database columns are intentionally preserved:
 * - user_tool_credits.balance = monthly credits
 * - user_tool_credits.bonus_balance = prepaid/top-up credits
 */
final class PricingCatalog
{
    public const VERSION = 'dbn-tools-nok-2026-05-v1';

    /** @return array<string,array<string,mixed>> */
    public static function plans(): array
    {
        return [
            'free' => [
                'sku' => 'free',
                'tier' => 'free',
                'name' => 'Gratis',
                'price_nok' => 0,
                'unit_amount' => 0,
                'currency' => 'nok',
                'monthly_credits' => 30,
                'effective_credit_cost' => null,
                'storage_bytes' => 0,
                'seats' => 1,
                'hourly_cap' => 10,
                'trial_days' => 0,
                'stripe_price_key' => null,
                'features' => [
                    'Pasted-text tools',
                    'Norwegian legal corpus search',
                    'No My Case storage',
                ],
            ],
            'plus' => [
                'sku' => 'plus',
                'tier' => 'plus',
                'name' => 'Pluss',
                'price_nok' => 149,
                'unit_amount' => 14900,
                'currency' => 'nok',
                'monthly_credits' => 250,
                'effective_credit_cost' => 0.60,
                'storage_bytes' => 524288000,
                'seats' => 1,
                'hourly_cap' => 20,
                'trial_days' => 14,
                'stripe_price_key' => 'STRIPE_PRICE_PLUS_NOK',
                'features' => [
                    '500 MB My Case storage',
                    'Use private case context in tools',
                    'Saved analyses',
                    '14-day trial',
                ],
            ],
            'pro' => [
                'sku' => 'pro',
                'tier' => 'pro',
                'name' => 'Pro Familie',
                'price_nok' => 399,
                'unit_amount' => 39900,
                'currency' => 'nok',
                'monthly_credits' => 900,
                'effective_credit_cost' => 0.44,
                'storage_bytes' => 5368709120,
                'seats' => 3,
                'hourly_cap' => 40,
                'trial_days' => 0,
                'stripe_price_key' => 'STRIPE_PRICE_PRO_NOK',
                'features' => [
                    '5 GB shared My Case storage',
                    '3 users on one case',
                    'Full Azure model route',
                    'Priority for complex analysis',
                ],
            ],
        ];
    }

    /** @return array<string,array<string,mixed>> */
    public static function topups(): array
    {
        return [
            'topup_100' => [
                'sku' => 'topup_100',
                'name' => 'Ekstra 100',
                'price_nok' => 129,
                'unit_amount' => 12900,
                'currency' => 'nok',
                'credits' => 100,
                'cost_per_credit' => 1.29,
                'stripe_price_key' => 'STRIPE_PRICE_TOPUP_100_NOK',
                'aliases' => ['topup_s'],
            ],
            'topup_300' => [
                'sku' => 'topup_300',
                'name' => 'Saks-pakke 300',
                'price_nok' => 299,
                'unit_amount' => 29900,
                'currency' => 'nok',
                'credits' => 300,
                'cost_per_credit' => 1.00,
                'stripe_price_key' => 'STRIPE_PRICE_TOPUP_300_NOK',
                'aliases' => ['topup_m'],
            ],
            'topup_1000' => [
                'sku' => 'topup_1000',
                'name' => 'Stor pakke 1000',
                'price_nok' => 849,
                'unit_amount' => 84900,
                'currency' => 'nok',
                'credits' => 1000,
                'cost_per_credit' => 0.85,
                'stripe_price_key' => 'STRIPE_PRICE_TOPUP_1000_NOK',
                'aliases' => ['topup_l'],
            ],
        ];
    }

    /** @return array<string,int> */
    public static function toolCosts(): array
    {
        return [
            'search' => 0,
            'corpus-search' => 0,
            'ask' => 1,
            'extract' => 1,
            'summarize' => 1,
            'translate' => 1,
            'korrespond_refine' => 1,
            'timeline' => 2,
            'redact' => 2,
            'barnevernet' => 3,
            'advocate' => 3,
            'korrespond' => 3,
            'legal-analysis' => 3,
            'discrepancy' => 4,
            'deep-research' => 6,
            'transcribe' => 5,
        ];
    }

    /** @return array<string,mixed> */
    public static function stt(): array
    {
        return [
            'minimum_credits' => 5,
            'credits_per_started_minute' => 1,
            'reservation_bytes_per_credit' => 300000,
        ];
    }

    /** @return array<string,mixed>|null */
    public static function plan(string $sku): ?array
    {
        $plans = self::plans();
        return $plans[$sku] ?? null;
    }

    /** @return array<string,mixed>|null */
    public static function topup(string $sku): ?array
    {
        $canonical = self::canonicalSku($sku);
        $topups = self::topups();
        return $topups[$canonical] ?? null;
    }

    public static function canonicalSku(string $sku): string
    {
        $sku = trim($sku);
        if ($sku === 'light') {
            return 'plus';
        }
        if ($sku === 'pro-plus') {
            return 'pro';
        }
        foreach (self::topups() as $canonical => $topup) {
            if ($sku === $canonical || in_array($sku, $topup['aliases'] ?? [], true)) {
                return $canonical;
            }
        }
        return $sku;
    }

    public static function isSubscriptionSku(string $sku): bool
    {
        $sku = self::canonicalSku($sku);
        return in_array($sku, ['plus', 'pro'], true);
    }

    public static function isTopupSku(string $sku): bool
    {
        return self::topup($sku) !== null;
    }

    /** @return list<string> */
    public static function subscriptionSkus(): array
    {
        return ['plus', 'pro'];
    }

    /** @return list<string> */
    public static function topupSkus(): array
    {
        return array_keys(self::topups());
    }

    public static function stripePriceKey(string $sku): ?string
    {
        $sku = self::canonicalSku($sku);
        if (self::isSubscriptionSku($sku)) {
            return self::plans()[$sku]['stripe_price_key'] ?? null;
        }
        $topup = self::topup($sku);
        return $topup['stripe_price_key'] ?? null;
    }

    public static function monthlyAllowance(string $tier): int
    {
        return (int)(self::plans()[$tier]['monthly_credits'] ?? self::plans()['free']['monthly_credits']);
    }

    public static function hourlyCap(string $tier): int
    {
        return (int)(self::plans()[$tier]['hourly_cap'] ?? self::plans()['free']['hourly_cap']);
    }

    public static function storageQuota(string $tier): int
    {
        return (int)(self::plans()[$tier]['storage_bytes'] ?? 0);
    }

    public static function isPaidTier(string $tier): bool
    {
        return in_array($tier, ['plus', 'pro'], true);
    }

    public static function toolCost(string $tool): int
    {
        return self::toolCosts()[$tool] ?? 1;
    }

    public static function topupCredits(string $sku): int
    {
        $topup = self::topup($sku);
        return $topup ? (int)$topup['credits'] : 0;
    }

    public static function planTrialDays(string $sku): int
    {
        $sku = self::canonicalSku($sku);
        return (int)(self::plans()[$sku]['trial_days'] ?? 0);
    }

    public static function transcribeCreditsForSeconds(float $seconds): int
    {
        $stt = self::stt();
        $perMinute = max(1, (int)$stt['credits_per_started_minute']);
        $minimum = max(1, (int)$stt['minimum_credits']);
        $minutes = max(1, (int)ceil(max(0.0, $seconds) / 60));
        return max($minimum, $minutes * $perMinute);
    }

    public static function estimateTranscribeCreditsFromBytes(int $bytes): int
    {
        $stt = self::stt();
        $bytesPerCredit = max(1, (int)$stt['reservation_bytes_per_credit']);
        return max((int)$stt['minimum_credits'], (int)ceil(max(1, $bytes) / $bytesPerCredit));
    }

    public static function estimateTranscribeCreditsFromFile(string $filename, int $bytes): int
    {
        $stt = self::stt();
        $minimum = (int)$stt['minimum_credits'];
        $ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
        $bytesPerCredit = match ($ext) {
            'wav', 'flac' => 2000000,
            'mp3', 'm4a', 'mp4', 'aac', 'ogg', 'oga', 'webm' => (int)$stt['reservation_bytes_per_credit'],
            default => 960000,
        };
        return max($minimum, (int)ceil(max(1, $bytes) / max(1, $bytesPerCredit)));
    }

    public static function formatNok(int $nok): string
    {
        return 'NOK ' . number_format($nok, 0, ',', ' ');
    }

    public static function formatCredits(int $credits): string
    {
        return number_format($credits, 0, ',', ' ');
    }
}
