// retoor <retoor@molodetz.nl>

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

export class ContainerInstance {
    constructor(root) {
        this.root = root;
        this.slug = root.dataset.slug;
        this.uid = root.dataset.uid;
        this.status = root.dataset.status;
        this.base = `/projects/${this.slug}/containers`;
        this.name = root.dataset.name || this.uid;
        this.canManage = root.dataset.canManage === "1";
        this.detailPoll = null;
        this.logPoll = null;
    }

    init() {
        this.renderActions(this.status);
        this.updateTerminalAvailability();
        this.bind();
        this.startDetailPoll();
        this.startLogPoll();
        this.subscribe();
    }

    subscribe() {
        const pubsub = window.app && window.app.pubsub;
        if (!pubsub) return;
        pubsub.subscribe(`container.${this.uid}.detail`, (data) => this.applyDetail(data));
        pubsub.subscribe(`container.${this.uid}.logs`, (data) => this.renderLogs(data.logs || ""));
    }

    bind() {
        const execRun = this.q("#ci-exec-run");
        if (execRun) execRun.addEventListener("click", () => this.execOnce());
        const termToggle = this.q("#ci-term-toggle");
        if (termToggle) termToggle.addEventListener("click", () => this.openTerminal());
        const form = document.getElementById("ci-schedule-form");
        if (form) {
            form.addEventListener("submit", (e) => { e.preventDefault(); this.addSchedule(form); });
            const kind = form.elements.kind;
            kind.addEventListener("change", () => this.syncScheduleFields(kind.value));
            this.syncScheduleFields(kind.value);
        }
        this.q("#ci-schedules").addEventListener("click", (e) => {
            const btn = e.target.closest("[data-schedule-delete]");
            if (btn) this.deleteSchedule(btn.dataset.scheduleDelete);
        });
    }

    q(sel) { return this.root.querySelector(sel); }

    toast(message, type) {
        if (window.app && window.app.toast) window.app.toast.show(message, { type: type || "info" });
    }

    escape(value) {
        const span = document.createElement("span");
        span.textContent = value == null ? "" : String(value);
        return span.innerHTML;
    }

    renderActions(status) {
        if (!this.canManage) {
            const box = this.q("#ci-actions");
            box.innerHTML = `<span class="cm-muted">View only. This container is managed by its owner.</span>`;
            return;
        }
        const running = status === "running";
        const paused = status === "paused";
        const spec = [
            { action: "start", label: "Start", disabled: running },
            { action: "stop", label: "Stop", disabled: status === "stopped" || status === "created" },
            { action: "restart", label: "Restart", disabled: !running },
            { action: "pause", label: "Pause", disabled: !running },
            { action: "resume", label: "Resume", disabled: !paused },
            { action: "sync", label: "Sync files", disabled: false },
            { action: "delete", label: "Delete", disabled: false },
        ];
        const box = this.q("#ci-actions");
        box.innerHTML = "";
        for (const item of spec) {
            const btn = document.createElement("button");
            btn.type = "button";
            btn.className = "btn btn-secondary" + (item.action === "delete" ? " ci-danger" : "");
            btn.textContent = item.label;
            btn.disabled = item.disabled;
            btn.addEventListener("click", () => this.instanceAction(item.action));
            box.appendChild(btn);
        }
    }

    async instanceAction(action) {
        if (action === "delete" && !window.confirm("Delete this instance? The container is removed.")) return;
        try {
            const url = action === "delete" ? `${this.base}/instances/${this.uid}/delete`
                : action === "sync" ? `${this.base}/instances/${this.uid}/sync`
                : `${this.base}/instances/${this.uid}/${action}`;
            const res = await Http.send(url, {});
            if (action === "delete") { window.location.href = "/admin/containers"; return; }
            if (action === "sync") this.toast(this.syncSummary(res.data), "success");
            else this.toast(`${action} requested`, "success");
        } catch (err) { this.toast(err.message, "error"); }
    }

    syncSummary(data) {
        const counts = data || {};
        const parts = [];
        if (counts.exported) parts.push(`${counts.exported} exported`);
        if (counts.imported) parts.push(`${counts.imported} imported`);
        if (counts.deleted_in_project) parts.push(`${counts.deleted_in_project} deleted in project`);
        if (counts.deleted_in_workspace) parts.push(`${counts.deleted_in_workspace} deleted in workspace`);
        return parts.length ? `Synced: ${parts.join(", ")}` : "Synced: already up to date";
    }

    startDetailPoll() {
        this.detailPoll = new Poller(async () => {
            const detail = await Http.getJson(`${this.base}/instances/${this.uid}`);
            this.applyDetail(detail);
        }, 20000, { pauseHidden: true });
    }

    applyDetail(detail) {
        const inst = detail.instance || {};
        if (inst.status && inst.status !== this.status) {
            this.status = inst.status;
            const badge = this.q("#ci-status");
            badge.textContent = inst.status;
            badge.className = `cm-badge cm-${inst.status}`;
            this.renderActions(inst.status);
            this.updateTerminalAvailability();
        }
        const stats = detail.stats || {};
        this.setMetric("samples", stats.samples);
        this.setMetric("cpu_avg", `${stats.cpu_avg ?? 0}%`);
        this.setMetric("cpu_p95", `${stats.cpu_p95 ?? 0}%`);
        this.setMetric("mem_max", stats.mem_max);
        const runtime = detail.runtime || {};
        const cid = this.q("#ci-container-id");
        if (cid) cid.textContent = runtime.container_id || "-";
        if (Array.isArray(detail.schedules)) this.renderSchedules(detail.schedules);
    }

    setMetric(key, value) {
        const el = this.q(`[data-metric="${key}"]`);
        if (el) el.textContent = value == null ? "0" : value;
    }

    startLogPoll() {
        this.logPoll = new Poller(async () => {
            try {
                const data = await Http.getJson(`${this.base}/instances/${this.uid}/logs?tail=400`);
                this.renderLogs(data.logs || "");
            } catch (error) {
                const pre = this.q("#ci-log");
                pre.classList.add("ci-empty");
                pre.textContent = "Logs are currently unavailable.";
            }
        }, 20000, { pauseHidden: true });
    }

    renderLogs(logs) {
        const pre = this.q("#ci-log");
        pre.classList.toggle("ci-empty", !logs);
        pre.textContent = logs || (this.status === "running"
            ? "No output yet. This panel shows the container's main process (stdout and stderr); commands run in the terminal above are not included here."
            : "No logs. Start the instance to capture its main-process output.");
        if (logs) pre.scrollTop = pre.scrollHeight;
    }

    async execOnce() {
        const input = this.q("#ci-exec-input");
        const cmd = input.value.trim();
        if (!cmd) return;
        this.q("#ci-terminal").hidden = false;
        const pre = this.q("#ci-term");
        try {
            const res = await Http.send(`${this.base}/instances/${this.uid}/exec`, { command: cmd });
            pre.textContent += `$ ${cmd}\n${this.cleanTerm(res.output || "")}\n`;
            pre.scrollTop = pre.scrollHeight;
            input.value = "";
        } catch (err) { this.toast(err.message, "error"); }
    }

    updateTerminalAvailability() {
        const toggle = this.q("#ci-term-toggle");
        if (!toggle) return;
        const running = this.status === "running";
        toggle.disabled = !running;
        toggle.title = running ? "" : "Start the instance to open an interactive shell";
    }

    openTerminal() {
        if (this.status !== "running") return;
        if (window.app && window.app.containerTerminals) {
            window.app.containerTerminals.open(this.slug, this.uid, this.name);
        }
    }

    cleanTerm(text) {
        return text
            .replace(/\x1b\][0-9];[^\x07\x1b]*(\x07|\x1b\\)/g, "")
            .replace(/\x1b\[[0-9;?]*[ -\/]*[@-~]/g, "")
            .replace(/\x1b[()#][0-9A-Za-z]/g, "")
            .replace(/\x1b[=>]/g, "");
    }

    syncScheduleFields(kind) {
        const form = document.getElementById("ci-schedule-form");
        form.querySelectorAll("[data-sched-field]").forEach((field) => {
            field.hidden = field.dataset.schedField !== kind;
        });
    }

    async addSchedule(form) {
        const kind = form.elements.kind.value;
        const body = { action: form.elements.action.value, kind };
        if (kind === "cron") body.cron = form.elements.cron.value.trim();
        if (kind === "interval") body.every_seconds = form.elements.every_seconds.value;
        if (kind === "once") body.delay_seconds = form.elements.delay_seconds.value;
        try {
            await Http.send(`${this.base}/instances/${this.uid}/schedules`, body);
            const modal = document.getElementById("ci-schedule-modal");
            if (modal) modal.classList.remove("visible");
            form.reset();
            this.syncScheduleFields(form.elements.kind.value);
            this.toast("Schedule added", "success");
        } catch (err) { this.toast(err.message, "error"); }
    }

    async deleteSchedule(sid) {
        const ok = await window.app.dialog.confirm({
            title: "Delete schedule",
            message: "Delete this schedule? The automated task is removed.",
            confirmLabel: "Delete",
            danger: true,
        });
        if (!ok) return;
        try {
            await Http.send(`${this.base}/instances/${this.uid}/schedules/${sid}/delete`, {});
            this.toast("Schedule removed", "success");
        } catch (err) { this.toast(err.message, "error"); }
    }

    renderSchedules(schedules) {
        const list = this.q("#ci-schedules");
        if (!schedules.length) {
            list.innerHTML = `<li class="cm-muted ci-schedule-empty">No schedules.</li>`;
            return;
        }
        const removeBtn = (s) => this.canManage
            ? `<button type="button" class="btn btn-secondary btn-sm" data-schedule-delete="${this.escape(s.uid)}">Delete</button>`
            : "";
        list.innerHTML = schedules.map((s) => `<li class="ci-schedule" data-sid="${this.escape(s.uid)}">
            <span class="ci-schedule-action">${this.escape(s.action)}</span>
            <span class="ci-schedule-next">next ${this.escape(s.next_run_at || "-")}</span>
            <span class="cm-muted">runs ${this.escape(s.run_count || 0)}</span>
            ${removeBtn(s)}
        </li>`).join("");
    }
}
