var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
    if (kind === "m") throw new TypeError("Private method is not writable");
    if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
    if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
    return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
    if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
    if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
    return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _TerminalMarketService_instances, _TerminalMarketService_deps, _TerminalMarketService_cache, _TerminalMarketService_validateItems, _TerminalMarketService_mapToMarketInfo, _TerminalMarketService_extractMetadata;
import { array, boolean, is, nullable, number, optional, string, type } from "@metamask/superstruct";
import { PERPS_CONSTANTS, TERMINAL_API_CONFIG } from "../constants/perpsConfig.mjs";
import { MarketCategory } from "../types/index.mjs";
import { ensureError } from "../utils/errorUtils.mjs";
const VALID_MARKET_TYPES = new Set(Object.values(MarketCategory));
/**
 * Runtime validation schema for a single market item returned by
 * `GET {terminalApiUrl}`.
 *
 * Uses `type()` (loose object matching) so that extra fields the API sends
 * (e.g. `price`, `iconUrl`, `trend`) are silently accepted.
 * Each item is individually validated; items that fail validation are
 * filtered out and logged rather than rejecting the entire response.
 */
const TerminalPerpetualItemStruct = type({
    symbol: string(),
    name: optional(nullable(string())),
    szDecimals: optional(number()),
    maxLeverage: optional(number()),
    marginTableId: optional(number()),
    onlyIsolated: optional(boolean()),
    isDelisted: optional(boolean()),
    minimumOrderSize: optional(number()),
    keywords: optional(nullable(array(string()))),
    tags: optional(nullable(array(string()))),
    categories: optional(nullable(array(string()))),
    marketType: optional(nullable(string())),
});
/**
 * TerminalMarketService
 *
 * Fetches structured market metadata from the MetaMask Terminal API.
 * Caches responses for {@link TERMINAL_API_CONFIG.CacheTtlMs} to avoid
 * redundant network calls across polling cycles.
 *
 * Instance-based service with constructor injection of platform dependencies.
 */
export class TerminalMarketService {
    constructor(deps) {
        _TerminalMarketService_instances.add(this);
        _TerminalMarketService_deps.set(this, void 0);
        _TerminalMarketService_cache.set(this, null);
        __classPrivateFieldSet(this, _TerminalMarketService_deps, deps, "f");
    }
    /**
     * Fetch markets from the Terminal API.
     * Returns cached data when available and within TTL.
     *
     * @returns Object with mapped MarketInfo array and per-symbol metadata.
     */
    async fetchMarkets() {
        if (__classPrivateFieldGet(this, _TerminalMarketService_cache, "f") &&
            Date.now() - __classPrivateFieldGet(this, _TerminalMarketService_cache, "f").timestamp < TERMINAL_API_CONFIG.CacheTtlMs) {
            return {
                markets: __classPrivateFieldGet(this, _TerminalMarketService_cache, "f").markets,
                metadata: __classPrivateFieldGet(this, _TerminalMarketService_cache, "f").metadata,
            };
        }
        if (!__classPrivateFieldGet(this, _TerminalMarketService_deps, "f").terminalApiUrl) {
            throw new Error('Terminal API URL not configured (terminalApiUrl is required)');
        }
        const url = __classPrivateFieldGet(this, _TerminalMarketService_deps, "f").terminalApiUrl;
        const controller = new AbortController();
        const timeoutId = setTimeout(() => controller.abort(new Error('Terminal API fetch timed out')), TERMINAL_API_CONFIG.FetchTimeoutMs);
        let response;
        try {
            response = await fetch(url, {
                method: 'GET',
                headers: { 'Content-Type': 'application/json' },
                signal: controller.signal,
            });
        }
        finally {
            clearTimeout(timeoutId);
        }
        if (!response.ok) {
            throw new Error(`Terminal API returned ${String(response.status)}: ${response.statusText}`);
        }
        const body = await response.json();
        if (!Array.isArray(body)) {
            throw new Error(`Terminal API returned non-array body: ${typeof body}`);
        }
        const items = __classPrivateFieldGet(this, _TerminalMarketService_instances, "m", _TerminalMarketService_validateItems).call(this, body);
        const markets = __classPrivateFieldGet(this, _TerminalMarketService_instances, "m", _TerminalMarketService_mapToMarketInfo).call(this, items);
        const metadata = __classPrivateFieldGet(this, _TerminalMarketService_instances, "m", _TerminalMarketService_extractMetadata).call(this, items);
        __classPrivateFieldSet(this, _TerminalMarketService_cache, { markets, metadata, timestamp: Date.now() }, "f");
        return { markets, metadata };
    }
    /**
     * Invalidate the internal cache so the next fetch hits the network.
     */
    clearCache() {
        __classPrivateFieldSet(this, _TerminalMarketService_cache, null, "f");
    }
    /**
     * Log a Terminal API error to Sentry without surfacing it to the user.
     *
     * @param error - The caught error.
     * @param method - The calling method name for context.
     */
    logError(error, method) {
        __classPrivateFieldGet(this, _TerminalMarketService_deps, "f").logger.error(ensureError(error, `TerminalMarketService.${method}`), {
            tags: { feature: PERPS_CONSTANTS.FeatureName, source: 'terminal-api' },
            context: {
                name: `TerminalMarketService.${method}`,
                data: { url: __classPrivateFieldGet(this, _TerminalMarketService_deps, "f").terminalApiUrl },
            },
        });
    }
}
_TerminalMarketService_deps = new WeakMap(), _TerminalMarketService_cache = new WeakMap(), _TerminalMarketService_instances = new WeakSet(), _TerminalMarketService_validateItems = function _TerminalMarketService_validateItems(raw) {
    const valid = [];
    for (const item of raw) {
        if (is(item, TerminalPerpetualItemStruct)) {
            valid.push(item);
        }
        else {
            __classPrivateFieldGet(this, _TerminalMarketService_deps, "f").logger.error(ensureError(new Error('Terminal API item failed schema validation'), 'TerminalMarketService.validateItems'), {
                tags: {
                    feature: PERPS_CONSTANTS.FeatureName,
                    source: 'terminal-api',
                },
                context: {
                    name: 'TerminalMarketService.validateItems',
                    data: {
                        symbol: typeof item === 'object' &&
                            item !== null &&
                            Object.prototype.hasOwnProperty.call(item, 'symbol')
                            ? item.symbol
                            : undefined,
                    },
                },
            });
        }
    }
    return valid;
}, _TerminalMarketService_mapToMarketInfo = function _TerminalMarketService_mapToMarketInfo(items) {
    return items
        .filter((item) => typeof item.symbol === 'string' && item.symbol.length > 0)
        .map((item) => ({
        name: item.symbol,
        szDecimals: item.szDecimals ?? 0,
        maxLeverage: item.maxLeverage ?? 1,
        marginTableId: item.marginTableId ?? 0,
        ...(item.onlyIsolated === true && { onlyIsolated: true }),
        ...(item.isDelisted === true && { isDelisted: true }),
        ...(item.minimumOrderSize !== undefined && {
            minimumOrderSize: item.minimumOrderSize,
        }),
    }));
}, _TerminalMarketService_extractMetadata = function _TerminalMarketService_extractMetadata(items) {
    const map = new Map();
    for (const item of items) {
        if (typeof item.symbol !== 'string' || item.symbol.length === 0) {
            continue;
        }
        const entry = {};
        if (typeof item.name === 'string' && item.name.length > 0) {
            entry.name = item.name;
        }
        if (Array.isArray(item.keywords) && item.keywords.length > 0) {
            entry.keywords = item.keywords;
        }
        if (Array.isArray(item.tags) && item.tags.length > 0) {
            entry.tags = item.tags;
        }
        if (Array.isArray(item.categories) && item.categories.length > 0) {
            entry.categories = item.categories;
        }
        if (typeof item.marketType === 'string' &&
            VALID_MARKET_TYPES.has(item.marketType)) {
            entry.marketType =
                item.marketType;
        }
        map.set(item.symbol, entry);
    }
    return map;
};
//# sourceMappingURL=TerminalMarketService.mjs.map