////
// The scrollback shows your history of interacting with the shell.
// input, output, etc

import type { Message, CommandOutput, CommandResult } from "../shared/types"
import { scrollback, cmdInput, $$ } from "./dom"
import { randomId } from "../shared/utils"

type InputStatus = "waiting" | "streaming" | "ok" | "error"
const statusColors = {
    waiting: "yellow",
    streaming: "purple",
    ok: "green",
    error: "red"
}

export function initScrollback() {
    window.addEventListener("click", handleInputClick)
}

export function insert(node: HTMLElement) {
    scrollback.append(node)
}

export function latestId(): string | undefined {
    const nodes = document.querySelectorAll("[data-id]")
    if (nodes.length)
        return (nodes[nodes.length - 1] as HTMLElement).dataset.id
}

export function addInput(id: string, input: string, status?: InputStatus) {
    const parent = $$("li.input")
    const statusSpan = $$(`span.status.${statusColors[status || "waiting"]}`, "•")
    const content = $$("span.content", input)
    parent.append(statusSpan, content)
    parent.dataset.id = id

    insert(parent)
    scrollback.scrollTop = scrollback.scrollHeight - scrollback.clientHeight
}

export function setStatus(id: string, status: InputStatus) {
    const statusEl = document.querySelector(`[data-id="${id}"].input .status`)
    if (!statusEl) return

    statusEl.classList.remove(...Object.values(statusColors))
    statusEl.classList.add(statusColors[status])
}

export function addOutput(id: string, output: CommandOutput) {
    const item = $$("li")
    item.classList.add("output")
    item.dataset.id = id || randomId()

    const [format, content] = processOutput(output)

    if (format === "html")
        item.innerHTML = content
    else
        item.textContent = content

    const input = document.querySelector(`[data-id="${id}"].input`)
    if (input instanceof HTMLLIElement) {
        input.parentNode!.insertBefore(item, input.nextSibling)
    } else {
        insert(item)
    }
}

export function addErrorMessage(message: string) {
    addOutput("", { html: `<span class="red">${message}</span>` })
}

export function appendOutput(id: string, output: CommandOutput) {
    const item = document.querySelector(`[data-id="${id}"].output`)

    if (!item) {
        console.error(`output id ${id} not found`)
        return
    }

    const [format, content] = processOutput(output)

    if (format === "html")
        item.innerHTML += content
    else
        item.textContent += content
}

export function replaceOutput(id: string, output: CommandOutput) {
    const item = document.querySelector(`[data-id="${id}"].output`)

    if (!item) {
        console.error(`output id ${id} not found`)
        return
    }

    const [format, content] = processOutput(output)

    if (format === "html")
        item.innerHTML = content
    else
        item.textContent = content
}

function processOutput(output: CommandOutput): ["html" | "text", string] {
    let content = ""
    let html = false

    if (typeof output === "string") {
        content = output
    } else if (Array.isArray(output)) {
        content = output.join(" ")
    } else if (typeof output === "object" && "html" in output) {
        html = true
        content = output.html
        if (output.script) eval(output.script)
    } else if (typeof output === "object" && "text" in output) {
        content = output.text
        if (output.script) eval(output.script)
    } else if (typeof output === "object" && "script" in output) {
        eval(output.script!)
    } else {
        content = JSON.stringify(output)
    }

    return [html ? "html" : "text", content]
}

function handleInputClick(e: MouseEvent) {
    const target = e.target

    if (!(target instanceof HTMLElement)) return

    if (target.matches(".input .content")) {
        cmdInput.value = target.textContent
    }
}

export function handleOutput(msg: Message) {
    const result = msg.data as CommandResult
    const id = "id" in msg ? msg.id || "" : ""
    setStatus(id, result.status)
    addOutput(id, result.output)
}
