import { FolkShape } from "./folk-shape";
import { css, html } from "./tags";
import { SpeechDictation } from "./speech-dictation";
import { findTool } from "./canvas-tools";

const styles = css`
	:host {
		background: var(--rs-bg-surface, #fff);
		color: var(--rs-text-primary, #1e293b);
		border-radius: 8px;
		box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
		min-width: 400px;
		min-height: 450px;
	}

	.header {
		display: flex;
		align-items: center;
		justify-content: space-between;
		padding: 8px 12px;
		background: linear-gradient(135deg, #0ea5e9, #6366f1);
		color: white;
		border-radius: 8px 8px 0 0;
		font-size: 12px;
		font-weight: 600;
		cursor: move;
	}

	.header-title {
		display: flex;
		align-items: center;
		gap: 6px;
	}

	.header-actions {
		display: flex;
		gap: 4px;
	}

	.header-actions button {
		background: transparent;
		border: none;
		color: white;
		cursor: pointer;
		padding: 2px 6px;
		border-radius: 4px;
		font-size: 14px;
	}

	.header-actions button:hover {
		background: rgba(255, 255, 255, 0.2);
	}

	.content {
		display: flex;
		flex-direction: column;
		height: calc(100% - 36px);
		overflow: hidden;
	}

	.messages {
		flex: 1;
		min-height: 0;
		overflow-y: auto;
		padding: 12px;
		display: flex;
		flex-direction: column;
		gap: 12px;
	}

	.message {
		max-width: 85%;
		padding: 10px 14px;
		border-radius: 12px;
		font-size: 13px;
		line-height: 1.5;
		overflow-wrap: break-word;
		word-break: break-word;
	}

	.message.user {
		align-self: flex-end;
		background: linear-gradient(135deg, #0ea5e9, #6366f1);
		color: white;
		border-bottom-right-radius: 4px;
	}

	.message.assistant {
		align-self: flex-start;
		background: var(--rs-bg-surface-sunken, #f1f5f9);
		color: var(--rs-text-primary, #1e293b);
		border-bottom-left-radius: 4px;
	}

	.message.streaming {
		background: var(--rs-bg-surface-sunken, #f1f5f9);
	}

	.message.streaming::after {
		content: "▌";
		animation: blink 1s infinite;
	}

	@keyframes blink {
		0%, 100% { opacity: 1; }
		50% { opacity: 0; }
	}

	.placeholder {
		flex: 1;
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		color: var(--rs-text-muted, #94a3b8);
		text-align: center;
		gap: 8px;
		padding: 24px;
	}

	.placeholder-icon {
		font-size: 48px;
		opacity: 0.5;
	}

	.input-area {
		padding: 12px;
		border-top: 1px solid var(--rs-border, #e2e8f0);
		display: flex;
		flex-direction: column;
		gap: 8px;
	}

	.model-select {
		padding: 6px 10px;
		border: 2px solid var(--rs-input-border, #e2e8f0);
		border-radius: 6px;
		font-size: 12px;
		background: var(--rs-input-bg, #fff);
		color: var(--rs-input-text, inherit);
		cursor: pointer;
		width: fit-content;
	}

	.prompt-row {
		display: flex;
		gap: 8px;
	}

	.prompt-input {
		flex: 1;
		padding: 10px 14px;
		border: 2px solid var(--rs-input-border, #e2e8f0);
		border-radius: 8px;
		font-size: 13px;
		resize: none;
		outline: none;
		font-family: inherit;
		background: var(--rs-input-bg, #fff);
		color: var(--rs-input-text, inherit);
	}

	.prompt-input:focus {
		border-color: #6366f1;
	}

	.send-btn {
		padding: 10px 16px;
		background: linear-gradient(135deg, #0ea5e9, #6366f1);
		color: white;
		border: none;
		border-radius: 8px;
		font-size: 16px;
		cursor: pointer;
		transition: opacity 0.2s;
	}

	.send-btn:hover {
		opacity: 0.9;
	}

	.send-btn:disabled {
		opacity: 0.5;
		cursor: not-allowed;
	}

	.mic-btn {
		padding: 10px 12px;
		background: transparent;
		border: 2px solid #e2e8f0;
		border-radius: 8px;
		font-size: 14px;
		cursor: pointer;
		transition: all 0.2s;
	}

	.mic-btn:hover {
		border-color: #6366f1;
	}

	.mic-btn.recording {
		border-color: #ef4444;
		animation: micPulse 1.5s infinite;
	}

	@keyframes micPulse {
		0%, 100% { transform: scale(1); }
		50% { transform: scale(1.15); }
	}

	.error {
		color: #ef4444;
		padding: 12px;
		background: #fef2f2;
		border-radius: 6px;
		font-size: 13px;
		margin: 12px;
	}

	.clear-btn {
		font-size: 12px;
		padding: 4px 8px;
		background: #f1f5f9;
		border: none;
		border-radius: 4px;
		cursor: pointer;
		color: #64748b;
	}

	.clear-btn:hover {
		background: #e2e8f0;
	}

	.attach-btn {
		padding: 10px 12px;
		background: transparent;
		border: 2px solid #e2e8f0;
		border-radius: 8px;
		font-size: 14px;
		cursor: pointer;
		transition: all 0.2s;
	}

	.attach-btn:hover {
		border-color: #6366f1;
	}

	.pending-images {
		display: flex;
		gap: 4px;
		flex-wrap: wrap;
	}

	.pending-thumb {
		position: relative;
		width: 48px;
		height: 48px;
		border-radius: 6px;
		overflow: hidden;
	}

	.pending-thumb img {
		width: 100%;
		height: 100%;
		object-fit: cover;
	}

	.pending-thumb .remove-img {
		position: absolute;
		top: 1px;
		right: 1px;
		width: 14px;
		height: 14px;
		background: rgba(0,0,0,0.6);
		color: white;
		border: none;
		border-radius: 50%;
		font-size: 9px;
		line-height: 14px;
		text-align: center;
		cursor: pointer;
		padding: 0;
	}

	.msg-images {
		display: flex;
		gap: 4px;
		margin-top: 6px;
	}

	.msg-images img {
		max-width: 80px;
		max-height: 80px;
		border-radius: 4px;
		object-fit: cover;
	}

	pre {
		background: #1e293b;
		color: #e2e8f0;
		padding: 12px;
		border-radius: 6px;
		overflow-x: auto;
		font-size: 12px;
		margin: 8px 0;
		max-width: 100%;
	}

	code {
		font-family: "Monaco", "Consolas", monospace;
	}

	.input-controls {
		display: flex;
		gap: 6px;
		align-items: center;
	}

	.tools-btn {
		padding: 5px 10px;
		border: 2px solid var(--rs-input-border, #e2e8f0);
		border-radius: 6px;
		font-size: 12px;
		background: var(--rs-input-bg, #fff);
		color: var(--rs-text-muted, #94a3b8);
		cursor: pointer;
		transition: all 0.2s;
		white-space: nowrap;
	}

	.tools-btn:hover {
		border-color: #6366f1;
	}

	.tools-btn.active {
		background: #6366f1;
		border-color: #6366f1;
		color: white;
	}

	.message.tool-action {
		align-self: center;
		background: #ecfdf5;
		color: #065f46;
		border-radius: 20px;
		padding: 6px 14px;
		font-size: 12px;
		font-weight: 500;
		max-width: 90%;
	}
`;

export interface ChatMessage {
	id: string;
	role: "user" | "assistant" | "tool-action";
	content: string;
	images?: string[];
	timestamp: Date;
}

declare global {
	interface HTMLElementTagNameMap {
		"folk-prompt": FolkPrompt;
	}
}

export class FolkPrompt extends FolkShape {
	static override tagName = "folk-prompt";

	static override portDescriptors = [
		{ name: "context", type: "text" as const, direction: "input" as const },
		{ name: "response", type: "text" as const, direction: "output" as const },
	];

	static {
		const sheet = new CSSStyleSheet();
		const parentRules = Array.from(FolkShape.styles.cssRules)
			.map((r) => r.cssText)
			.join("\n");
		const childRules = Array.from(styles.cssRules)
			.map((r) => r.cssText)
			.join("\n");
		sheet.replaceSync(`${parentRules}\n${childRules}`);
		this.styles = sheet;
	}

	#messages: ChatMessage[] = [];
	#isStreaming = false;
	#error: string | null = null;
	#model = "gemini-flash";
	#pendingImages: string[] = [];
	#toolsEnabled = false;
	#systemPrompt = "";

	#messagesEl: HTMLElement | null = null;
	#promptInput: HTMLTextAreaElement | null = null;
	#modelSelect: HTMLSelectElement | null = null;
	#sendBtn: HTMLButtonElement | null = null;
	#attachInput: HTMLInputElement | null = null;
	#pendingImagesEl: HTMLElement | null = null;
	#toolsBtn: HTMLButtonElement | null = null;

	get messages() {
		return this.#messages;
	}

	get toolsEnabled() { return this.#toolsEnabled; }
	set toolsEnabled(v: boolean) {
		this.#toolsEnabled = v;
		this.#toolsBtn?.classList.toggle("active", v);
		if (this.#promptInput) {
			this.#promptInput.placeholder = v
				? "Ask me to create maps, notes, images, or embeds on the canvas..."
				: "Type your message...";
		}
	}

	get systemPrompt() { return this.#systemPrompt; }
	set systemPrompt(v: string) { this.#systemPrompt = v; }

	override createRenderRoot() {
		const root = super.createRenderRoot();

		const wrapper = document.createElement("div");
		wrapper.innerHTML = html`
			<div class="header">
				<span class="header-title">
					<span>💬</span>
					<span>AI Prompt</span>
				</span>
				<div class="header-actions">
					<button class="clear-btn" title="Clear chat">Clear</button>
					<button class="close-btn" title="Close">×</button>
				</div>
			</div>
			<div class="content">
				<div class="messages">
					<div class="placeholder">
						<span class="placeholder-icon">🤖</span>
						<span>Ask me anything!</span>
						<span style="font-size: 11px;">I can help with code, writing, analysis, and more</span>
					</div>
				</div>
				<div class="input-area">
					<div class="input-controls">
						<select class="model-select">
							<option value="gemini-flash">Gemini 2.5 Flash</option>
							<option value="gemini-pro">Gemini 2.5 Pro</option>
						</select>
						<button class="tools-btn" title="Enable canvas tools — AI can create maps, notes, embeds, and more">Tools</button>
					</div>
					<div class="pending-images"></div>
					<div class="prompt-row">
						<textarea class="prompt-input" placeholder="Type your message..." rows="2"></textarea>
						<button class="attach-btn" title="Attach image">📎</button>
						<input type="file" class="attach-input" accept="image/*" multiple hidden />
						${SpeechDictation.isSupported() ? '<button class="mic-btn" title="Voice dictation">🎤</button>' : ''}
						<button class="send-btn">→</button>
					</div>
				</div>
			</div>
		`;

		// Replace the container div (slot's parent) with our wrapper
		const slot = root.querySelector("slot");
		const containerDiv = slot?.parentElement as HTMLElement;
		if (containerDiv) {
			containerDiv.replaceWith(wrapper);
		}

		this.#messagesEl = wrapper.querySelector(".messages");
		this.#promptInput = wrapper.querySelector(".prompt-input");
		this.#modelSelect = wrapper.querySelector(".model-select");
		this.#sendBtn = wrapper.querySelector(".send-btn");
		this.#attachInput = wrapper.querySelector(".attach-input");
		this.#pendingImagesEl = wrapper.querySelector(".pending-images");
		this.#toolsBtn = wrapper.querySelector(".tools-btn") as HTMLButtonElement;
		const clearBtn = wrapper.querySelector(".clear-btn") as HTMLButtonElement;
		const closeBtn = wrapper.querySelector(".close-btn") as HTMLButtonElement;
		const attachBtn = wrapper.querySelector(".attach-btn") as HTMLButtonElement | null;

		// Tools toggle
		this.#toolsBtn?.addEventListener("click", (e) => {
			e.stopPropagation();
			this.toolsEnabled = !this.#toolsEnabled;
		});

		// Sync initial state after DOM ready
		this.#toolsBtn?.classList.toggle("active", this.#toolsEnabled);
		if (this.#toolsEnabled && this.#promptInput) {
			this.#promptInput.placeholder = "Ask me to create maps, notes, images, or embeds on the canvas...";
		}

		// Attach button
		attachBtn?.addEventListener("click", (e) => {
			e.stopPropagation();
			this.#attachInput?.click();
		});
		this.#attachInput?.addEventListener("change", () => {
			if (!this.#attachInput?.files) return;
			for (const file of Array.from(this.#attachInput.files)) {
				if (!file.type.startsWith("image/")) continue;
				const reader = new FileReader();
				reader.onload = () => {
					this.#pendingImages.push(reader.result as string);
					this.#renderPendingImages();
				};
				reader.readAsDataURL(file);
			}
			this.#attachInput.value = "";
		});

		// Send button
		this.#sendBtn?.addEventListener("click", (e) => {
			e.stopPropagation();
			this.#send();
		});

		// Enter key (without shift)
		this.#promptInput?.addEventListener("keydown", (e) => {
			if (e.key === "Enter" && !e.shiftKey) {
				e.preventDefault();
				this.#send();
			}
		});

		// Model select
		this.#modelSelect?.addEventListener("change", () => {
			this.#model = this.#modelSelect?.value || "gemini-flash";
		});

		// Clear button
		clearBtn.addEventListener("click", (e) => {
			e.stopPropagation();
			this.#clearChat();
		});

		// Prevent canvas drag when interacting with chat content
		this.#messagesEl?.addEventListener("pointerdown", (e) => e.stopPropagation());
		this.#promptInput?.addEventListener("pointerdown", (e) => e.stopPropagation());

		// Voice dictation
		const micBtn = wrapper.querySelector(".mic-btn") as HTMLButtonElement | null;
		if (micBtn) {
			let baseText = "";
			let interimText = "";
			const dictation = new SpeechDictation({
				onInterim: (text) => {
					interimText = text;
					if (this.#promptInput) {
						this.#promptInput.value = baseText + (baseText ? " " : "") + text;
					}
				},
				onFinal: (text) => {
					interimText = "";
					baseText += (baseText ? " " : "") + text;
					if (this.#promptInput) {
						this.#promptInput.value = baseText;
					}
				},
				onStateChange: (recording) => {
					micBtn.classList.toggle("recording", recording);
					if (!recording) {
						baseText = this.#promptInput?.value || "";
						interimText = "";
					}
				},
				onError: (err) => console.warn("Prompt dictation:", err),
			});

			micBtn.addEventListener("click", (e) => {
				e.stopPropagation();
				if (!dictation.isRecording) {
					baseText = this.#promptInput?.value || "";
				}
				dictation.toggle();
				this.#promptInput?.focus();
			});
		}

		// Close button
		closeBtn.addEventListener("click", (e) => {
			e.stopPropagation();
			this.dispatchEvent(new CustomEvent("close"));
		});

		return root;
	}

	#renderPendingImages() {
		if (!this.#pendingImagesEl) return;
		if (!this.#pendingImages.length) {
			this.#pendingImagesEl.innerHTML = "";
			return;
		}
		this.#pendingImagesEl.innerHTML = this.#pendingImages
			.map(
				(img, i) => `<div class="pending-thumb">
				<img src="${this.#escapeHtml(img)}" />
				<button class="remove-img" data-img-index="${i}">×</button>
			</div>`
			)
			.join("");
		this.#pendingImagesEl.querySelectorAll(".remove-img").forEach((btn) => {
			btn.addEventListener("click", (e) => {
				e.stopPropagation();
				const idx = parseInt((btn as HTMLElement).dataset.imgIndex || "0", 10);
				this.#pendingImages.splice(idx, 1);
				this.#renderPendingImages();
			});
		});
	}

	async #send() {
		const content = this.#promptInput?.value.trim();
		if (!content || this.#isStreaming) return;

		// Add user message with any pending images (cap at 30 to prevent unbounded growth with base64 images)
		const userMessage: ChatMessage = {
			id: crypto.randomUUID(),
			role: "user",
			content,
			images: this.#pendingImages.length ? [...this.#pendingImages] : undefined,
			timestamp: new Date(),
		};
		this.#messages.push(userMessage);
		if (this.#messages.length > 30) this.#messages = this.#messages.slice(-30);

		// Clear input and pending images
		if (this.#promptInput) this.#promptInput.value = "";
		this.#pendingImages = [];
		this.#renderPendingImages();

		this.#isStreaming = true;
		this.#error = null;
		if (this.#sendBtn) this.#sendBtn.disabled = true;
		this.#renderMessages(true);

		try {
			const useTools = this.#toolsEnabled && this.#model.startsWith("gemini");
			const response = await fetch("/api/prompt", {
				method: "POST",
				headers: { "Content-Type": "application/json" },
				body: JSON.stringify({
					messages: this.#messages.filter((m) => m.role !== "tool-action").map((m) => ({
						role: m.role,
						content: m.content,
						...(m.images?.length ? { images: m.images } : {}),
					})),
					model: this.#model,
					...(useTools ? { useTools: true, systemPrompt: this.#systemPrompt || undefined } : {}),
					...(useTools && (window as any).__rspaceEnabledModules ? { enabledModules: (window as any).__rspaceEnabledModules } : {}),
				}),
			});

			if (!response.ok) {
				throw new Error(`Request failed: ${response.statusText}`);
			}

			const result = await response.json();

			// Execute tool calls if any
			if (result.toolCalls?.length) {
				this.#executeToolCalls(result.toolCalls);
			}

			const assistantMessage: ChatMessage = {
				id: crypto.randomUUID(),
				role: "assistant",
				content: result.content || result.text || result.message,
				timestamp: new Date(),
			};
			this.#messages.push(assistantMessage);

			this.dispatchEvent(
				new CustomEvent("message", { detail: { message: assistantMessage } })
			);
		} catch (error) {
			this.#error = error instanceof Error ? error.message : "Request failed";
			// Remove the user message if we failed
			this.#messages.pop();
		} finally {
			this.#isStreaming = false;
			if (this.#sendBtn) this.#sendBtn.disabled = false;
			this.#renderMessages(false);
		}
	}

	#executeToolCalls(toolCalls: { name: string; args: Record<string, any>; label: string }[]) {
		const api = (window as any).__canvasApi;
		if (!api) {
			console.warn("[folk-prompt] Canvas API not available — cannot spawn shapes");
			return;
		}

		let offsetY = 0;
		for (const tc of toolCalls) {
			const tool = findTool(tc.name);
			if (!tool) continue;

			const props = tool.buildProps(tc.args);
			const defaults = api.SHAPE_DEFAULTS?.[tool.tagName] || { width: 300, height: 200 };
			const preferX = this.x + this.width + 40 + defaults.width / 2;
			const preferY = this.y + offsetY + defaults.height / 2;
			const pos = api.findFreePosition(defaults.width, defaults.height, preferX, preferY, this);

			try {
				api.newShape(tool.tagName, props, { x: pos.x + defaults.width / 2, y: pos.y + defaults.height / 2 });
			} catch (e) {
				console.error(`[folk-prompt] Failed to create ${tool.tagName}:`, e);
			}

			// Tool action card in chat
			this.#messages.push({
				id: crypto.randomUUID(),
				role: "tool-action",
				content: tc.label,
				timestamp: new Date(),
			});

			offsetY += defaults.height + 20;
		}
	}

	#clearChat() {
		this.#messages = [];
		this.#error = null;
		this.#renderMessages(false);
	}

	#renderMessages(streaming: boolean) {
		if (!this.#messagesEl) return;

		if (this.#messages.length === 0 && !this.#error) {
			this.#messagesEl.innerHTML = `
				<div class="placeholder">
					<span class="placeholder-icon">🤖</span>
					<span>Ask me anything!</span>
					<span style="font-size: 11px;">I can help with code, writing, analysis, and more</span>
				</div>
			`;
			return;
		}

		let messagesHtml = this.#messages
			.map((msg) => {
				if (msg.role === "tool-action") {
					return `<div class="message tool-action">${this.#escapeHtml(msg.content)}</div>`;
				}
				let imgHtml = "";
				if (msg.images?.length) {
					imgHtml = `<div class="msg-images">${msg.images.map((src) => `<img src="${this.#escapeHtml(src)}" />`).join("")}</div>`;
				}
				return `<div class="message ${msg.role}">${this.#formatContent(msg.content)}${imgHtml}</div>`;
			})
			.join("");

		if (streaming) {
			messagesHtml += '<div class="message assistant streaming"></div>';
		}

		if (this.#error) {
			messagesHtml += `<div class="error">${this.#escapeHtml(this.#error)}</div>`;
		}

		this.#messagesEl.innerHTML = messagesHtml;
		this.#messagesEl.scrollTop = this.#messagesEl.scrollHeight;
	}

	#formatContent(content: string): string {
		// Simple markdown-like formatting
		let html = this.#escapeHtml(content);

		// Code blocks
		html = html.replace(/```(\w*)\n([\s\S]*?)```/g, "<pre><code>$2</code></pre>");

		// Inline code
		html = html.replace(/`([^`]+)`/g, "<code>$1</code>");

		// Bold
		html = html.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");

		// Line breaks
		html = html.replace(/\n/g, "<br>");

		return html;
	}

	#escapeHtml(text: string): string {
		const div = document.createElement("div");
		div.textContent = text;
		return div.innerHTML;
	}

	static override fromData(data: Record<string, any>): FolkPrompt {
		const shape = FolkShape.fromData(data) as FolkPrompt;
		if (data.toolsEnabled) shape.toolsEnabled = true;
		if (data.systemPrompt) shape.systemPrompt = data.systemPrompt;
		return shape;
	}

	override toJSON() {
		return {
			...super.toJSON(),
			type: "folk-prompt",
			model: this.#model,
			toolsEnabled: this.#toolsEnabled || undefined,
			systemPrompt: this.#systemPrompt || undefined,
			messages: this.messages.filter((m) => m.role !== "tool-action").map((msg) => ({
				role: msg.role,
				content: msg.content,
				...(msg.images?.length ? { images: msg.images } : {}),
				timestamp: msg.timestamp.toISOString(),
			})),
		};
	}

	override applyData(data: Record<string, any>): void {
		super.applyData(data);
		if (data.toolsEnabled !== undefined) this.toolsEnabled = !!data.toolsEnabled;
		if (data.systemPrompt !== undefined) this.systemPrompt = data.systemPrompt;
	}
}
