// retoor <retoor@molodetz.nl>

import { Http } from "./Http.js";
import { StatisticsCharts } from "./StatisticsCharts.js";

class StatisticsDashboard {
    constructor(options = {}) {
        this.root = document.getElementById(options.rootId || "statistics-root");
        this.initialNode = document.getElementById(options.initialId || "statistics-initial");
        this.windowSelect = document.getElementById("statistics-window");
        this.compareToggle = document.getElementById("statistics-compare");
        this.generated = document.querySelector("[data-meta='generated']");
        this.tabLinks = Array.from(document.querySelectorAll(".admin-tabs .admin-tab[data-tab]"));
        this.activeTab = options.activeTab || "overview";
        this.windowHours = Number(options.windowHours || 168);
        this.charts = new StatisticsCharts();
        this.cache = new Map();
        this.loading = false;
    }

    start() {
        if (!this.root) return;
        if (this.windowSelect) {
            this.windowSelect.addEventListener("change", () => {
                const parsed = parseInt(this.windowSelect.value, 10);
                this.windowHours = Number.isNaN(parsed) ? 168 : parsed;
                this.cache.clear();
                this.loadTab(this.activeTab, true);
            });
        }
        if (this.compareToggle) {
            this.compareToggle.addEventListener("change", () => {
                this.cache.clear();
                this.loadTab(this.activeTab, true);
            });
        }
        for (const link of this.tabLinks) {
            link.addEventListener("click", (event) => {
                event.preventDefault();
                const tab = link.dataset.tab;
                if (!tab || tab === this.activeTab) return;
                for (const node of this.tabLinks) {
                    node.classList.toggle("active", node.dataset.tab === tab);
                }
                this.activeTab = tab;
                const url = new URL(window.location.href);
                url.searchParams.set("tab", tab);
                url.searchParams.set("hours", String(this.windowHours));
                window.history.replaceState({}, "", url);
                this.loadTab(tab, false);
            });
        }
        const initial = this.readInitial();
        if (initial) {
            this.cache.set(this.cacheKey(this.activeTab), initial);
            this.render(initial);
        } else {
            this.loadTab(this.activeTab, true);
        }
    }

    readInitial() {
        if (!this.initialNode) return null;
        try {
            return JSON.parse(this.initialNode.textContent || "null");
        } catch {
            return null;
        }
    }

    cacheKey(tab) {
        const compare = this.compareToggle && this.compareToggle.checked ? 1 : 0;
        return `${tab}:${this.windowHours}:${compare}`;
    }

    async loadTab(tab, force) {
        const key = this.cacheKey(tab);
        if (!force && this.cache.has(key)) {
            this.render(this.cache.get(key));
            return;
        }
        if (this.loading) return;
        this.loading = true;
        this.root.innerHTML = '<p class="admin-empty">Loading statistics...</p>';
        try {
            const compare = this.compareToggle && this.compareToggle.checked ? 1 : 0;
            const data = await Http.getJson(
                `/admin/statistics/data?tab=${encodeURIComponent(tab)}&hours=${this.windowHours}&compare=${compare}`
            );
            this.cache.set(key, data);
            this.render(data);
        } catch {
            this.root.innerHTML = '<p class="admin-empty">Could not load statistics.</p>';
        } finally {
            this.loading = false;
        }
    }

    formatValue(card) {
        const value = card.value;
        const kind = card.format || "int";
        if (kind === "money") {
            const number = Number(value || 0);
            return number < 1 ? `$${number.toFixed(4)}` : `$${number.toFixed(2)}`;
        }
        if (kind === "decimal") {
            return Number(value || 0).toLocaleString("en-GB", { maximumFractionDigits: 1 });
        }
        return Number(value || 0).toLocaleString("en-GB");
    }

    deltaMarkup(card) {
        if (card.delta === undefined || card.delta === null) return null;
        const direction = card.direction || "flat";
        const arrow = direction === "up" ? "\u2191" : direction === "down" ? "\u2193" : "\u2192";
        const node = document.createElement("span");
        node.className = `statistics-delta ${direction}`;
        node.textContent = `${arrow} ${Math.abs(Number(card.delta || 0)).toLocaleString("en-GB")}%`;
        return node;
    }

    isNumericValue(value) {
        if (typeof value === "number" && Number.isFinite(value)) return true;
        const text = String(value ?? "").trim().replace(/,/g, "");
        if (!text) return false;
        return /^-?\d+(\.\d+)?$/.test(text);
    }

    columnIsNumeric(columnIndex, columns, rows) {
        const header = (columns[columnIndex] || "").toLowerCase();
        const numericHeaders = [
            "count", "events", "views", "gists", "posts", "votes", "actions", "stars",
            "turns", "requests", "cost", "coins", "level", "restarts", "followers",
            "upvotes", "downvotes", "members", "guests", "tokens", "errors",
        ];
        if (numericHeaders.some((token) => header.includes(token))) return true;
        const sample = rows.slice(0, 12);
        if (!sample.length) return columnIndex > 0;
        return sample.every((row) => this.isNumericValue(row[columnIndex]));
    }

    formatTableCell(value) {
        if (typeof value === "number" && Number.isFinite(value)) {
            if (Number.isInteger(value)) return value.toLocaleString("en-GB");
            return value.toLocaleString("en-GB", { maximumFractionDigits: 2 });
        }
        const text = String(value ?? "");
        const plain = text.trim().replace(/,/g, "");
        if (/^-?\d+(\.\d+)?$/.test(plain)) {
            const number = Number(plain);
            if (Number.isInteger(number)) return number.toLocaleString("en-GB");
            return number.toLocaleString("en-GB", { maximumFractionDigits: 2 });
        }
        return text;
    }

    columnClass(columnIndex, columns, rows) {
        const header = (columns[columnIndex] || "").toLowerCase();
        if (columnIndex === 0) {
            if (
                header.includes("username")
                || header === "user"
                || header.includes("instance")
                || header === "name"
            ) {
                return "statistics-col-name";
            }
            return "statistics-col-key";
        }
        if (this.columnIsNumeric(columnIndex, columns, rows)) return "statistics-col-num";
        return "statistics-col-text";
    }

    renderBreakdownTable(table) {
        const columns = table.columns || [];
        const rows = table.rows || [];
        const panel = document.createElement("article");
        panel.className = "statistics-table-panel";
        if (columns.length > 2) panel.classList.add("is-wide");

        const head = document.createElement("div");
        head.className = "statistics-table-panel-head";
        const title = document.createElement("h4");
        title.textContent = table.title || "";
        head.appendChild(title);
        const count = document.createElement("span");
        count.className = "statistics-table-panel-count";
        count.textContent = `${rows.length.toLocaleString("en-GB")} rows`;
        head.appendChild(count);
        panel.appendChild(head);

        const wrap = document.createElement("div");
        wrap.className = "admin-table-wrap";

        const node = document.createElement("table");
        node.className = "admin-table";

        const thead = document.createElement("thead");
        const headRow = document.createElement("tr");
        for (let index = 0; index < columns.length; index += 1) {
            const th = document.createElement("th");
            th.className = this.columnClass(index, columns, rows);
            th.textContent = columns[index];
            headRow.appendChild(th);
        }
        thead.appendChild(headRow);
        node.appendChild(thead);

        const tbody = document.createElement("tbody");
        for (const row of rows) {
            const tr = document.createElement("tr");
            for (let index = 0; index < columns.length; index += 1) {
                const td = document.createElement("td");
                const cellClass = this.columnClass(index, columns, rows);
                td.className = cellClass;
                const cell = row[index];
                td.textContent = cellClass === "statistics-col-num"
                    ? this.formatTableCell(cell)
                    : String(cell ?? "");
                tr.appendChild(td);
            }
            tbody.appendChild(tr);
        }
        node.appendChild(tbody);
        wrap.appendChild(node);
        panel.appendChild(wrap);
        return panel;
    }

    render(data) {
        if (!data) return;
        if (this.generated && data.generated_at) {
            this.generated.textContent = `Generated ${data.generated_at.replace("T", " ").slice(0, 19)} UTC`;
        }
        this.root.innerHTML = "";
        if (Array.isArray(data.highlights) && data.highlights.length) {
            const wrap = document.createElement("div");
            wrap.className = "metric-cards";
            for (const item of data.highlights) {
                const card = document.createElement("div");
                card.className = "metric-card";
                const value = document.createElement("span");
                value.className = "metric-value";
                value.textContent = String(item.value ?? "");
                const label = document.createElement("span");
                label.className = "metric-label";
                label.textContent = item.label || "";
                card.appendChild(value);
                card.appendChild(label);
                wrap.appendChild(card);
            }
            this.root.appendChild(wrap);
        }
        if (Array.isArray(data.cards) && data.cards.length) {
            const grid = document.createElement("div");
            grid.className = "metric-cards";
            for (const card of data.cards) {
                const node = document.createElement("div");
                node.className = "metric-card";
                const value = document.createElement("span");
                value.className = "metric-value";
                value.textContent = this.formatValue(card);
                const label = document.createElement("span");
                label.className = "metric-label";
                label.textContent = card.label || "";
                node.appendChild(value);
                node.appendChild(label);
                const delta = this.deltaMarkup(card);
                if (delta) node.appendChild(delta);
                grid.appendChild(node);
            }
            this.root.appendChild(grid);
        }
        if (Array.isArray(data.series) && data.series.length) {
            const section = document.createElement("div");
            section.className = "statistics-section";
            const heading = document.createElement("h3");
            heading.textContent = "Trends";
            section.appendChild(heading);
            const charts = document.createElement("div");
            charts.className = "statistics-charts";
            section.appendChild(charts);
            this.root.appendChild(section);
            const renderCharts = () => this.charts.render(charts, data.series);
            if (window.Chart) {
                renderCharts();
            } else {
                window.addEventListener("load", renderCharts, { once: true });
            }
        } else {
            this.charts.destroy();
        }
        if (Array.isArray(data.tables) && data.tables.length) {
            const section = document.createElement("div");
            section.className = "statistics-section statistics-breakdowns";
            const heading = document.createElement("h3");
            heading.textContent = "Breakdowns";
            section.appendChild(heading);
            const grid = document.createElement("div");
            grid.className = "statistics-breakdowns-grid";
            for (const table of data.tables) {
                grid.appendChild(this.renderBreakdownTable(table));
            }
            section.appendChild(grid);
            this.root.appendChild(section);
        }
        if (data.notes && data.notes.full_page) {
            const note = document.createElement("p");
            note.className = "statistics-note";
            const link = document.createElement("a");
            link.className = "statistics-link";
            link.href = data.notes.full_page;
            link.textContent = "Open full detail page";
            note.appendChild(link);
            this.root.appendChild(note);
        }
        if (data.notes && data.notes.retention_days) {
            const note = document.createElement("p");
            note.className = "statistics-note";
            note.textContent = `Visitor data retention: ${data.notes.retention_days} days.`;
            this.root.appendChild(note);
        }
    }
}

window.StatisticsDashboard = StatisticsDashboard;
export { StatisticsDashboard };