import { FolkShape } from "./folk-shape";
import { css, html } from "./tags";

const styles = css`
	:host {
		display: flex !important;
		flex-direction: column;
		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: 350px;
		min-height: 400px;
		overflow: hidden;
	}

	.note-wrapper {
		display: flex;
		flex-direction: column;
		height: 100%;
		width: 100%;
		min-height: 0;
	}

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

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

	.note-title {
		background: transparent;
		border: none;
		color: white;
		font-size: 12px;
		font-weight: 600;
		outline: none;
		min-width: 60px;
		max-width: 200px;
		flex: 1;
	}

	.note-title::placeholder {
		color: rgba(255, 255, 255, 0.7);
	}

	.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;
		flex: 1;
		min-height: 0;
		overflow: hidden;
	}

	.toolbar {
		display: flex;
		align-items: center;
		gap: 4px;
		padding: 8px 12px;
		border-bottom: 1px solid var(--rs-border, rgba(255, 255, 255, 0.1));
		flex-wrap: wrap;
		flex-shrink: 0;
	}

	.toolbar-btn {
		padding: 4px 8px;
		border: none;
		border-radius: 4px;
		background: var(--rs-bg-surface-raised, #334155);
		cursor: pointer;
		font-size: 13px;
		transition: all 0.2s;
	}

	.toolbar-btn:hover {
		background: var(--rs-bg-hover, rgba(255, 255, 255, 0.05));
	}

	.toolbar-btn.active {
		background: #7c3aed;
		color: white;
	}

	.toolbar-divider {
		width: 1px;
		height: 20px;
		background: var(--rs-border, rgba(255, 255, 255, 0.1));
		margin: 0 4px;
	}

	.mode-toggle {
		margin-left: auto;
		display: flex;
		gap: 2px;
		background: var(--rs-bg-surface-sunken, #0f172a);
		border-radius: 6px;
		padding: 2px;
	}

	.mode-btn {
		padding: 4px 10px;
		border: none;
		border-radius: 4px;
		background: transparent;
		cursor: pointer;
		font-size: 11px;
		font-weight: 500;
		color: var(--rs-text-muted, #64748b);
		transition: all 0.2s;
	}

	.mode-btn.active {
		background: var(--rs-bg-surface, #1e293b);
		color: var(--rs-text-primary, #e2e8f0);
		box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
	}

	.editor-container {
		flex: 1;
		display: flex;
		overflow: hidden;
		min-height: 0;
	}

	.editor {
		flex: 1;
		padding: 12px 16px;
		border: none;
		outline: none;
		resize: none;
		font-family: "Monaco", "Consolas", "Courier New", monospace;
		font-size: 13px;
		line-height: 1.6;
		background: var(--rs-bg-surface-sunken, #0f172a);
		min-height: 0;
		overflow-y: auto;
	}

	.preview {
		flex: 1;
		padding: 12px 16px;
		overflow-y: auto;
		font-size: 14px;
		line-height: 1.7;
		display: none;
		min-height: 0;
	}

	.preview.visible {
		display: block;
	}

	.preview h1 {
		font-size: 1.5em;
		margin: 0 0 0.5em;
		border-bottom: 1px solid var(--rs-border, rgba(255, 255, 255, 0.1));
		padding-bottom: 0.3em;
	}

	.preview h2 {
		font-size: 1.3em;
		margin: 1em 0 0.5em;
	}

	.preview h3 {
		font-size: 1.1em;
		margin: 1em 0 0.5em;
	}

	.preview p {
		margin: 0.5em 0;
	}

	.preview code {
		background: var(--rs-bg-surface-raised, #334155);
		padding: 2px 6px;
		border-radius: 4px;
		font-family: "Monaco", "Consolas", monospace;
		font-size: 0.9em;
	}

	.preview pre {
		background: #1e293b;
		color: #e2e8f0;
		padding: 12px 16px;
		border-radius: 6px;
		overflow-x: auto;
	}

	.preview pre code {
		background: none;
		padding: 0;
	}

	.preview blockquote {
		border-left: 4px solid #7c3aed;
		margin: 0.5em 0;
		padding: 0.5em 1em;
		background: rgba(124, 58, 237, 0.1);
		color: var(--rs-text-primary, #e2e8f0);
	}

	.preview ul, .preview ol {
		margin: 0.5em 0;
		padding-left: 1.5em;
	}

	.preview li {
		margin: 0.25em 0;
	}

	.preview a {
		color: #7c3aed;
		text-decoration: none;
	}

	.preview a:hover {
		text-decoration: underline;
	}

	.preview hr {
		border: none;
		border-top: 1px solid #e2e8f0;
		margin: 1em 0;
	}

	.footer {
		display: flex;
		align-items: center;
		justify-content: space-between;
		padding: 6px 12px;
		border-top: 1px solid var(--rs-border, rgba(255, 255, 255, 0.1));
		font-size: 11px;
		color: #64748b;
		flex-shrink: 0;
	}

	.word-count {
		display: flex;
		gap: 12px;
	}

	.save-status {
		display: flex;
		align-items: center;
		gap: 4px;
	}

	.save-status.saved {
		color: #10b981;
	}

	.save-status.unsaved {
		color: #f59e0b;
	}

	/* ── Semantic zoom levels ── */

	/* Icon-only mode: zoomed far out */
	.zoom-icon {
		display: none;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		height: 100%;
		gap: 8px;
		padding: 12px;
		text-align: center;
	}

	.zoom-icon-emoji {
		font-size: 48px;
		line-height: 1;
	}

	.zoom-icon-title {
		font-size: 13px;
		font-weight: 600;
		color: var(--rs-text-primary, #e2e8f0);
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
		max-width: 100%;
	}

	/* Summary mode: medium zoom */
	.zoom-summary {
		display: none;
		flex-direction: column;
		height: 100%;
	}

	.zoom-summary-header {
		display: flex;
		align-items: center;
		gap: 8px;
		padding: 10px 14px;
		background: linear-gradient(135deg, #7c3aed, #8b5cf6);
		color: white;
		border-radius: 8px 8px 0 0;
		font-size: 13px;
		font-weight: 600;
		flex-shrink: 0;
	}

	.zoom-summary-body {
		flex: 1;
		padding: 12px 14px;
		font-size: 13px;
		line-height: 1.6;
		color: var(--rs-text-secondary, #94a3b8);
		overflow: hidden;
	}

	/* Zoom state classes applied by JS */
	:host(.zoom-level-icon) .note-wrapper { display: none; }
	:host(.zoom-level-icon) .zoom-icon { display: flex; }
	:host(.zoom-level-icon) .zoom-summary { display: none; }

	:host(.zoom-level-summary) .note-wrapper { display: none; }
	:host(.zoom-level-summary) .zoom-icon { display: none; }
	:host(.zoom-level-summary) .zoom-summary { display: flex; }

	:host(.zoom-level-full) .note-wrapper { display: flex; }
	:host(.zoom-level-full) .zoom-icon { display: none; }
	:host(.zoom-level-full) .zoom-summary { display: none; }
`;

declare global {
	interface HTMLElementTagNameMap {
		"folk-obs-note": FolkObsNote;
	}
}

export class FolkObsNote extends FolkShape {
	static override tagName = "folk-obs-note";

	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;
	}

	#content = "";
	#title = "Untitled";
	#mode: "edit" | "preview" | "split" = "edit";
	#isDirty = false;
	#lastSaved: Date | null = null;
	#zoomLevel: "icon" | "summary" | "full" = "full";
	#zoomRaf = 0;

	#editor: HTMLTextAreaElement | null = null;
	#preview: HTMLElement | null = null;
	#titleInput: HTMLInputElement | null = null;
	#wordCountEl: HTMLElement | null = null;
	#saveStatusEl: HTMLElement | null = null;
	#iconTitleEl: HTMLElement | null = null;
	#summaryTitleEl: HTMLElement | null = null;
	#summaryBodyEl: HTMLElement | null = null;

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

	set content(value: string) {
		this.#content = value;
		if (this.#editor) this.#editor.value = value;
		this.#updatePreview();
		this.#updateWordCount();
		this.#updateZoomContent();
	}

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

	set title(value: string) {
		this.#title = value;
		if (this.#titleInput) this.#titleInput.value = value;
		this.#updateZoomContent();
	}

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

		const wrapper = document.createElement("div");
		wrapper.className = "note-wrapper";
		wrapper.innerHTML = html`
			<div class="header">
				<span class="header-title">
					<span>📝</span>
					<input type="text" class="note-title" placeholder="Note title..." />
				</span>
				<div class="header-actions">
					<button class="save-btn" title="Save">💾</button>
					<button class="close-btn" title="Close">×</button>
				</div>
			</div>
			<div class="content">
				<div class="toolbar">
					<button class="toolbar-btn" data-action="heading" title="Heading">#</button>
					<button class="toolbar-btn" data-action="bold" title="Bold">B</button>
					<button class="toolbar-btn" data-action="italic" title="Italic">I</button>
					<button class="toolbar-btn" data-action="code" title="Code">&lt;/&gt;</button>
					<span class="toolbar-divider"></span>
					<button class="toolbar-btn" data-action="link" title="Link">🔗</button>
					<button class="toolbar-btn" data-action="list" title="List">•</button>
					<button class="toolbar-btn" data-action="quote" title="Quote">"</button>
					<div class="mode-toggle">
						<button class="mode-btn active" data-mode="edit">Edit</button>
						<button class="mode-btn" data-mode="preview">Preview</button>
						<button class="mode-btn" data-mode="split">Split</button>
					</div>
				</div>
				<div class="editor-container">
					<textarea class="editor" placeholder="Start writing..."></textarea>
					<div class="preview"></div>
				</div>
				<div class="footer">
					<div class="word-count">
						<span class="words">0 words</span>
						<span class="chars">0 characters</span>
					</div>
					<div class="save-status saved">
						<span>✓</span>
						<span>Saved</span>
					</div>
				</div>
			</div>
		`;

		// Semantic zoom: icon-only view
		const iconView = document.createElement("div");
		iconView.className = "zoom-icon";
		iconView.innerHTML = `<span class="zoom-icon-emoji">📝</span><span class="zoom-icon-title"></span>`;

		// Semantic zoom: summary view
		const summaryView = document.createElement("div");
		summaryView.className = "zoom-summary";
		summaryView.innerHTML = `<div class="zoom-summary-header"><span>📝</span><span class="zoom-summary-title"></span></div><div class="zoom-summary-body"></div>`;

		// Replace the container div (slot's parent) with our wrapper + zoom views
		const slot = root.querySelector("slot");
		const containerDiv = slot?.parentElement as HTMLElement;
		if (containerDiv) {
			const frag = document.createDocumentFragment();
			frag.appendChild(wrapper);
			frag.appendChild(iconView);
			frag.appendChild(summaryView);
			containerDiv.replaceWith(frag);
		}

		this.#editor = wrapper.querySelector(".editor");
		this.#preview = wrapper.querySelector(".preview");
		this.#titleInput = wrapper.querySelector(".note-title");
		this.#wordCountEl = wrapper.querySelector(".word-count");
		this.#saveStatusEl = wrapper.querySelector(".save-status");
		this.#iconTitleEl = iconView.querySelector(".zoom-icon-title");
		this.#summaryTitleEl = summaryView.querySelector(".zoom-summary-title");
		this.#summaryBodyEl = summaryView.querySelector(".zoom-summary-body");
		const saveBtn = wrapper.querySelector(".save-btn") as HTMLButtonElement;
		const closeBtn = wrapper.querySelector(".close-btn") as HTMLButtonElement;
		const toolbarBtns = wrapper.querySelectorAll(".toolbar-btn[data-action]");
		const modeBtns = wrapper.querySelectorAll(".mode-btn");

		// Editor input
		this.#editor?.addEventListener("input", () => {
			this.#content = this.#editor?.value || "";
			this.#isDirty = true;
			this.#updatePreview();
			this.#updateWordCount();
			this.#updateSaveStatus();
			this.#updateZoomContent();
			this.dispatchEvent(new CustomEvent("content-change", { detail: { content: this.#content } }));
		});

		// Title input
		this.#titleInput?.addEventListener("input", () => {
			this.#title = this.#titleInput?.value || "Untitled";
			this.#isDirty = true;
			this.#updateSaveStatus();
			this.#updateZoomContent();
			this.dispatchEvent(new CustomEvent("title-change", { detail: { title: this.#title } }));
		});

		// Prevent drag on inputs
		this.#editor?.addEventListener("pointerdown", (e) => e.stopPropagation());
		this.#titleInput?.addEventListener("pointerdown", (e) => e.stopPropagation());

		// Toolbar actions
		toolbarBtns.forEach((btn) => {
			btn.addEventListener("click", (e) => {
				e.stopPropagation();
				const action = (btn as HTMLElement).dataset.action;
				if (action) this.#applyFormatting(action);
			});
		});

		// Mode toggle
		modeBtns.forEach((btn) => {
			btn.addEventListener("click", (e) => {
				e.stopPropagation();
				const mode = (btn as HTMLElement).dataset.mode as "edit" | "preview" | "split";
				this.#setMode(mode, modeBtns);
			});
		});

		// Save button
		saveBtn.addEventListener("click", (e) => {
			e.stopPropagation();
			this.#save();
		});

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

		// Keyboard shortcuts — stop all propagation from editor/title to prevent
		// canvas handlers (delete, arrow-move, space-pan) from firing
		const stopKeys = (e: KeyboardEvent) => {
			e.stopPropagation();
			if ((e.metaKey || e.ctrlKey) && e.key === "s") {
				e.preventDefault();
				this.#save();
			}
			if ((e.metaKey || e.ctrlKey) && e.key === "b") {
				e.preventDefault();
				this.#applyFormatting("bold");
			}
			if ((e.metaKey || e.ctrlKey) && e.key === "i") {
				e.preventDefault();
				this.#applyFormatting("italic");
			}
		};
		this.#editor?.addEventListener("keydown", stopKeys);
		this.#titleInput?.addEventListener("keydown", (e) => e.stopPropagation());

		// Semantic zoom: poll --canvas-scale CSS custom property
		this.classList.add("zoom-level-full");
		this.#updateZoomContent();
		const checkZoom = () => {
			const scaleStr = getComputedStyle(this).getPropertyValue("--canvas-scale");
			const s = parseFloat(scaleStr) || 1;
			let level: "icon" | "summary" | "full";
			if (s < 0.3) level = "icon";
			else if (s < 0.7) level = "summary";
			else level = "full";
			if (level !== this.#zoomLevel) {
				this.classList.remove(`zoom-level-${this.#zoomLevel}`);
				this.#zoomLevel = level;
				this.classList.add(`zoom-level-${level}`);
				this.#updateZoomContent();
			}
			this.#zoomRaf = requestAnimationFrame(checkZoom);
		};
		this.#zoomRaf = requestAnimationFrame(checkZoom);

		return root;
	}

	disconnectedCallback() {
		if (this.#zoomRaf) cancelAnimationFrame(this.#zoomRaf);
	}

	#updateZoomContent() {
		if (this.#iconTitleEl) {
			this.#iconTitleEl.textContent = this.#title || "Untitled";
		}
		if (this.#summaryTitleEl) {
			this.#summaryTitleEl.textContent = this.#title || "Untitled";
		}
		if (this.#summaryBodyEl) {
			// Show first ~200 chars of content as plain text summary
			const plain = this.#content.replace(/[#*`>\[\]()-]/g, "").trim();
			this.#summaryBodyEl.textContent = plain.length > 200 ? plain.slice(0, 200) + "..." : plain || "Empty note";
		}
	}

	#setMode(mode: "edit" | "preview" | "split", buttons: NodeListOf<Element>) {
		this.#mode = mode;

		buttons.forEach((btn) => {
			btn.classList.toggle("active", (btn as HTMLElement).dataset.mode === mode);
		});

		if (this.#editor && this.#preview) {
			switch (mode) {
				case "edit":
					this.#editor.style.display = "block";
					this.#preview.style.display = "none";
					break;
				case "preview":
					this.#editor.style.display = "none";
					this.#preview.style.display = "block";
					break;
				case "split":
					this.#editor.style.display = "block";
					this.#preview.style.display = "block";
					break;
			}
		}

		this.#updatePreview();
	}

	#applyFormatting(action: string) {
		if (!this.#editor) return;

		const start = this.#editor.selectionStart;
		const end = this.#editor.selectionEnd;
		const text = this.#editor.value;
		const selected = text.substring(start, end);

		let replacement = selected;
		let cursorOffset = 0;

		switch (action) {
			case "heading":
				replacement = `## ${selected}`;
				cursorOffset = 3;
				break;
			case "bold":
				replacement = `**${selected}**`;
				cursorOffset = selected ? 0 : 2;
				break;
			case "italic":
				replacement = `*${selected}*`;
				cursorOffset = selected ? 0 : 1;
				break;
			case "code":
				if (selected.includes("\n")) {
					replacement = `\`\`\`\n${selected}\n\`\`\``;
				} else {
					replacement = `\`${selected}\``;
					cursorOffset = selected ? 0 : 1;
				}
				break;
			case "link":
				replacement = `[${selected || "link text"}](url)`;
				cursorOffset = selected ? selected.length + 3 : 1;
				break;
			case "list":
				replacement = `- ${selected}`;
				cursorOffset = 2;
				break;
			case "quote":
				replacement = `> ${selected}`;
				cursorOffset = 2;
				break;
		}

		this.#editor.value =
			text.substring(0, start) + replacement + text.substring(end);
		this.#content = this.#editor.value;

		// Set cursor position
		const newPos = start + (selected ? replacement.length : cursorOffset);
		this.#editor.setSelectionRange(newPos, newPos);
		this.#editor.focus();

		this.#isDirty = true;
		this.#updatePreview();
		this.#updateWordCount();
		this.#updateSaveStatus();
	}

	#updatePreview() {
		if (!this.#preview) return;
		this.#preview.innerHTML = this.#renderMarkdown(this.#content);
	}

	#renderMarkdown(text: string): string {
		let html = this.#escapeHtml(text);

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

		// Headers
		html = html.replace(/^### (.+)$/gm, "<h3>$1</h3>");
		html = html.replace(/^## (.+)$/gm, "<h2>$1</h2>");
		html = html.replace(/^# (.+)$/gm, "<h1>$1</h1>");

		// Bold/Italic
		html = html.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>");
		html = html.replace(/\*(.+?)\*/g, "<em>$1</em>");

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

		// Links
		html = html.replace(
			/\[([^\]]+)\]\(([^)]+)\)/g,
			'<a href="$2" target="_blank" rel="noopener">$1</a>'
		);

		// Blockquotes
		html = html.replace(/^&gt; (.+)$/gm, "<blockquote>$1</blockquote>");

		// Lists
		html = html.replace(/^- (.+)$/gm, "<li>$1</li>");
		html = html.replace(/(<li>.*<\/li>\n?)+/g, "<ul>$&</ul>");

		// Horizontal rules
		html = html.replace(/^---$/gm, "<hr>");

		// Paragraphs
		html = html.replace(/\n\n/g, "</p><p>");
		html = `<p>${html}</p>`;
		html = html.replace(/<p><\/p>/g, "");

		return html;
	}

	#updateWordCount() {
		if (!this.#wordCountEl) return;

		const words = this.#content
			.trim()
			.split(/\s+/)
			.filter((w) => w.length > 0).length;
		const chars = this.#content.length;

		this.#wordCountEl.innerHTML = `
			<span class="words">${words} words</span>
			<span class="chars">${chars} characters</span>
		`;
	}

	#updateSaveStatus() {
		if (!this.#saveStatusEl) return;

		if (this.#isDirty) {
			this.#saveStatusEl.className = "save-status unsaved";
			this.#saveStatusEl.innerHTML = "<span>•</span><span>Unsaved</span>";
		} else {
			this.#saveStatusEl.className = "save-status saved";
			this.#saveStatusEl.innerHTML = "<span>✓</span><span>Saved</span>";
		}
	}

	#save() {
		this.#isDirty = false;
		this.#lastSaved = new Date();
		this.#updateSaveStatus();
		this.dispatchEvent(
			new CustomEvent("save", {
				detail: { title: this.#title, content: this.#content },
			})
		);
	}

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

	static override fromData(data: Record<string, any>): FolkObsNote {
		const shape = FolkShape.fromData(data) as FolkObsNote;
		if (data.title) shape.title = data.title;
		if (data.content) shape.content = data.content;
		return shape;
	}

	override toJSON() {
		return {
			...super.toJSON(),
			type: "folk-obs-note",
			title: this.title,
			content: this.content,
		};
	}

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