/**
 * folk-gov-knob — Adjustable Parameter
 *
 * An SVG rotary knob (225° sweep) with numeric input fallback.
 * Optional "temporal viscosity" cooldown delays value propagation.
 */

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

const HEADER_COLOR = "#b45309";
const SWEEP = 225; // degrees
const START_ANGLE = (360 - SWEEP) / 2 + 90; // start from bottom-left

const styles = css`
	:host {
		background: var(--rs-bg-surface, #1e293b);
		border-radius: 10px;
		box-shadow: 0 2px 12px rgba(0, 0, 0, 0.25);
		min-width: 160px;
		min-height: 120px;
		overflow: hidden;
	}

	.header {
		display: flex;
		align-items: center;
		justify-content: space-between;
		padding: 8px 12px;
		background: ${HEADER_COLOR};
		color: white;
		font-size: 12px;
		font-weight: 600;
		cursor: move;
		border-radius: 10px 10px 0 0;
	}

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

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

	.body {
		display: flex;
		flex-direction: column;
		align-items: center;
		padding: 12px;
		gap: 8px;
	}

	.title-input {
		background: transparent;
		border: none;
		color: var(--rs-text-primary, #e2e8f0);
		font-size: 12px;
		font-weight: 600;
		text-align: center;
		width: 100%;
		outline: none;
	}

	.title-input::placeholder {
		color: var(--rs-text-muted, #64748b);
	}

	.knob-svg {
		cursor: grab;
		user-select: none;
	}

	.knob-svg:active {
		cursor: grabbing;
	}

	.value-row {
		display: flex;
		align-items: center;
		gap: 4px;
	}

	.value-input {
		background: rgba(255, 255, 255, 0.06);
		border: 1px solid rgba(255, 255, 255, 0.1);
		border-radius: 4px;
		color: var(--rs-text-primary, #e2e8f0);
		font-size: 12px;
		font-weight: 600;
		padding: 2px 6px;
		width: 60px;
		text-align: center;
		outline: none;
	}

	.unit-label {
		font-size: 11px;
		color: var(--rs-text-muted, #94a3b8);
	}

	.range-row {
		display: flex;
		gap: 4px;
		font-size: 10px;
		color: var(--rs-text-muted, #64748b);
	}

	.range-input {
		background: rgba(255, 255, 255, 0.04);
		border: 1px solid rgba(255, 255, 255, 0.08);
		border-radius: 3px;
		color: var(--rs-text-muted, #94a3b8);
		font-size: 10px;
		padding: 1px 4px;
		width: 40px;
		text-align: center;
		outline: none;
	}

	.cooldown-row {
		display: flex;
		align-items: center;
		gap: 4px;
		font-size: 10px;
		color: var(--rs-text-muted, #64748b);
	}

	.cooldown-input {
		background: rgba(255, 255, 255, 0.04);
		border: 1px solid rgba(255, 255, 255, 0.08);
		border-radius: 3px;
		color: var(--rs-text-muted, #94a3b8);
		font-size: 10px;
		padding: 1px 4px;
		width: 36px;
		text-align: center;
		outline: none;
	}

	.cooldown-active {
		color: #f59e0b;
		font-weight: 600;
	}
`;

declare global {
	interface HTMLElementTagNameMap {
		"folk-gov-knob": FolkGovKnob;
	}
}

export class FolkGovKnob extends FolkShape {
	static override tagName = "folk-gov-knob";

	static override portDescriptors: PortDescriptor[] = [
		{ name: "value-out", type: "number", direction: "output" },
	];

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

	#title = "Parameter";
	#min = 0;
	#max = 100;
	#step = 1;
	#value = 50;
	#unit = "";
	#cooldown = 0; // seconds (0 = disabled)
	#cooldownRemaining = 0;
	#cooldownTimer: ReturnType<typeof setInterval> | null = null;
	#pendingValue: number | null = null;

	// DOM refs
	#titleEl!: HTMLInputElement;
	#valueEl!: HTMLInputElement;
	#knobSvg!: SVGSVGElement;
	#knobArc!: SVGPathElement;
	#knobDot!: SVGCircleElement;
	#cooldownRing!: SVGCircleElement;
	#cooldownLabel!: HTMLElement;

	get title() { return this.#title; }
	set title(v: string) {
		this.#title = v;
		if (this.#titleEl) this.#titleEl.value = v;
	}

	get min() { return this.#min; }
	set min(v: number) { this.#min = v; this.#updateKnob(); }

	get max() { return this.#max; }
	set max(v: number) { this.#max = v; this.#updateKnob(); }

	get step() { return this.#step; }
	set step(v: number) { this.#step = v; }

	get value() { return this.#value; }
	set value(v: number) {
		this.#value = Math.max(this.#min, Math.min(this.#max, v));
		if (this.#valueEl) this.#valueEl.value = String(this.#value);
		this.#updateKnob();
	}

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

	get cooldown() { return this.#cooldown; }
	set cooldown(v: number) { this.#cooldown = Math.max(0, v); }

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

		const wrapper = document.createElement("div");
		wrapper.style.cssText = "width:100%;height:100%;display:flex;flex-direction:column;";
		wrapper.innerHTML = html`
			<div class="header" data-drag>
				<span class="header-title">🎛️ Knob</span>
				<span class="header-actions">
					<button class="close-btn">&times;</button>
				</span>
			</div>
			<div class="body">
				<input class="title-input" type="text" placeholder="Parameter name..." />
				<svg class="knob-svg" width="80" height="80" viewBox="0 0 80 80">
					<circle cx="40" cy="40" r="35" fill="none" stroke="rgba(255,255,255,0.1)" stroke-width="6" />
					<circle class="cooldown-ring" cx="40" cy="40" r="35" fill="none" stroke="#f59e0b"
						stroke-width="6" stroke-dasharray="220" stroke-dashoffset="220"
						transform="rotate(-90 40 40)" opacity="0.5" />
					<path class="knob-arc" fill="none" stroke="${HEADER_COLOR}" stroke-width="6" stroke-linecap="round" d="" />
					<circle class="knob-dot" cx="40" cy="40" r="5" fill="white" />
				</svg>
				<div class="value-row">
					<input class="value-input" type="number" />
					<span class="unit-label"></span>
				</div>
				<div class="range-row">
					<span>min</span>
					<input class="range-input min-input" type="number" />
					<span>max</span>
					<input class="range-input max-input" type="number" />
					<span>step</span>
					<input class="range-input step-input" type="number" min="0.01" />
				</div>
				<div class="cooldown-row">
					<span>cooldown</span>
					<input class="cooldown-input" type="number" min="0" />
					<span>s</span>
					<span class="cooldown-active" style="display:none"></span>
				</div>
			</div>
		`;

		const slot = root.querySelector("slot");
		const container = slot?.parentElement as HTMLElement;
		if (container) container.replaceWith(wrapper);

		// Cache refs
		this.#titleEl = wrapper.querySelector(".title-input") as HTMLInputElement;
		this.#valueEl = wrapper.querySelector(".value-input") as HTMLInputElement;
		this.#knobSvg = wrapper.querySelector(".knob-svg") as unknown as SVGSVGElement;
		this.#knobArc = wrapper.querySelector(".knob-arc") as unknown as SVGPathElement;
		this.#knobDot = wrapper.querySelector(".knob-dot") as unknown as SVGCircleElement;
		this.#cooldownRing = wrapper.querySelector(".cooldown-ring") as unknown as SVGCircleElement;
		this.#cooldownLabel = wrapper.querySelector(".cooldown-active") as HTMLElement;

		const minEl = wrapper.querySelector(".min-input") as HTMLInputElement;
		const maxEl = wrapper.querySelector(".max-input") as HTMLInputElement;
		const stepEl = wrapper.querySelector(".step-input") as HTMLInputElement;
		const cooldownEl = wrapper.querySelector(".cooldown-input") as HTMLInputElement;
		const unitLabel = wrapper.querySelector(".unit-label") as HTMLElement;

		// Set initial values
		this.#titleEl.value = this.#title;
		this.#valueEl.value = String(this.#value);
		minEl.value = String(this.#min);
		maxEl.value = String(this.#max);
		stepEl.value = String(this.#step);
		cooldownEl.value = String(this.#cooldown);
		unitLabel.textContent = this.#unit;
		this.#updateKnob();

		// Wire events
		const onChange = () => this.dispatchEvent(new CustomEvent("content-change"));

		this.#titleEl.addEventListener("input", (e) => {
			e.stopPropagation();
			this.#title = this.#titleEl.value;
			onChange();
		});

		this.#valueEl.addEventListener("input", (e) => {
			e.stopPropagation();
			const v = parseFloat(this.#valueEl.value);
			if (!isNaN(v)) this.#setValueWithCooldown(v);
			onChange();
		});

		minEl.addEventListener("input", (e) => {
			e.stopPropagation();
			this.#min = parseFloat(minEl.value) || 0;
			this.#updateKnob();
			onChange();
		});

		maxEl.addEventListener("input", (e) => {
			e.stopPropagation();
			this.#max = parseFloat(maxEl.value) || 100;
			this.#updateKnob();
			onChange();
		});

		stepEl.addEventListener("input", (e) => {
			e.stopPropagation();
			this.#step = parseFloat(stepEl.value) || 1;
			onChange();
		});

		cooldownEl.addEventListener("input", (e) => {
			e.stopPropagation();
			this.#cooldown = parseFloat(cooldownEl.value) || 0;
			onChange();
		});

		wrapper.querySelector(".close-btn")!.addEventListener("click", (e) => {
			e.stopPropagation();
			this.dispatchEvent(new CustomEvent("close"));
		});

		// Knob drag interaction
		let dragging = false;
		this.#knobSvg.addEventListener("pointerdown", (e) => {
			e.stopPropagation();
			dragging = true;
			(e.target as Element).setPointerCapture(e.pointerId);
		});

		this.#knobSvg.addEventListener("pointermove", (e) => {
			if (!dragging) return;
			e.stopPropagation();
			const rect = this.#knobSvg.getBoundingClientRect();
			const cx = rect.left + rect.width / 2;
			const cy = rect.top + rect.height / 2;
			let angle = Math.atan2(e.clientY - cy, e.clientX - cx) * (180 / Math.PI);
			angle = (angle + 360) % 360;
			// Map angle to value
			const startA = START_ANGLE;
			let relAngle = (angle - startA + 360) % 360;
			if (relAngle > SWEEP) relAngle = relAngle > SWEEP + (360 - SWEEP) / 2 ? 0 : SWEEP;
			const ratio = relAngle / SWEEP;
			let v = this.#min + ratio * (this.#max - this.#min);
			// Snap to step
			v = Math.round(v / this.#step) * this.#step;
			v = Math.max(this.#min, Math.min(this.#max, v));
			this.#setValueWithCooldown(v);
			onChange();
		});

		this.#knobSvg.addEventListener("pointerup", (e) => {
			dragging = false;
			e.stopPropagation();
		});

		// Prevent drag on all inputs
		for (const el of wrapper.querySelectorAll("input, button")) {
			el.addEventListener("pointerdown", (e) => e.stopPropagation());
		}

		// Emit initial value
		this.#emitPort();

		return root;
	}

	#setValueWithCooldown(v: number) {
		v = Math.max(this.#min, Math.min(this.#max, v));
		this.#value = v;
		if (this.#valueEl) this.#valueEl.value = String(v);
		this.#updateKnob();

		if (this.#cooldown > 0) {
			this.#pendingValue = v;
			if (!this.#cooldownTimer) {
				this.#cooldownRemaining = this.#cooldown;
				this.#cooldownLabel.style.display = "";
				this.#cooldownLabel.textContent = `${this.#cooldownRemaining}s`;
				this.#updateCooldownRing();
				this.#cooldownTimer = setInterval(() => {
					this.#cooldownRemaining--;
					if (this.#cooldownRemaining <= 0) {
						clearInterval(this.#cooldownTimer!);
						this.#cooldownTimer = null;
						this.#cooldownLabel.style.display = "none";
						this.#cooldownRing.setAttribute("stroke-dashoffset", "220");
						if (this.#pendingValue !== null) {
							this.#emitPort();
							this.#pendingValue = null;
						}
					} else {
						this.#cooldownLabel.textContent = `${this.#cooldownRemaining}s`;
						this.#updateCooldownRing();
					}
				}, 1000);
			} else {
				// Reset countdown
				this.#cooldownRemaining = this.#cooldown;
				this.#cooldownLabel.textContent = `${this.#cooldownRemaining}s`;
				this.#updateCooldownRing();
			}
		} else {
			this.#emitPort();
		}
	}

	#updateCooldownRing() {
		if (!this.#cooldownRing || this.#cooldown <= 0) return;
		const circumference = 220; // 2 * π * 35
		const progress = this.#cooldownRemaining / this.#cooldown;
		this.#cooldownRing.setAttribute("stroke-dashoffset", String(circumference * (1 - progress)));
	}

	#updateKnob() {
		if (!this.#knobArc || !this.#knobDot) return;
		const range = this.#max - this.#min;
		const ratio = range > 0 ? (this.#value - this.#min) / range : 0;
		const endAngle = START_ANGLE + ratio * SWEEP;

		// Arc path
		const r = 35;
		const cx = 40;
		const cy = 40;
		const startRad = (START_ANGLE * Math.PI) / 180;
		const endRad = (endAngle * Math.PI) / 180;
		const x1 = cx + r * Math.cos(startRad);
		const y1 = cy + r * Math.sin(startRad);
		const x2 = cx + r * Math.cos(endRad);
		const y2 = cy + r * Math.sin(endRad);
		const largeArc = ratio * SWEEP > 180 ? 1 : 0;

		if (ratio > 0.001) {
			this.#knobArc.setAttribute("d", `M ${x1} ${y1} A ${r} ${r} 0 ${largeArc} 1 ${x2} ${y2}`);
		} else {
			this.#knobArc.setAttribute("d", "");
		}

		// Dot position
		this.#knobDot.setAttribute("cx", String(x2));
		this.#knobDot.setAttribute("cy", String(y2));
	}

	#emitPort() {
		this.setPortValue("value-out", this.#value);
	}

	override disconnectedCallback() {
		super.disconnectedCallback();
		if (this.#cooldownTimer) {
			clearInterval(this.#cooldownTimer);
			this.#cooldownTimer = null;
		}
	}

	override toJSON() {
		return {
			...super.toJSON(),
			type: "folk-gov-knob",
			title: this.#title,
			min: this.#min,
			max: this.#max,
			step: this.#step,
			value: this.#value,
			unit: this.#unit,
			cooldown: this.#cooldown,
		};
	}

	static override fromData(data: Record<string, any>): FolkGovKnob {
		const shape = FolkShape.fromData.call(this, data) as FolkGovKnob;
		if (data.title !== undefined) shape.title = data.title;
		if (data.min !== undefined) shape.min = data.min;
		if (data.max !== undefined) shape.max = data.max;
		if (data.step !== undefined) shape.step = data.step;
		if (data.value !== undefined) shape.value = data.value;
		if (data.unit !== undefined) shape.unit = data.unit;
		if (data.cooldown !== undefined) shape.cooldown = data.cooldown;
		return shape;
	}

	override applyData(data: Record<string, any>): void {
		super.applyData(data);
		if (data.title !== undefined && data.title !== this.#title) this.title = data.title;
		if (data.min !== undefined && data.min !== this.#min) this.min = data.min;
		if (data.max !== undefined && data.max !== this.#max) this.max = data.max;
		if (data.step !== undefined && data.step !== this.#step) this.step = data.step;
		if (data.value !== undefined && data.value !== this.#value) this.value = data.value;
		if (data.unit !== undefined && data.unit !== this.#unit) this.unit = data.unit;
		if (data.cooldown !== undefined && data.cooldown !== this.#cooldown) this.cooldown = data.cooldown;
	}
}
