<?php
declare(strict_types=1);

/**
 * Credit system for free-tier (Google-authenticated) users of tools.dobetternorge.no.
 *
 * Credits are stored in dobetternorge_maindb.user_tool_credits.
 * Usage is logged in user_tool_usage_log.
 *
 * CaveauAI client sessions (dbn_tools_user_id + client_id) bypass all checks.
 * Only SSO sessions with tier='free' are subject to limits.
 */
final class FreeTier
{
    private const HOURLY_LIMIT = 10;

    private const COSTS = [
        'corpus-search' => 0,
        'search'        => 0,
        'ask'           => 1,
        'extract'       => 1,
        'timeline'      => 2,
        'redact'        => 2,
        'barnevernet'   => 3,
        'advocate'      => 3,
        'deep-research' => 5,
        'transcribe'    => 2,  // flat rate; actual duration unknown upfront
        'discrepancy'   => 4,  // 2 docs × 4 extraction steps + cross-ref + synthesis
    ];

    /** Credit cost for a given tool slug. Returns 1 for unknown tools. */
    public static function cost(string $tool): int
    {
        return self::COSTS[$tool] ?? 1;
    }

    /**
     * Check whether the user may proceed with a tool call.
     * Handles monthly reset automatically.
     *
     * Returns ['ok' => true, 'balance' => int]
     *     or  ['ok' => false, 'balance' => int, 'reason' => 'no_credits'|'rate_limit']
     */
    public static function check(int $userId, string $tool): array
    {
        $db = dbnmDb();
        $cost = self::cost($tool);

        // Auto-reset balance if a new month has begun since last reset
        $db->prepare(
            'UPDATE user_tool_credits
             SET balance = allowance, last_reset = CURDATE()
             WHERE user_id = ?
               AND (YEAR(last_reset) < YEAR(CURDATE()) OR MONTH(last_reset) < MONTH(CURDATE()))'
        )->execute([$userId]);

        $row = $db->prepare(
            'SELECT balance FROM user_tool_credits WHERE user_id = ? LIMIT 1'
        );
        $row->execute([$userId]);
        $credits = $row->fetch(PDO::FETCH_ASSOC);

        if ($credits === false) {
            // No credits row — treat as 0 balance (shouldn't happen after ensureFreeTierCredits)
            return ['ok' => false, 'balance' => 0, 'reason' => 'no_credits'];
        }

        $balance = (int)$credits['balance'];

        // Free tools always pass
        if ($cost === 0) {
            return ['ok' => true, 'balance' => $balance];
        }

        if ($balance < $cost) {
            return ['ok' => false, 'balance' => $balance, 'reason' => 'no_credits'];
        }

        // Hourly rate limit check (counts any tool that costs > 0)
        $hourlyCount = $db->prepare(
            'SELECT COUNT(*) FROM user_tool_usage_log
             WHERE user_id = ? AND created_at > DATE_SUB(NOW(), INTERVAL 1 HOUR) AND credits_used > 0'
        );
        $hourlyCount->execute([$userId]);
        if ((int)$hourlyCount->fetchColumn() >= self::HOURLY_LIMIT) {
            return ['ok' => false, 'balance' => $balance, 'reason' => 'rate_limit'];
        }

        return ['ok' => true, 'balance' => $balance];
    }

    /**
     * Deduct credits for a completed tool call and log the usage.
     * Safe to call even when cost is 0 (logs the call but deducts nothing).
     */
    public static function deduct(int $userId, string $tool): int
    {
        $db   = dbnmDb();
        $cost = self::cost($tool);

        if ($cost > 0) {
            $db->prepare(
                'UPDATE user_tool_credits
                 SET balance = GREATEST(0, balance - ?)
                 WHERE user_id = ?'
            )->execute([$cost, $userId]);
        }

        $db->prepare(
            'INSERT INTO user_tool_usage_log (user_id, tool, credits_used) VALUES (?, ?, ?)'
        )->execute([$userId, $tool, $cost]);

        $row = $db->prepare('SELECT balance FROM user_tool_credits WHERE user_id = ? LIMIT 1');
        $row->execute([$userId]);
        $r = $row->fetch(PDO::FETCH_ASSOC);
        return $r ? (int)$r['balance'] : 0;
    }

    /**
     * Current balance for a user (after any pending monthly reset).
     */
    public static function balance(int $userId): int
    {
        $result = self::check($userId, 'corpus-search'); // cost=0, triggers reset if needed
        return $result['balance'];
    }
}
