import { MiniLineChart } from "../components/mini-line-chart.js";

class CurrencyConverter {
    constructor(options = {}) {
        this.root = options.rootNode || document;
        this._sendPostRequest = options.sendPostRequest || (typeof sendPostRequest !== "undefined" ? sendPostRequest : null);
        this._addThousandSeparator = options.addThousandSeparator || (typeof addThousandSeparator !== "undefined" ? addThousandSeparator : null);
        this._initCustomSelects = options.initCustomSelects || (typeof initCustomSelects !== "undefined" ? initCustomSelects : null);
        this._apiEndpoint = options.apiEndpoint || (typeof API_ENDPOINT !== "undefined" ? API_ENDPOINT : "");
        this.iconPath = options.iconPath || (typeof ICON_PATH !== "undefined" ? ICON_PATH : "");
        this.iconType = options.iconType || (typeof ICON_TYPE !== "undefined" ? ICON_TYPE : ".svg");
        const initial = options.initial || window.CONVERTER_INITIAL || {};
        this.CONFIG = {
            OBSERVER_THRESHOLD: .1,
            OBSERVER_ROOT_MARGIN: "100px"
        };
        this.state = {
            fromCurrency: initial.from || "USD",
            toCurrency: initial.to || "EUR",
            fromAmount: 1,
            toAmount: 0,
            rate: 0,
            inverseRate: 0,
            currencies: {},
            currenciesLoaded: false,
            rateLoaded: false,
            activeInput: "from"
        };
        this.chart = null;
        this.chartInitialized = false;
        this.els = {};
        this.observer = null;
        this.#init();
    }
    #init() {
        this.#cacheElements();
        this.#setupEventListeners();
        this.#setupLazyLoading();
        this.#setupObserver();
    }
    #setupObserver() {
        const container = this.root.querySelector(".converter-card") || this.els.fromAmount?.closest(".converter-container");
        if (!container) {
            this.#fetchRate();
            return;
        }
        this.observer = new IntersectionObserver(entries => {
            entries.forEach(entry => {
                if (entry.isIntersecting) {
                    this.#fetchRate();
                    this.observer.disconnect();
                }
            });
        }, {
            threshold: this.CONFIG.OBSERVER_THRESHOLD,
            rootMargin: this.CONFIG.OBSERVER_ROOT_MARGIN
        });
        this.observer.observe(container);
    }
    #cacheElements() {
        const r = this.root;
        this.els.fromAmount = r.getElementById("fromAmount");
        this.els.toAmount = r.getElementById("toAmount");
        this.els.fromSelect = r.getElementById("fromCurrencySelect");
        this.els.toSelect = r.getElementById("toCurrencySelect");
        this.els.fromList = r.getElementById("fromList");
        this.els.toList = r.getElementById("toList");
        this.els.swapBtn = r.getElementById("swapBtn");
        this.els.rateDisplay = r.getElementById("rateDisplay");
        this.els.rateInfo = r.getElementById("rateInfo");
        this.els.rateMain = r.getElementById("rateMain");
        this.els.rateInverse = r.getElementById("rateInverse");
        this.els.chartWrapper = r.getElementById("chartWrapper");
        this.els.chartContainer = r.getElementById("chartContainer");
    }
    #setupEventListeners() {
        this.els.fromAmount?.addEventListener("input", e => {
            this.state.activeInput = "from";
            this.#handleAmountInput(e.target, "from");
        });
        this.els.toAmount?.addEventListener("input", e => {
            this.state.activeInput = "to";
            this.#handleAmountInput(e.target, "to");
        });
        this.els.fromSelect?.addEventListener("customSelectChange", e => {
            this.#handleCurrencyChange("from", e.detail.value);
        });
        this.els.toSelect?.addEventListener("customSelectChange", e => {
            this.#handleCurrencyChange("to", e.detail.value);
        });
        this.els.swapBtn?.addEventListener("click", () => this.#swapCurrencies());
    }
    #setupLazyLoading() {
        const fromTrigger = this.els.fromSelect?.querySelector(".select-trigger");
        const toTrigger = this.els.toSelect?.querySelector(".select-trigger");
        fromTrigger?.addEventListener("click", async () => {
            if (!this.state.currenciesLoaded) {
                await this.#loadCurrencies();
            }
        });
        toTrigger?.addEventListener("click", async () => {
            if (!this.state.currenciesLoaded) {
                await this.#loadCurrencies();
            }
        });
    }
    #handleAmountInput(input, type) {
        let value = input.value.replace(/[^\d.]/g, "");
        value = value.replace(/(\..*)\./g, "$1");
        input.value = value;
        const amount = parseFloat(value) || 0;
        if (type === "from") {
            this.state.fromAmount = amount;
            if (this.state.rate > 0) {
                this.state.toAmount = amount * this.state.rate;
                this.els.toAmount.value = this.#formatAmount(this.state.toAmount);
            }
        } else {
            this.state.toAmount = amount;
            if (this.state.inverseRate > 0) {
                this.state.fromAmount = amount * this.state.inverseRate;
                this.els.fromAmount.value = this.#formatAmount(this.state.fromAmount);
            }
        }
    }
    #formatAmount(value) {
        if (value === 0) return "0";
        let decimals = 2;
        if (value < .01) decimals = 6; else if (value < 1) decimals = 4; else if (value >= 1e3) decimals = 2;
        const formatted = value.toFixed(decimals);
        return this._addThousandSeparator ? this._addThousandSeparator(formatted) : formatted;
    }
    async #fetchRate() {
        this.#setRateUpdating(true);
        try {
            const rateResponse = await this._sendPostRequest(`${this._apiEndpoint}forex/converter`, {
                pair1: this.state.fromCurrency,
                pair2: this.state.toCurrency,
                amount: 1
            });
            if (rateResponse?.status && rateResponse.response) {
                const total = parseFloat(rateResponse.response.total) || 0;
                this.state.rate = total;
                this.state.inverseRate = total > 0 ? 1 / total : 0;
                this.state.rateLoaded = true;
                if (this.state.activeInput === "from") {
                    this.state.toAmount = this.state.fromAmount * this.state.rate;
                    this.els.toAmount.value = this.#formatAmount(this.state.toAmount);
                } else {
                    this.state.fromAmount = this.state.toAmount * this.state.inverseRate;
                    this.els.fromAmount.value = this.#formatAmount(this.state.fromAmount);
                }
                this.#updateRateDisplay();
                this.#fetchChartData();
            }
        } catch (error) {} finally {
            this.#setRateUpdating(false);
        }
    }
    #updateRateDisplay() {
        const {fromCurrency: fromCurrency, toCurrency: toCurrency, rate: rate, inverseRate: inverseRate} = this.state;
        this.els.rateDisplay?.removeAttribute("aria-busy");
        const isRtl = this.root.querySelector?.('[dir="rtl"]')?.dir === "rtl" || document.documentElement.dir === "rtl";
        if (this.els.rateMain) {
            this.els.rateMain.classList.remove("skeleton", "skeleton-text", "skeleton-w-60");
            this.els.rateMain.innerHTML = isRtl ? `${toCurrency} <strong>${rate.toFixed(5)}</strong> = ${fromCurrency} 1` : `1 ${fromCurrency} = <strong>${rate.toFixed(5)}</strong> ${toCurrency}`;
        }
        if (this.els.rateInverse) {
            this.els.rateInverse.classList.remove("skeleton", "skeleton-text-sm", "skeleton-w-50");
            this.els.rateInverse.textContent = isRtl ? `${fromCurrency} ${inverseRate.toFixed(5)} = ${toCurrency} 1` : `1 ${toCurrency} = ${inverseRate.toFixed(5)} ${fromCurrency}`;
        }
    }
    async #fetchChartData() {
        if (!this.els.chartContainer) return;
        if (this.chartInitialized) {
            this.#setChartUpdating(true);
        }
        try {
            const response = await this._sendPostRequest(`${this._apiEndpoint}forex/history`, {
                symbol: `FX:${this.state.fromCurrency}${this.state.toCurrency}`,
                period: "1d",
                level: 3
            });
            if (response?.status && response.response) {
                const historyData = Object.entries(response.response).map(([timestamp, candle]) => ({
                    timestamp: parseInt(timestamp) * 1e3,
                    open: parseFloat(candle.o),
                    high: parseFloat(candle.h),
                    low: parseFloat(candle.l),
                    close: parseFloat(candle.c)
                })).sort((a, b) => a.timestamp - b.timestamp);
                if (historyData.length) {
                    this.#renderChart(historyData);
                } else {
                    this.#showChartError("No historical data available");
                }
            } else {
                this.#showChartError(response?.msg || "Chart data unavailable");
            }
        } catch (error) {
            this.#showChartError("Failed to load chart");
        }
    }
    #showChartError(message) {
        if (this.chart?.destroy) {
            this.chart.destroy();
        }
        this.chart = null;
        this.els.chartContainer?.classList.remove("skeleton", "chart-skeleton");
        this.els.chartContainer.innerHTML = `\n            <div class="chart-error">\n                <svg class="chart-error-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n                    <path d="M3 3v18h18"/>\n                    <path d="M18 9l-5 5-4-4-3 3"/>\n                </svg>\n                <span class="chart-error-text">${message}</span>\n            </div>\n        `;
        this.els.chartWrapper?.classList.add("chart-loaded");
        this.#setChartUpdating(false);
    }
    #renderChart(data) {
        const isFirstLoad = !this.chartInitialized;
        if (!this.chart) {
            this.els.chartContainer.innerHTML = "";
            this.els.chartContainer.classList.remove("chart-skeleton");
            this.chart = new MiniLineChart(this.els.chartContainer, {
                height: 280,
                showLastInfo: true,
                showXAxis: true,
                showYAxis: true,
                xAxisTicks: 6,
                yAxisTicks: 4
            });
        }
        this.chart.setData(data);
        this.chartInitialized = true;
        if (isFirstLoad) {
            this.#setChartLoaded();
        } else {
            this.#setChartUpdating(false);
        }
    }
    #setChartLoaded() {
        this.els.chartContainer?.classList.remove("skeleton");
        this.els.chartWrapper?.classList.add("chart-loaded");
    }
    #setChartUpdating(isUpdating) {
        if (isUpdating) {
            this.els.chartWrapper?.classList.add("chart-updating");
        } else {
            this.els.chartWrapper?.classList.remove("chart-updating");
        }
    }
    #setRateUpdating(isUpdating) {
        if (isUpdating) {
            if (this.state.rateLoaded) {
                this.els.rateInfo?.classList.add("rate-updating");
            }
        } else {
            this.els.rateInfo?.classList.remove("rate-updating");
        }
    }
    #handleCurrencyChange(type, code) {
        if (!code) return;
        if (type === "from") {
            this.state.fromCurrency = code;
        } else {
            this.state.toCurrency = code;
        }
        this.#fetchRate();
    }
    async #loadCurrencies() {
        if (this.state.currenciesLoaded) return;
        try {
            const response = await this._sendPostRequest(`${this._apiEndpoint}forex/list`, {
                type: "forex",
                exchange: "FX",
                merge: "profile,meta",
                columns: "profile.currency,profile.base_logo,profile.curr_logo,meta.base_currency_desc,meta.currency_desc,meta.base_currency"
            });
            if (response?.status && response.response) {
                this.#processCurrencies(response.response);
                this.state.currenciesLoaded = true;
                this.#hideLoadingShowOptions("from");
                this.#hideLoadingShowOptions("to");
                this.#buildCurrencyList("from");
                this.#buildCurrencyList("to");
                if (this._initCustomSelects) {
                    this._initCustomSelects("#fromCurrencySelect", this.root instanceof ShadowRoot ? this.root : null);
                    this._initCustomSelects("#toCurrencySelect", this.root instanceof ShadowRoot ? this.root : null);
                }
            }
        } catch (error) {}
    }
    #hideLoadingShowOptions(type) {
        const select = type === "from" ? this.els.fromSelect : this.els.toSelect;
        const list = type === "from" ? this.els.fromList : this.els.toList;
        const loadingSpinner = select?.querySelector(".menu-search-result-initial-loading");
        if (loadingSpinner) loadingSpinner.classList.add("d-none");
        if (list) list.classList.remove("d-none");
    }
    #processCurrencies(data) {
        const currencies = {};
        for (let i = 0; i < data.length; i++) {
            const {profile: profile, meta: meta} = data[i];
            if (!profile || !meta) continue;
            if (meta.base_currency && !currencies[meta.base_currency]) {
                currencies[meta.base_currency] = {
                    code: meta.base_currency,
                    logo: profile.base_logo || "",
                    name: meta.base_currency_desc || meta.base_currency
                };
            }
            if (profile.currency && !currencies[profile.currency]) {
                currencies[profile.currency] = {
                    code: profile.currency,
                    logo: profile.curr_logo || "",
                    name: meta.currency_desc || profile.currency
                };
            }
        }
        this.state.currencies = currencies;
    }
    #buildCurrencyList(type) {
        const list = type === "from" ? this.els.fromList : this.els.toList;
        const currentValue = type === "from" ? this.state.fromCurrency : this.state.toCurrency;
        const codes = Object.keys(this.state.currencies).sort();
        let html = "";
        for (let i = 0; i < codes.length; i++) {
            const code = codes[i];
            const currency = this.state.currencies[code];
            const selected = code === currentValue ? " selected" : "";
            const imgSrc = `${this.iconPath}${currency.logo}${this.iconType}`;
            const triggerHTML = `<span class="select-option-img"><div class="flag-icon-container"><img src="${imgSrc}" alt="${code}" class="flag-img"></div>${code}</span>`;
            html += `\n                <li class="select-option${selected}" data-value="${code}" data-html="${triggerHTML.replace(/"/g, "&quot;")}">\n                    <span class="select-option-img">\n                        <div class="flag-icon-container">\n                            <img data-src="${imgSrc}" alt="${code}" class="flag-img">\n                        </div>\n                        ${currency.name}\n                    </span>\n                </li>\n            `;
        }
        list.innerHTML = html;
    }
    #swapCurrencies() {
        const tempCurrency = this.state.fromCurrency;
        this.state.fromCurrency = this.state.toCurrency;
        this.state.toCurrency = tempCurrency;
        const tempAmount = this.state.fromAmount;
        this.state.fromAmount = this.state.toAmount;
        this.state.toAmount = tempAmount;
        this.els.fromAmount.value = this.#formatAmount(this.state.fromAmount);
        this.els.toAmount.value = this.#formatAmount(this.state.toAmount);
        const tempRate = this.state.rate;
        this.state.rate = this.state.inverseRate;
        this.state.inverseRate = tempRate;
        this.#updateRateDisplay();
        this.#fetchChartData();
        this.#swapSelectTriggers();
    }
    #swapSelectTriggers() {
        if (this.state.currenciesLoaded && this.els.fromSelect?.customSelectAPI?.setValue) {
            this.els.fromSelect.customSelectAPI.setValue(this.state.fromCurrency);
            this.els.toSelect.customSelectAPI.setValue(this.state.toCurrency);
            return;
        }
        const fromTrigger = this.els.fromSelect?.querySelector(".select-trigger-text");
        const toTrigger = this.els.toSelect?.querySelector(".select-trigger-text");
        if (fromTrigger && toTrigger) {
            const fromImg = fromTrigger.querySelector("img");
            const toImg = toTrigger.querySelector("img");
            const fromLogo = fromImg?.getAttribute("src") || "";
            const toLogo = toImg?.getAttribute("src") || "";
            fromTrigger.innerHTML = `\n                <span class="select-option-img">\n                    <div class="flag-icon-container">\n                        <img src="${toLogo}" alt="${this.state.fromCurrency}" class="flag-img">\n                    </div>\n                    <span>${this.state.fromCurrency}</span>\n                </span>\n            `;
            toTrigger.innerHTML = `\n                <span class="select-option-img">\n                    <div class="flag-icon-container">\n                        <img src="${fromLogo}" alt="${this.state.toCurrency}" class="flag-img">\n                    </div>\n                    <span>${this.state.toCurrency}</span>\n                </span>\n            `;
        }
    }
}

document.addEventListener("DOMContentLoaded", () => {
    if (document.getElementById("fromAmount")) {
        new CurrencyConverter;
    }
});

export { CurrencyConverter };

if (typeof window !== "undefined") {
    window.CurrencyConverter = CurrencyConverter;
}