// retoor <retoor@molodetz.nl>

import { Component } from "./Component.js";
import { assetUrl } from "../assetVersion.js";

const CHAT_CSS_ID = "deepsearch-chat-css";
const RETRY_CODE = 4013;
const RETRY_MS = 200;
const RECONNECT_MS = 1500;

export class AppDeepsearchChat extends Component {
    constructor() {
        super();
        this.ws = null;
        this._shouldRun = true;
        this._busy = false;
    }

    _ensureCss() {
        if (document.getElementById(CHAT_CSS_ID)) return;
        const link = document.createElement("link");
        link.id = CHAT_CSS_ID;
        link.rel = "stylesheet";
        link.href = assetUrl("/static/css/deepsearch.css");
        document.head.appendChild(link);
    }

    connectedCallback() {
        if (this._built) return;
        this._built = true;
        this._ensureCss();
        this.uid = this.attr("uid");
        this.chatPath = this.attr("chat-ws") || `/tools/deepsearch/${this.uid}/chat`;
        this._build();
        this._open();
    }

    disconnectedCallback() {
        this._shouldRun = false;
        if (this.ws) this.ws.close();
    }

    _build() {
        this.innerHTML = "";
        this.log = document.createElement("div");
        this.log.className = "ds-chat-log";
        this.log.setAttribute("role", "log");
        this.log.setAttribute("aria-live", "polite");

        this.statusEl = document.createElement("div");
        this.statusEl.className = "ds-chat-status";
        this.statusEl.hidden = true;

        this.form = document.createElement("form");
        this.form.className = "ds-chat-input";
        this.field = document.createElement("textarea");
        this.field.className = "ds-chat-field";
        this.field.rows = 1;
        this.field.placeholder = "Ask about the gathered evidence...";
        this.send = document.createElement("button");
        this.send.type = "submit";
        this.send.className = "ds-chat-send";
        this.send.textContent = "Ask";
        this.form.append(this.field, this.send);

        this.append(this.log, this.statusEl, this.form);

        this.form.addEventListener("submit", (event) => {
            event.preventDefault();
            this._submit(this.field.value);
        });
        this.field.addEventListener("keydown", (event) => {
            if (event.key === "Enter" && !event.shiftKey) {
                event.preventDefault();
                this._submit(this.field.value);
            }
        });
        this.field.addEventListener("input", () => this._autosize());
    }

    _wsUrl() {
        const scheme = location.protocol === "https:" ? "wss://" : "ws://";
        return `${scheme}${location.host}${this.chatPath}`;
    }

    _open() {
        this.ws = new WebSocket(this._wsUrl());
        this.ws.addEventListener("message", (event) => {
            let frame;
            try {
                frame = JSON.parse(event.data);
            } catch {
                return;
            }
            this._onMessage(frame);
        });
        this.ws.addEventListener("close", (event) => {
            if (event.code === RETRY_CODE && this._shouldRun) {
                window.setTimeout(() => this._open(), RETRY_MS);
                return;
            }
            if (this._shouldRun) {
                this._setStatus("Reconnecting...");
                window.setTimeout(() => this._open(), RECONNECT_MS);
            }
        });
        this.ws.addEventListener("error", () => this._setStatus("Connection error."));
    }

    _onMessage(frame) {
        if (frame.type === "ready") {
            this._setStatus("");
            this._renderHistory(frame.history || []);
        } else if (frame.type === "status") {
            this._setStatus(frame.text);
        } else if (frame.type === "reply") {
            this._busy = false;
            this._setStatus("");
            this._agent(frame.text, frame.citations);
        } else if (frame.type === "error") {
            this._busy = false;
            this._setStatus("");
            this._errorLine(frame.text);
        }
    }

    ask(text) {
        this._submit(text);
    }

    _submit(raw) {
        const text = (raw || "").trim();
        if (!text || this._busy) return;
        if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
            this._errorLine("Not connected.");
            return;
        }
        this._busy = true;
        this._userLine(text);
        this.field.value = "";
        this._autosize();
        this.ws.send(JSON.stringify({ type: "input", text }));
        this._setStatus("Searching the research collection");
    }

    _renderHistory(messages) {
        this.log.innerHTML = "";
        messages.forEach((message) => {
            if (message.role === "user") {
                this._userLine(message.content);
            } else if (message.role === "assistant" && message.content) {
                this._agent(message.content, []);
            }
        });
    }

    _userLine(text) {
        const row = document.createElement("div");
        row.className = "ds-chat-msg user";
        const bubble = document.createElement("div");
        bubble.className = "ds-chat-bubble";
        bubble.textContent = text;
        row.appendChild(bubble);
        this._append(row);
    }

    _agent(text, citations) {
        const row = document.createElement("div");
        row.className = "ds-chat-msg agent";
        const bubble = document.createElement("div");
        bubble.className = "ds-chat-bubble";
        const content = document.createElement("dp-content");
        content.setAttribute("data-render", "");
        content.textContent = text;
        bubble.appendChild(content);
        if (citations && citations.length) {
            const list = document.createElement("div");
            list.className = "ds-chat-citations";
            citations.forEach((cite) => {
                const link = document.createElement("a");
                link.href = cite.url;
                link.target = "_blank";
                link.rel = "noopener nofollow";
                link.textContent = `[${cite.index}] ${cite.title}`;
                list.appendChild(link);
            });
            bubble.appendChild(list);
        }
        row.appendChild(bubble);
        this._append(row);
    }

    _errorLine(text) {
        const row = document.createElement("div");
        row.className = "ds-chat-msg error";
        const bubble = document.createElement("div");
        bubble.className = "ds-chat-bubble";
        bubble.textContent = text;
        row.appendChild(bubble);
        this._append(row);
    }

    _append(node) {
        const pinned = this.log.scrollHeight - this.log.scrollTop - this.log.clientHeight < 80;
        this.log.appendChild(node);
        if (pinned) this.log.scrollTop = this.log.scrollHeight;
    }

    _autosize() {
        this.field.style.height = "auto";
        this.field.style.height = `${Math.min(this.field.scrollHeight, 160)}px`;
    }

    _setStatus(text) {
        this.statusEl.textContent = text || "";
        this.statusEl.hidden = !text;
    }
}

customElements.define("dp-deepsearch-chat", AppDeepsearchChat);
