import { createStore } from "@stencil/store";
import { M0LiquidityApiClient, M0LiquidityApiEnvironment, } from "@m0-foundation/liquidity-sdk";
import { getChainsFromAssets } from "./chains";
import { fetchTokenBalance, fetchAllTokenBalances, getTokenKey, } from "./balances";
const { state, onChange } = createStore({
    // Props
    network: "mainnet",
    runtime: "evm",
    rpcOverrides: {},
    walletAddress: "",
    // API data
    assets: [],
    quotes: undefined,
    chains: [],
    // Loading states
    stateLoading: false,
    swapLoading: false,
    // User input
    fromToken: undefined,
    fromTokenBalance: 0n,
    tokenBalances: {}, // Map of "chain:address" -> balance
    toToken: undefined,
    fromAmount: "",
    toAmount: "",
    sourceAmount: "",
    inputDirection: "source",
    recipientAddress: undefined,
    fromChain: "all",
    toChain: "all",
    showFromTokenList: false,
    showToTokenList: false,
    // Selected quote
    selectedQuoteIndex: undefined,
    // Error state
    quoteError: undefined,
    // Exchange rate for reverse calculations (stored per-instance via store)
    lastExchangeRate: null,
});
// Helper to normalize whitelist input (handles string JSON from web component attributes)
function normalizeWhitelist(whitelist) {
    if (!whitelist)
        return [];
    if (Array.isArray(whitelist))
        return whitelist;
    if (typeof whitelist === "string") {
        try {
            const parsed = JSON.parse(whitelist);
            return Array.isArray(parsed) ? parsed : [];
        }
        catch {
            // If it's a single token string like "chain:address", wrap it
            return whitelist.includes(":") ? [whitelist] : [];
        }
    }
    return [];
}
// Helper function to filter assets by whitelist (exported for component use)
export function applyTokenFilter(assets, whitelist) {
    const normalizedWhitelist = normalizeWhitelist(whitelist);
    if (normalizedWhitelist.length === 0) {
        return assets;
    }
    // Pre-parse whitelist into a Set for O(1) lookups instead of O(n) per asset
    const whitelistSet = new Set(normalizedWhitelist.map((token) => {
        const [chain, address] = token.split(":");
        return `${chain?.toLowerCase() || ""}:${address?.toLowerCase() || ""}`;
    }));
    return assets.filter((asset) => {
        const key = `${asset.chain.toLowerCase()}:${asset.address.toLowerCase()}`;
        return whitelistSet.has(key);
    });
}
export const loadState = async (network, rpcOverrides, enableQueryParams = false, disableSvm = false, disableEvm = false) => {
    state.network = network;
    state.rpcOverrides = rpcOverrides;
    if (state.assets.length > 0) {
        return state.assets;
    }
    state.stateLoading = true;
    let assets = await getApiClient(network).quote.getSupportedAssets();
    // Filter assets based on disabled runtimes
    if (disableSvm) {
        assets = assets.filter((a) => a.runtime !== "svm");
    }
    if (disableEvm) {
        assets = assets.filter((a) => a.runtime !== "evm");
    }
    let fromToken;
    let toToken;
    // First priority: Query parameters (if enabled)
    if (enableQueryParams) {
        const urlParams = new URLSearchParams(window.location.search);
        const fromChain = urlParams.get("fromChain");
        const fromAddress = urlParams.get("fromAddress");
        const toChain = urlParams.get("toChain");
        const toAddress = urlParams.get("toAddress");
        if (fromChain && fromAddress) {
            fromToken = assets.find((a) => a.chain.toLowerCase() === fromChain.toLowerCase() &&
                a.address.toLowerCase() === fromAddress.toLowerCase());
        }
        if (toChain && toAddress) {
            toToken = assets.find((a) => a.chain.toLowerCase() === toChain.toLowerCase() &&
                a.address.toLowerCase() === toAddress.toLowerCase());
        }
    }
    // Second priority: LocalStorage (if query params didn't find tokens)
    if (!fromToken || !toToken) {
        const savedFromToken = localStorage.getItem("fromToken");
        const savedToToken = localStorage.getItem("toToken");
        if (savedFromToken && savedToToken) {
            try {
                const parsedFromToken = JSON.parse(savedFromToken);
                const parsedToToken = JSON.parse(savedToToken);
                // Only use localStorage if we didn't find from query params
                if (!fromToken) {
                    fromToken = assets.find((a) => a.chain === parsedFromToken.chain &&
                        a.address === parsedFromToken.address);
                }
                if (!toToken) {
                    toToken = assets.find((a) => a.chain === parsedToToken.chain &&
                        a.address === parsedToToken.address);
                }
            }
            catch (error) {
                console.warn("Failed to parse saved tokens from localStorage", error);
            }
        }
    }
    // Fallback to default selection
    state.fromToken = fromToken || assets[0];
    state.toToken = toToken || assets[1];
    state.fromChain = state.fromToken.chain;
    state.toChain = state.toToken.chain;
    // Set runtime based on fromToken
    state.runtime = state.fromToken.runtime;
    state.assets = assets;
    state.stateLoading = false;
    // Update localStorage and query params with selected tokens
    if (state.fromToken) {
        localStorage.setItem("fromToken", JSON.stringify(state.fromToken));
    }
    if (state.toToken) {
        localStorage.setItem("toToken", JSON.stringify(state.toToken));
    }
    // Update query params if enabled
    if (enableQueryParams) {
        const url = new URL(window.location.href);
        url.searchParams.set("fromChain", state.fromToken.chain);
        url.searchParams.set("fromAddress", state.fromToken.address);
        url.searchParams.set("toChain", state.toToken.chain);
        url.searchParams.set("toAddress", state.toToken.address);
        window.history.replaceState({}, "", url.toString());
    }
    fetchTokenBalance(state.walletAddress, fromToken, state.rpcOverrides);
    return assets;
};
export const loadQuotes = async () => {
    try {
        state.swapLoading = true;
        let inputAmount;
        if (state.inputDirection === "destination") {
            // User is editing destination amount, need to calculate source
            const toAmount = state.toAmount;
            if (!toAmount || parseFloat(toAmount) <= 0) {
                state.swapLoading = false;
                return;
            }
            // If we have a previous rate, estimate the source amount
            if (state.lastExchangeRate && state.lastExchangeRate > 0) {
                inputAmount = (parseFloat(toAmount) * state.lastExchangeRate).toString();
            }
            else {
                // No rate yet, use a 1:1 estimate to get initial rate
                inputAmount = toAmount;
            }
        }
        else {
            // User is editing source amount
            inputAmount = state.sourceAmount || state.fromAmount;
            if (!inputAmount || parseFloat(inputAmount) <= 0) {
                state.swapLoading = false;
                return;
            }
        }
        const request = {
            route: {
                source: {
                    chain: state.fromToken.chain,
                    address: state.fromToken.address,
                },
                destination: {
                    chain: state.toToken.chain,
                    address: state.toToken.address,
                },
            },
            amountIn: (parseFloat(inputAmount) *
                10 ** state.fromToken.decimals).toString(),
            sender: state.walletAddress,
            recipient: state.recipientAddress ?? state.walletAddress,
            quoteType: undefined,
            maxNumQuotes: 1,
        };
        state.quotes = await getApiClient(state.network).quote.quote(request, {
            maxRetries: 0,
        });
        state.selectedQuoteIndex = state.quotes.length > 0 ? 0 : undefined;
        // Update the exchange rate and sync amounts
        if (state.quotes && state.quotes.length > 0) {
            const quote = state.quotes[0];
            const amountIn = parseFloat(inputAmount);
            const amountOut = parseFloat(quote.amountOut) / 10 ** state.toToken.decimals;
            if (amountOut > 0) {
                state.lastExchangeRate = amountIn / amountOut;
            }
            // Sync the other input based on which one user edited
            if (state.inputDirection === "destination") {
                // User edited destination, update source to match
                state.fromAmount = inputAmount;
            }
            else {
                // User edited source, update destination display
                state.toAmount = amountOut.toString();
            }
        }
    }
    catch (error) {
        console.error("Error loading quotes", error);
        state.quoteError =
            error instanceof Error
                ? error.message
                : "Failed to fetch quotes. Please try again.";
        state.quotes = undefined;
    }
    finally {
        state.swapLoading = false;
    }
};
export const inputChain = () => {
    return state.chains.find((c) => c.id === state.fromToken.chain);
};
export const outputChain = () => {
    return state.chains.find((c) => c.id === state.toToken.chain);
};
onChange("assets", (assets) => {
    state.chains = getChainsFromAssets(assets);
});
onChange("walletAddress", (address) => {
    // update wallet balances for all tokens
    state.fromTokenBalance = 0n;
    state.tokenBalances = {};
    fetchAllTokenBalances(address, state.assets, state.rpcOverrides);
    if (address && state.fromToken && state.fromAmount) {
        loadQuotes();
    }
});
// Reset recipient address when switching back to same runtime
onChange("fromToken", (fromToken) => {
    // update fromTokenBalance from tokenBalances map
    if (fromToken) {
        const key = getTokenKey(fromToken.chain, fromToken.address);
        state.fromTokenBalance = state.tokenBalances[key] || 0n;
        state.runtime = fromToken.runtime;
    }
    else {
        state.fromTokenBalance = 0n;
    }
    if (fromToken &&
        state.toToken &&
        fromToken.runtime === state.toToken.runtime) {
        state.recipientAddress = undefined;
    }
    // Save to localStorage
    if (fromToken) {
        localStorage.setItem("fromToken", JSON.stringify(fromToken));
    }
});
onChange("toToken", (toToken) => {
    if (toToken &&
        state.fromToken &&
        toToken.runtime === state.fromToken.runtime) {
        state.recipientAddress = undefined;
    }
    // Save to localStorage
    if (toToken) {
        localStorage.setItem("toToken", JSON.stringify(toToken));
    }
});
function getApiClient(network) {
    let apiEnv;
    if (network === "local") {
        apiEnv = M0LiquidityApiEnvironment.Localnet;
    }
    else if (network === "devnet") {
        apiEnv = M0LiquidityApiEnvironment.Devnet;
    }
    else {
        apiEnv = M0LiquidityApiEnvironment.Mainnet;
    }
    return new M0LiquidityApiClient({ environment: apiEnv });
}
export { state };
//# sourceMappingURL=store.js.map
