// retoor <retoor@molodetz.nl>

import { Http } from "./Http.js";
import { Poller } from "./Poller.js";
import { Toast } from "./Toast.js";
import { DateFormat } from "./DateFormat.js";

class ServiceMonitor {
    constructor() {
        this.pollInterval = 5000;
        this.detail = document.querySelector("[data-service-detail]");
        this.detailName = this.detail ? this.detail.dataset.service : null;
        this.list = document.getElementById("services-list");
    }

    start() {
        const container = this.detail || this.list;
        if (!container) return;
        this.bindActions(container);
        this.bindConfigForms(container);
        this.poller = new Poller(() => this.poll(), 20000, { pauseHidden: true });
        const pubsub = window.app && window.app.pubsub;
        if (pubsub) {
            const topic = this.detail ? `admin.services.${this.detailName}` : "admin.services";
            pubsub.subscribe(topic, (data) => this.applyData(data));
            if (this.detail) {
                pubsub.subscribe(`admin.services.${this.detailName}.logs`, (data) => this.appendLive(data));
            }
        }
    }

    appendLive(data) {
        if (!data || !data.line || !this.detail) return;
        const pre = this.detail.querySelector("[data-live-log]");
        if (!pre) return;
        const lines = pre.textContent === "No live output yet." ? [] : pre.textContent.split("\n");
        lines.push(data.line);
        pre.textContent = lines.slice(-300).join("\n");
        pre.scrollTop = pre.scrollHeight;
    }

    applyData(data) {
        if (!data) return;
        if (this.detail) {
            if (data.service) this.update(this.detail, data.service);
        } else if (this.list && Array.isArray(data.services)) {
            for (const svc of data.services) {
                const row = this.list.querySelector(`[data-service="${svc.name}"]`);
                if (row) this.update(row, svc);
            }
        }
    }

    bindActions(container) {
        container.addEventListener("click", (event) => {
            const button = event.target.closest("[data-action]");
            if (!button) return;
            event.preventDefault();
            this.runAction(button);
        });
    }

    async runAction(button) {
        const action = button.dataset.action;
        const name = button.dataset.service;
        button.disabled = true;
        try {
            await Http.sendForm(`/admin/services/${name}/${action}`, {});
            await this.poll();
        } catch {
            Toast.flash(button, "Error", 1500);
        } finally {
            button.disabled = false;
        }
    }

    bindConfigForms(container) {
        container.querySelectorAll("[data-config-form]").forEach((form) => {
            form.addEventListener("submit", (event) => {
                event.preventDefault();
                this.saveConfig(form);
            });
            const markDirty = (event) => {
                if (event.target.name) event.target.dataset.dirty = "1";
            };
            form.addEventListener("input", markDirty);
            form.addEventListener("change", markDirty);
        });
    }

    async saveConfig(form) {
        const name = form.dataset.service;
        const statusEl = form.querySelector("[data-config-status]");
        form.querySelectorAll("[data-error]").forEach((el) => {
            el.textContent = "";
        });
        const params = new URLSearchParams();
        form.querySelectorAll("input, select, textarea").forEach((input) => {
            if (input.type === "password" && input.value === "") return;
            params.append(input.name, input.value);
        });
        try {
            const data = await Http.sendForm(`/admin/services/${name}/config`, params);
            (data.saved || []).forEach((key) => {
                const el = form.querySelector(`[name="${key}"]`);
                if (el) delete el.dataset.dirty;
            });
            if (data.ok) {
                if (statusEl) Toast.flash(statusEl, "Saved", 2000, "");
                await this.poll();
                return;
            }
            for (const [key, message] of Object.entries(data.errors || {})) {
                const el = form.querySelector(`[data-error="${key}"]`);
                if (el) el.textContent = message;
            }
            if (statusEl) Toast.flash(statusEl, "Check the errors below", 3000, "");
        } catch {
            if (statusEl) Toast.flash(statusEl, "Request failed", 2000, "");
        }
    }

    async poll() {
        try {
            if (this.detail) {
                this.applyData(await Http.getJson(`/admin/services/${this.detailName}/data`));
            } else if (this.list) {
                this.applyData(await Http.getJson("/admin/services/data"));
            }
        } catch {
        }
    }

    update(root, svc) {
        const statusEl = root.querySelector(".service-status");
        if (statusEl) {
            statusEl.textContent = svc.status;
            statusEl.className = "service-status " + svc.status;
        }
        this.setMeta(root, "uptime", svc.uptime || "-");
        this.setMeta(root, "last_run", svc.last_run ? DateFormat.format(svc.last_run, true) : "-");
        this.setMeta(root, "next_run", svc.next_run ? DateFormat.format(svc.next_run, true) : "-");
        this.setMeta(root, "heartbeat", svc.heartbeat ? DateFormat.format(svc.heartbeat, true) : "-");
        this.setMeta(root, "interval", "every " + svc.interval_seconds + "s");

        const logPre = root.querySelector(".log-output");
        if (logPre) {
            logPre.textContent = (svc.log_buffer || []).map((line) => line + "\n").join("") || "No log entries yet.\n";
        }
        const startBtn = root.querySelector('[data-action="start"]');
        const stopBtn = root.querySelector('[data-action="stop"]');
        if (startBtn) startBtn.disabled = svc.enabled;
        if (stopBtn) stopBtn.disabled = !svc.enabled;
        const metricsEl = root.querySelector("[data-metrics]");
        if (metricsEl) this.renderMetrics(metricsEl, svc.metrics);
        this.syncConfigFields(root, svc.fields);
    }

    syncConfigFields(root, fields) {
        if (!Array.isArray(fields)) return;
        const form = root.querySelector("[data-config-form]");
        if (!form) return;
        for (const field of fields) {
            if (field.secret) continue;
            const input = form.querySelector(`[name="${field.key}"]`);
            if (!input) continue;
            if (input === document.activeElement) continue;
            if (input.dataset.dirty === "1") continue;
            const value = field.value == null ? "" : String(field.value);
            if (input.value !== value) input.value = value;
        }
    }

    setMeta(root, key, value) {
        const el = root.querySelector(`[data-meta="${key}"]`);
        if (el) el.textContent = value;
    }

    renderMetrics(container, metrics) {
        container.textContent = "";
        if (!metrics || (!metrics.stats && !metrics.table)) return;
        const stats = metrics.stats || [];
        if (stats.length) {
            const grid = document.createElement("div");
            grid.className = "metric-cards";
            for (const stat of stats) {
                const cell = document.createElement("div");
                cell.className = "metric-card";
                const value = document.createElement("span");
                value.className = "metric-value";
                value.textContent = String(stat.value);
                const label = document.createElement("span");
                label.className = "metric-label";
                label.textContent = stat.label;
                cell.append(value, label);
                grid.appendChild(cell);
            }
            container.appendChild(grid);
        }
        const table = metrics.table;
        if (table && table.rows && table.rows.length) {
            const el = document.createElement("table");
            el.className = "metric-table";
            const thead = document.createElement("thead");
            const headRow = document.createElement("tr");
            for (const col of table.columns || []) {
                const th = document.createElement("th");
                th.textContent = col;
                headRow.appendChild(th);
            }
            thead.appendChild(headRow);
            el.appendChild(thead);
            const tbody = document.createElement("tbody");
            for (const row of table.rows) {
                const tr = document.createElement("tr");
                for (const cell of row) {
                    const td = document.createElement("td");
                    this.renderCell(td, cell);
                    tr.appendChild(td);
                }
                tbody.appendChild(tr);
            }
            el.appendChild(tbody);
            container.appendChild(el);
        }
    }

    renderCell(td, cell) {
        if (cell && typeof cell === "object" && cell.href) {
            const link = document.createElement("a");
            link.href = cell.href;
            link.className = "metric-link";
            if (cell.avatar) {
                const avatar = document.createElement("dp-avatar");
                avatar.setAttribute("username", String(cell.avatar));
                avatar.setAttribute("size", "20");
                link.appendChild(avatar);
            }
            link.appendChild(document.createTextNode(String(cell.text)));
            td.appendChild(link);
            return;
        }
        td.textContent = String(cell);
    }

}

window.ServiceMonitor = ServiceMonitor;
