<?php
declare(strict_types=1);

require_once __DIR__ . '/FreeTier.php';

final class UserMcpTokens
{
    public const TOKEN_PREFIX = 'dbn_user_mcp_';

    public static function ensureSchema(): void
    {
        $db = dbnmDb();
        $db->exec(
            "CREATE TABLE IF NOT EXISTS user_mcp_tokens (
                id INT UNSIGNED NOT NULL AUTO_INCREMENT,
                user_id INT NOT NULL,
                token_hash CHAR(64) NOT NULL,
                token_prefix VARCHAR(32) NOT NULL,
                name VARCHAR(100) NOT NULL DEFAULT 'Default',
                scopes JSON NULL,
                is_active TINYINT(1) NOT NULL DEFAULT 1,
                last_used_at DATETIME NULL,
                created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
                revoked_at DATETIME NULL,
                PRIMARY KEY (id),
                KEY idx_hash (token_hash),
                KEY idx_user_active (user_id, is_active, revoked_at),
                CONSTRAINT fk_user_mcp_tokens_user
                    FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
        );
    }

    public static function isUserEligible(int $userId): bool
    {
        return $userId > 0 && FreeTier::isPaidTier(FreeTier::tier($userId));
    }

    public static function listForUser(int $userId): array
    {
        self::ensureSchema();
        $db = dbnmDb();
        $stmt = $db->prepare(
            'SELECT id, token_prefix, name, scopes, is_active, last_used_at, created_at, revoked_at
               FROM user_mcp_tokens
              WHERE user_id = ?
              ORDER BY is_active DESC, created_at DESC'
        );
        $stmt->execute([$userId]);
        $rows = $stmt->fetchAll(PDO::FETCH_ASSOC) ?: [];
        foreach ($rows as &$row) {
            $row['id'] = (int)$row['id'];
            $row['is_active'] = (int)$row['is_active'] === 1;
            $row['scopes'] = json_decode((string)($row['scopes'] ?? '[]'), true) ?: [];
        }
        unset($row);
        return $rows;
    }

    public static function createForUser(int $userId, string $name = 'Default'): array
    {
        if (!self::isUserEligible($userId)) {
            throw new DbnToolsHttpException('MCP tokens require a Plus or Pro plan.', 403, 'not_paid');
        }
        self::ensureSchema();

        $plain = self::TOKEN_PREFIX . bin2hex(random_bytes(32));
        $hash = hash('sha256', $plain);
        $prefix = substr($plain, 0, 24);
        $name = mb_substr(trim($name) !== '' ? trim($name) : 'Default', 0, 100, 'UTF-8');
        $scopes = [
            'tools' => ['dbn.*'],
            'privacy' => 'process-and-forget-default',
            'tiers' => ['plus', 'pro'],
        ];

        $db = dbnmDb();
        $stmt = $db->prepare(
            'INSERT INTO user_mcp_tokens (user_id, token_hash, token_prefix, name, scopes, is_active, created_at)
             VALUES (?, ?, ?, ?, ?, 1, NOW())'
        );
        $stmt->execute([
            $userId,
            $hash,
            $prefix,
            $name,
            json_encode($scopes, JSON_UNESCAPED_SLASHES),
        ]);

        return [
            'id' => (int)$db->lastInsertId(),
            'token' => $plain,
            'token_prefix' => $prefix,
            'name' => $name,
            'scopes' => $scopes,
        ];
    }

    public static function revokeForUser(int $userId, int $tokenId): bool
    {
        self::ensureSchema();
        $db = dbnmDb();
        $stmt = $db->prepare(
            'UPDATE user_mcp_tokens
                SET is_active = 0, revoked_at = NOW()
              WHERE id = ? AND user_id = ? AND is_active = 1 AND revoked_at IS NULL'
        );
        $stmt->execute([$tokenId, $userId]);
        return $stmt->rowCount() > 0;
    }

    public static function resolve(string $token): ?array
    {
        $token = trim($token);
        if ($token === '' || !str_starts_with($token, self::TOKEN_PREFIX)) {
            return null;
        }
        self::ensureSchema();
        $hash = hash('sha256', $token);
        $db = dbnmDb();
        $stmt = $db->prepare(
            'SELECT t.*, u.email
               FROM user_mcp_tokens t
               JOIN users u ON u.id = t.user_id
              WHERE t.token_hash = ?
                AND t.is_active = 1
                AND t.revoked_at IS NULL
              LIMIT 1'
        );
        $stmt->execute([$hash]);
        $row = $stmt->fetch(PDO::FETCH_ASSOC);
        if (!$row) {
            return null;
        }

        $userId = (int)$row['user_id'];
        $tier = FreeTier::tier($userId);
        if (!FreeTier::isPaidTier($tier)) {
            return null;
        }

        try {
            $db->prepare('UPDATE user_mcp_tokens SET last_used_at = NOW() WHERE id = ?')
                ->execute([(int)$row['id']]);
        } catch (Throwable $_) {
            // Token touch is best-effort.
        }

        $row['id'] = (int)$row['id'];
        $row['user_id'] = $userId;
        $row['tier'] = $tier;
        $row['scopes'] = json_decode((string)($row['scopes'] ?? '[]'), true) ?: [];
        return $row;
    }
}
