/**
 * <rstack-mi> — AI-powered assistant embedded in the rSpace header.
 *
 * Renders a search input ("Ask mi anything..."). On submit, queries
 * /api/mi/ask and streams the response into a dropdown panel.
 * Supports multi-turn conversation with context.
 */

import { getAccessToken } from "./rstack-identity";
import { parseMiActions, summariseActions } from "../../lib/mi-actions";
import { MiActionExecutor } from "../../lib/mi-action-executor";
import { suggestTools, type ToolHint } from "../../lib/mi-tool-schema";

interface MiMessage {
	role: "user" | "assistant";
	content: string;
	actionSummary?: string;
	toolHints?: ToolHint[];
}

export class RStackMi extends HTMLElement {
	#shadow: ShadowRoot;
	#messages: MiMessage[] = [];
	#abortController: AbortController | null = null;

	constructor() {
		super();
		this.#shadow = this.attachShadow({ mode: "open" });
	}

	connectedCallback() {
		this.#render();
	}

	#render() {
		this.#shadow.innerHTML = `
			<style>${STYLES}</style>
			<div class="mi">
				<div class="mi-bar" id="mi-bar">
					<span class="mi-icon">&#10023;</span>
					<input class="mi-input" id="mi-input" type="text"
						placeholder="Ask mi anything — setup, navigation, what's possible..." autocomplete="off" />
				</div>
				<div class="mi-panel" id="mi-panel">
					<div class="mi-messages" id="mi-messages">
						<div class="mi-welcome">
							<span class="mi-welcome-icon">&#10023;</span>
							<p>Hi, I'm <strong>mi</strong> — your mycelial intelligence guide.</p>
							<p class="mi-welcome-sub">I see everything you have open and can help with setup, navigation, connecting rApps, or anything else.</p>
						</div>
					</div>
				</div>
			</div>
		`;

		const input = this.#shadow.getElementById("mi-input") as HTMLInputElement;
		const panel = this.#shadow.getElementById("mi-panel")!;
		const bar = this.#shadow.getElementById("mi-bar")!;

		input.addEventListener("focus", () => {
			panel.classList.add("open");
			bar.classList.add("focused");
		});

		input.addEventListener("keydown", (e) => {
			if (e.key === "Enter" && input.value.trim()) {
				this.#ask(input.value.trim());
				input.value = "";
			}
			if (e.key === "Escape") {
				panel.classList.remove("open");
				bar.classList.remove("focused");
				input.blur();
			}
		});

		document.addEventListener("click", (e) => {
			if (!this.contains(e.target as Node)) {
				panel.classList.remove("open");
				bar.classList.remove("focused");
			}
		});

		// Stop clicks inside the panel from closing it
		panel.addEventListener("click", (e) => e.stopPropagation());
		bar.addEventListener("click", (e) => e.stopPropagation());
	}

	/** Gather page context: open shapes, active module, tabs, canvas state. */
	#gatherContext(): Record<string, any> {
		const ctx: Record<string, any> = {};

		// Current space and module
		ctx.space = document.querySelector("rstack-space-switcher")?.getAttribute("current") || "";
		ctx.module = document.querySelector("rstack-app-switcher")?.getAttribute("current") || "";

		// Deep canvas context from MI bridge (if available)
		const bridge = (window as any).__miCanvasBridge;
		if (bridge) {
			const cc = bridge.getCanvasContext();
			ctx.openShapes = cc.allShapes.slice(0, 20).map((s: any) => ({
				type: s.type,
				id: s.id,
				x: Math.round(s.x),
				y: Math.round(s.y),
				width: Math.round(s.width),
				height: Math.round(s.height),
				...(s.content ? { snippet: s.content.slice(0, 80) } : {}),
				...(s.title ? { title: s.title } : {}),
			}));
			if (cc.selectedShapes.length) {
				ctx.selectedShapes = cc.selectedShapes.map((s: any) => ({
					type: s.type,
					id: s.id,
					x: Math.round(s.x),
					y: Math.round(s.y),
					...(s.content ? { snippet: s.content.slice(0, 80) } : {}),
					...(s.title ? { title: s.title } : {}),
				}));
			}
			if (cc.connections.length) ctx.connections = cc.connections;
			ctx.viewport = cc.viewport;
			if (cc.shapeGroups.length) ctx.shapeGroups = cc.shapeGroups;
			ctx.shapeCountByType = cc.shapeCountByType;
		} else {
			// Fallback: basic shape list from DOM
			const canvasContent = document.getElementById("canvas-content");
			if (canvasContent) {
				const shapes = [...canvasContent.children]
					.filter((el) => el.tagName?.includes("-") && el.id)
					.map((el: any) => ({
						type: el.tagName.toLowerCase(),
						id: el.id,
						...(el.content ? { snippet: el.content.slice(0, 60) } : {}),
						...(el.title ? { title: el.title } : {}),
					}))
					.slice(0, 20);
				if (shapes.length) ctx.openShapes = shapes;
			}
		}

		// Active tab/layer info
		const tabBar = document.querySelector("rstack-tab-bar");
		if (tabBar) {
			const active = tabBar.getAttribute("active") || "";
			ctx.activeTab = active;
		}

		// Page title
		ctx.pageTitle = document.title;

		return ctx;
	}

	async #ask(query: string) {
		const panel = this.#shadow.getElementById("mi-panel")!;
		const messagesEl = this.#shadow.getElementById("mi-messages")!;

		panel.classList.add("open");
		this.#shadow.getElementById("mi-bar")!.classList.add("focused");

		// Add user message
		this.#messages.push({ role: "user", content: query });
		this.#renderMessages(messagesEl);

		// Add placeholder for assistant
		this.#messages.push({ role: "assistant", content: "" });
		const assistantIdx = this.#messages.length - 1;
		this.#renderMessages(messagesEl);

		// Abort previous
		this.#abortController?.abort();
		this.#abortController = new AbortController();

		try {
			const token = getAccessToken();
			const context = this.#gatherContext();
			const res = await fetch("/api/mi/ask", {
				method: "POST",
				headers: {
					"Content-Type": "application/json",
					...(token ? { Authorization: `Bearer ${token}` } : {}),
				},
				body: JSON.stringify({
					query,
					messages: this.#messages.slice(0, -1).slice(-10),
					space: context.space,
					module: context.module,
					context,
				}),
				signal: this.#abortController.signal,
			});

			if (!res.ok) {
				const err = await res.json().catch(() => ({ error: "Request failed" }));
				throw new Error(err.error || "Request failed");
			}

			if (!res.body) throw new Error("No response stream");

			const reader = res.body.getReader();
			const decoder = new TextDecoder();

			while (true) {
				const { done, value } = await reader.read();
				if (done) break;

				const chunk = decoder.decode(value, { stream: true });
				for (const line of chunk.split("\n").filter(Boolean)) {
					try {
						const data = JSON.parse(line);
						if (data.message?.content) {
							this.#messages[assistantIdx].content += data.message.content;
						}
						// Non-streaming fallback
						if (data.response) {
							this.#messages[assistantIdx].content = data.response;
						}
					} catch { /* skip malformed lines */ }
				}
				this.#renderMessages(messagesEl);
			}

			// If still empty after stream, show fallback
			if (!this.#messages[assistantIdx].content) {
				this.#messages[assistantIdx].content = "I couldn't generate a response. Please try again.";
				this.#renderMessages(messagesEl);
			} else {
				// Parse and execute MI actions from the response
				const rawText = this.#messages[assistantIdx].content;
				const { displayText, actions } = parseMiActions(rawText);
				this.#messages[assistantIdx].content = displayText;

				if (actions.length) {
					const executor = new MiActionExecutor();
					executor.execute(actions);
					this.#messages[assistantIdx].actionSummary = summariseActions(actions);
				}

				// Check for tool suggestions
				const hints = suggestTools(query);
				if (hints.length) {
					this.#messages[assistantIdx].toolHints = hints;
				}

				this.#renderMessages(messagesEl);
			}
		} catch (e: any) {
			if (e.name !== "AbortError") {
				this.#messages[this.#messages.length - 1].content =
					"Sorry, I'm not available right now. Please try again later.";
				this.#renderMessages(messagesEl);
			}
		}
	}

	#renderMessages(container: HTMLElement) {
		container.innerHTML = this.#messages
			.map(
				(m) => `
			<div class="mi-msg mi-msg--${m.role}">
				<span class="mi-msg-who">${m.role === "user" ? "You" : "&#10023; mi"}</span>
				<div class="mi-msg-body">${m.content ? this.#formatContent(m.content) : '<span class="mi-typing"><span></span><span></span><span></span></span>'}</div>
				${m.actionSummary ? `<div class="mi-action-chip">${this.#escapeHtml(m.actionSummary)}</div>` : ""}
				${m.toolHints?.length ? `<div class="mi-tool-chips">${m.toolHints.map((h) => `<button class="mi-tool-chip" data-tag="${h.tagName}">${h.icon} ${this.#escapeHtml(h.label)}</button>`).join("")}</div>` : ""}
			</div>
		`,
			)
			.join("");

		// Wire tool chip clicks
		container.querySelectorAll<HTMLButtonElement>(".mi-tool-chip").forEach((btn) => {
			btn.addEventListener("click", () => {
				const tag = btn.dataset.tag;
				if (!tag) return;
				const executor = new MiActionExecutor();
				executor.execute([{ type: "create-shape", tagName: tag, props: {} }]);
			});
		});

		container.scrollTop = container.scrollHeight;
	}

	#escapeHtml(s: string): string {
		return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
	}

	#formatContent(s: string): string {
		// Escape HTML then convert markdown-like formatting
		return s
			.replace(/&/g, "&amp;")
			.replace(/</g, "&lt;")
			.replace(/>/g, "&gt;")
			.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>")
			.replace(/`(.+?)`/g, '<code class="mi-code">$1</code>')
			.replace(/\n/g, "<br>");
	}

	static define(tag = "rstack-mi") {
		if (!customElements.get(tag)) customElements.define(tag, RStackMi);
	}
}

const STYLES = `
:host { display: contents; }

.mi { position: relative; flex: 1; max-width: 480px; min-width: 0; }

.mi-bar {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 14px; border-radius: 10px;
  transition: all 0.2s;
  background: var(--rs-btn-secondary-bg);
}
.mi-bar.focused { background: var(--rs-bg-hover); box-shadow: 0 0 0 1px rgba(6,182,212,0.3); }

.mi-icon {
  font-size: 0.9rem; flex-shrink: 0;
  background: linear-gradient(135deg, #06b6d4, #7c3aed);
  -webkit-background-clip: text; -webkit-text-fill-color: transparent;
}

.mi-input {
  flex: 1; border: none; outline: none; background: none;
  font-size: 0.85rem; min-width: 0;
  font-family: inherit;
  color: var(--rs-text-primary);
}
.mi-input::placeholder { color: var(--rs-text-muted); }

.mi-panel {
  position: absolute; top: calc(100% + 8px); left: 0; right: 0;
  min-width: 360px; max-height: 420px;
  border-radius: 14px; overflow: hidden;
  box-shadow: 0 12px 40px rgba(0,0,0,0.3);
  display: none; z-index: 300;
  background: var(--rs-bg-surface); border: 1px solid var(--rs-border);
}
.mi-panel.open { display: flex; flex-direction: column; }

.mi-messages {
  flex: 1; overflow-y: auto; padding: 16px;
  display: flex; flex-direction: column; gap: 12px;
  max-height: 400px;
}

.mi-welcome { text-align: center; padding: 24px 16px; }
.mi-welcome-icon {
  font-size: 2rem; display: block; margin-bottom: 8px;
  background: linear-gradient(135deg, #06b6d4, #7c3aed);
  -webkit-background-clip: text; -webkit-text-fill-color: transparent;
}
.mi-welcome p { font-size: 0.9rem; line-height: 1.5; margin: 0; color: var(--rs-text-primary); }
.mi-welcome-sub { font-size: 0.8rem; opacity: 0.6; margin-top: 6px !important; }

.mi-msg { display: flex; flex-direction: column; gap: 4px; }
.mi-msg-who { font-size: 0.7rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; }
.mi-msg--user .mi-msg-who { color: #06b6d4; }
.mi-msg--assistant .mi-msg-who {
  background: linear-gradient(135deg, #06b6d4, #7c3aed);
  -webkit-background-clip: text; -webkit-text-fill-color: transparent;
}

.mi-msg-body {
  font-size: 0.85rem; line-height: 1.6; word-break: break-word;
  color: var(--rs-text-secondary);
}
.mi-msg--user .mi-msg-body { color: var(--rs-text-primary); }

.mi-code {
  padding: 1px 5px; border-radius: 4px; font-size: 0.8rem;
  font-family: 'SF Mono', Monaco, Consolas, monospace;
  background: var(--rs-btn-secondary-bg); color: #0ea5e9;
}

.mi-typing { display: inline-flex; gap: 4px; padding: 4px 0; }
.mi-typing span {
  width: 6px; height: 6px; border-radius: 50%;
  animation: miBounce 1.2s ease-in-out infinite;
  background: var(--rs-text-muted);
}
.mi-typing span:nth-child(2) { animation-delay: 0.15s; }
.mi-typing span:nth-child(3) { animation-delay: 0.3s; }
@keyframes miBounce {
  0%, 60%, 100% { transform: translateY(0); }
  30% { transform: translateY(-4px); }
}

.mi-action-chip {
  display: inline-block; margin-top: 6px; padding: 3px 10px;
  border-radius: 12px; font-size: 0.75rem; font-weight: 600;
  background: rgba(6,182,212,0.12); color: #06b6d4;
}

.mi-tool-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.mi-tool-chip {
  padding: 4px 10px; border-radius: 8px; border: none;
  font-size: 0.75rem; cursor: pointer; transition: background 0.15s;
  font-family: inherit;
  background: var(--rs-btn-secondary-bg); color: var(--rs-text-primary);
}
.mi-tool-chip:hover { background: var(--rs-bg-hover); }

@media (max-width: 640px) {
  .mi { max-width: none; width: 100%; }
  .mi-panel { min-width: 0; width: 100%; left: 0; right: 0; }
}
`;
