/**
 * folk-task-request — Canvas shape representing a task card with skill slots.
 * Acts as a drop target for orbs dragged from folk-commitment-pool.
 *
 * On drop: POST connection to rTime API + dispatch notification event.
 */

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

// Skill constants (mirrored from rtime/schemas)
const SKILL_COLORS: Record<string, string> = {
	facilitation: '#8b5cf6', design: '#ec4899', tech: '#3b82f6',
	outreach: '#10b981', logistics: '#f59e0b',
};
const SKILL_LABELS: Record<string, string> = {
	facilitation: 'Facilitation', design: 'Design', tech: 'Tech',
	outreach: 'Outreach', logistics: 'Logistics',
};

const styles = css`
	:host {
		background: #1e293b;
		border-radius: 10px;
		border: 1.5px solid #334155;
		overflow: hidden;
	}
	:host(.drag-highlight) {
		border-color: #14b8a6;
		box-shadow: 0 0 16px rgba(20, 184, 166, 0.3);
	}
	.header {
		display: flex;
		align-items: center;
		gap: 8px;
		padding: 10px 12px 6px;
		cursor: move;
	}
	.header .icon { font-size: 16px; }
	.task-name {
		flex: 1;
		font-size: 13px;
		font-weight: 600;
		color: #e2e8f0;
		background: none;
		border: none;
		outline: none;
		font-family: inherit;
		padding: 0;
	}
	.task-name:focus {
		border-bottom: 1px solid #14b8a6;
	}
	.task-name::placeholder { color: #64748b; }
	.desc {
		padding: 0 12px 8px;
		font-size: 11px;
		color: #94a3b8;
		line-height: 1.4;
	}
	.desc-input {
		width: 100%;
		background: none;
		border: none;
		outline: none;
		color: #94a3b8;
		font-size: 11px;
		font-family: inherit;
		padding: 0;
		resize: none;
	}
	.desc-input::placeholder { color: #475569; }
	.skill-slots {
		padding: 0 12px 10px;
		display: flex;
		flex-direction: column;
		gap: 4px;
	}
	.slot {
		display: flex;
		align-items: center;
		gap: 8px;
		padding: 5px 8px;
		border-radius: 6px;
		background: #0f172a;
		border: 1.5px dashed #334155;
		font-size: 11px;
		color: #cbd5e1;
		transition: border-color 0.2s, background 0.2s;
	}
	.slot.highlight {
		border-color: #14b8a6;
		background: rgba(20, 184, 166, 0.08);
	}
	.slot.filled {
		border-style: solid;
		opacity: 0.7;
	}
	.slot-dot {
		width: 8px;
		height: 8px;
		border-radius: 50%;
		flex-shrink: 0;
	}
	.slot-label { flex: 1; }
	.slot-hours {
		font-weight: 600;
		font-size: 10px;
		color: #64748b;
	}
	.slot-assigned {
		font-size: 10px;
		color: #14b8a6;
		font-weight: 500;
	}
`;

interface SlotState {
	skill: string;
	hoursNeeded: number;
	assignedMember?: string;
	commitmentId?: string;
}

declare global {
	interface HTMLElementTagNameMap {
		"folk-task-request": FolkTaskRequest;
	}
}

export class FolkTaskRequest extends FolkShape {
	static override tagName = "folk-task-request";

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

	// ── Module-disabled gating ──
	static #enabledModules: Set<string> | null = null;
	static #instances = new Set<FolkTaskRequest>();

	static setEnabledModules(ids: string[] | null) {
		FolkTaskRequest.#enabledModules = ids ? new Set(ids) : null;
		for (const inst of FolkTaskRequest.#instances) inst.#syncDisabledState();
	}

	#isModuleDisabled(): boolean {
		const enabled = FolkTaskRequest.#enabledModules;
		if (!enabled) return false;
		return !enabled.has("rtime");
	}

	#syncDisabledState() {
		if (!this.#wrapper) return;
		const disabled = this.#isModuleDisabled();
		const wasDisabled = this.hasAttribute("data-module-disabled");
		if (disabled && !wasDisabled) {
			this.#showDisabledOverlay();
		} else if (!disabled && wasDisabled) {
			this.removeAttribute("data-module-disabled");
			this.#wrapper.querySelector(".disabled-overlay")?.remove();
		}
	}

	#showDisabledOverlay() {
		this.setAttribute("data-module-disabled", "");
		let overlay = this.#wrapper?.querySelector(".disabled-overlay") as HTMLElement;
		if (!overlay && this.#wrapper) {
			overlay = document.createElement("div");
			overlay.className = "disabled-overlay";
			overlay.style.cssText = "position:absolute;inset:0;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:8px;background:rgba(15,23,42,0.85);border-radius:inherit;z-index:10;color:#94a3b8;font-size:13px;";
			overlay.innerHTML = '<span style="font-size:24px">🔒</span><span>rTime is disabled</span>';
			this.#wrapper.appendChild(overlay);
		}
	}

	#taskId = "";
	#spaceSlug = "demo";
	#taskName = "New Task";
	#taskDescription = "";
	#needs: Record<string, number> = {};
	#slots: SlotState[] = [];
	#wrapper!: HTMLElement;
	#slotsContainer!: HTMLElement;
	#nameInput!: HTMLInputElement;
	#descInput!: HTMLTextAreaElement;

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

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

	get taskName() { return this.#taskName; }
	set taskName(v: string) {
		this.#taskName = v;
		if (this.#nameInput) this.#nameInput.value = v;
		this.dispatchEvent(new CustomEvent("content-change"));
	}

	get taskDescription() { return this.#taskDescription; }
	set taskDescription(v: string) {
		this.#taskDescription = v;
		if (this.#descInput) this.#descInput.value = v;
		this.dispatchEvent(new CustomEvent("content-change"));
	}

	get needs() { return this.#needs; }
	set needs(v: Record<string, number>) {
		this.#needs = v;
		this.#buildSlots();
		this.dispatchEvent(new CustomEvent("content-change"));
	}

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

		this.#wrapper = document.createElement("div");
		this.#wrapper.style.cssText = "width:100%;height:100%;display:flex;flex-direction:column;";
		this.#wrapper.innerHTML = html`
			<div class="header">
				<span class="icon">📋</span>
				<input class="task-name" type="text" placeholder="Task name..." />
			</div>
			<div class="desc">
				<textarea class="desc-input" rows="2" placeholder="Description..."></textarea>
			</div>
			<div class="skill-slots"></div>
		`;

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

		this.#nameInput = this.#wrapper.querySelector(".task-name") as HTMLInputElement;
		this.#descInput = this.#wrapper.querySelector(".desc-input") as HTMLTextAreaElement;
		this.#slotsContainer = this.#wrapper.querySelector(".skill-slots") as HTMLElement;

		this.#nameInput.value = this.#taskName;
		this.#descInput.value = this.#taskDescription;

		// Stop events from triggering shape move
		const stopProp = (e: Event) => e.stopPropagation();
		this.#nameInput.addEventListener("click", stopProp);
		this.#descInput.addEventListener("click", stopProp);
		this.#nameInput.addEventListener("pointerdown", stopProp);
		this.#descInput.addEventListener("pointerdown", stopProp);

		this.#nameInput.addEventListener("change", () => {
			this.#taskName = this.#nameInput.value.trim() || "New Task";
			this.dispatchEvent(new CustomEvent("content-change"));
		});
		this.#nameInput.addEventListener("keydown", (e) => {
			e.stopPropagation();
			if (e.key === "Enter") this.#nameInput.blur();
		});
		this.#descInput.addEventListener("change", () => {
			this.#taskDescription = this.#descInput.value.trim();
			this.dispatchEvent(new CustomEvent("content-change"));
		});
		this.#descInput.addEventListener("keydown", (e) => {
			e.stopPropagation();
		});

		this.#buildSlots();

		// Listen for commitment drag events
		document.addEventListener("commitment-drag-start", this.#onDragStart as EventListener);
		document.addEventListener("commitment-drag-move", this.#onDragMove as EventListener);
		document.addEventListener("commitment-drag-end", this.#onDragEnd as EventListener);

		FolkTaskRequest.#instances.add(this);
		if (this.#isModuleDisabled()) this.#showDisabledOverlay();

		return root;
	}

	disconnectedCallback() {
		FolkTaskRequest.#instances.delete(this);
		document.removeEventListener("commitment-drag-start", this.#onDragStart as EventListener);
		document.removeEventListener("commitment-drag-move", this.#onDragMove as EventListener);
		document.removeEventListener("commitment-drag-end", this.#onDragEnd as EventListener);
	}

	#buildSlots() {
		if (!this.#slotsContainer) return;
		// Build slots from needs
		this.#slots = Object.entries(this.#needs).map(([skill, hours]) => {
			// Preserve existing assignment if any
			const existing = this.#slots.find(s => s.skill === skill);
			return {
				skill,
				hoursNeeded: hours,
				assignedMember: existing?.assignedMember,
				commitmentId: existing?.commitmentId,
			};
		});
		this.#renderSlots();
	}

	#renderSlots() {
		if (!this.#slotsContainer) return;
		this.#slotsContainer.innerHTML = "";
		for (const slot of this.#slots) {
			const el = document.createElement("div");
			el.className = "slot" + (slot.assignedMember ? " filled" : "");
			el.dataset.skill = slot.skill;
			el.innerHTML = `
				<span class="slot-dot" style="background:${SKILL_COLORS[slot.skill] || '#64748b'}"></span>
				<span class="slot-label">${SKILL_LABELS[slot.skill] || slot.skill}</span>
				<span class="slot-hours">${slot.hoursNeeded}hr</span>
				${slot.assignedMember ? `<span class="slot-assigned">${slot.assignedMember}</span>` : ""}
			`;
			this.#slotsContainer.appendChild(el);
		}
	}

	// ── Drag handling ──

	#activeDragSkill: string | null = null;

	#onDragStart = (e: CustomEvent) => {
		this.#activeDragSkill = e.detail.skill;
		// Highlight matching unfilled slots
		const matchingSlots = this.#slots.filter(s => s.skill === this.#activeDragSkill && !s.assignedMember);
		if (matchingSlots.length > 0) {
			this.classList.add("drag-highlight");
			this.#highlightSlots(this.#activeDragSkill!);
		}
	};

	#onDragMove = (e: CustomEvent) => {
		// Check if pointer is over this shape
		const rect = this.getBoundingClientRect();
		const over = e.detail.clientX >= rect.left && e.detail.clientX <= rect.right
			&& e.detail.clientY >= rect.top && e.detail.clientY <= rect.bottom;

		if (over && this.#activeDragSkill) {
			this.classList.add("drag-highlight");
			this.#highlightSlots(this.#activeDragSkill);
		} else {
			this.classList.remove("drag-highlight");
			this.#clearSlotHighlights();
		}
	};

	#onDragEnd = (e: CustomEvent) => {
		this.classList.remove("drag-highlight");
		this.#clearSlotHighlights();

		if (!this.#activeDragSkill) return;
		const skill = this.#activeDragSkill;
		this.#activeDragSkill = null;

		// Check if drop landed on this shape
		const rect = this.getBoundingClientRect();
		const over = e.detail.clientX >= rect.left && e.detail.clientX <= rect.right
			&& e.detail.clientY >= rect.top && e.detail.clientY <= rect.bottom;
		if (!over) return;

		// Find matching unfilled slot
		const slot = this.#slots.find(s => s.skill === skill && !s.assignedMember);
		if (!slot) return;

		// Assign
		slot.assignedMember = e.detail.memberName;
		slot.commitmentId = e.detail.commitmentId;
		this.#renderSlots();

		// POST connection
		this.#postConnection(e.detail.commitmentId, skill);

		this.dispatchEvent(new CustomEvent("commitment-assigned", {
			bubbles: true,
			detail: {
				taskId: this.#taskId,
				commitmentId: e.detail.commitmentId,
				skill,
			},
		}));
	};

	#highlightSlots(skill: string) {
		const slotEls = this.#slotsContainer.querySelectorAll(".slot");
		slotEls.forEach(el => {
			const s = (el as HTMLElement).dataset.skill;
			if (s === skill && !el.classList.contains("filled")) {
				el.classList.add("highlight");
			}
		});
	}

	#clearSlotHighlights() {
		this.#slotsContainer.querySelectorAll(".slot.highlight").forEach(el => el.classList.remove("highlight"));
	}

	async #postConnection(commitmentId: string, skill: string) {
		if (!this.#taskId) return;
		try {
			// Get auth token from cookie or localStorage
			const token = document.cookie.split(";").map(c => c.trim()).find(c => c.startsWith("auth_token="))?.split("=")[1]
				|| localStorage.getItem("auth_token") || "";
			await fetch(`${getModuleApiBase("rtime")}/api/connections`, {
				method: "POST",
				headers: {
					"Content-Type": "application/json",
					...(token ? { "Authorization": `Bearer ${token}` } : {}),
				},
				body: JSON.stringify({
					fromCommitmentId: commitmentId,
					toTaskId: this.#taskId,
					skill,
				}),
			});
		} catch { /* best-effort */ }
	}

	// ── Serialization ──

	override toJSON() {
		const slotsData: Record<string, { member?: string; commitmentId?: string }> = {};
		for (const s of this.#slots) {
			if (s.assignedMember) {
				slotsData[s.skill] = { member: s.assignedMember, commitmentId: s.commitmentId };
			}
		}
		return {
			...super.toJSON(),
			type: "folk-task-request",
			taskId: this.#taskId,
			spaceSlug: this.#spaceSlug,
			taskName: this.#taskName,
			taskDescription: this.#taskDescription,
			needs: this.#needs,
			assignments: slotsData,
		};
	}

	static override fromData(data: Record<string, any>): FolkTaskRequest {
		const shape = FolkShape.fromData(data) as FolkTaskRequest;
		if (data.taskId) shape.taskId = data.taskId;
		if (data.spaceSlug) shape.spaceSlug = data.spaceSlug;
		if (data.taskName) shape.taskName = data.taskName;
		if (data.taskDescription) shape.taskDescription = data.taskDescription;
		if (data.needs) shape.needs = data.needs;
		// Restore assignments
		if (data.assignments) {
			for (const [skill, info] of Object.entries(data.assignments as Record<string, any>)) {
				const slot = shape.#slots.find(s => s.skill === skill);
				if (slot && info.member) {
					slot.assignedMember = info.member;
					slot.commitmentId = info.commitmentId;
				}
			}
			shape.#renderSlots();
		}
		return shape;
	}

	override applyData(data: Record<string, any>): void {
		super.applyData(data);
		if (data.taskId && data.taskId !== this.#taskId) this.taskId = data.taskId;
		if (data.spaceSlug && data.spaceSlug !== this.#spaceSlug) this.spaceSlug = data.spaceSlug;
		if (data.taskName !== undefined && data.taskName !== this.#taskName) this.taskName = data.taskName;
		if (data.taskDescription !== undefined && data.taskDescription !== this.#taskDescription) this.taskDescription = data.taskDescription;
		if (data.needs && JSON.stringify(data.needs) !== JSON.stringify(this.#needs)) this.needs = data.needs;
		if (data.assignments) {
			for (const [skill, info] of Object.entries(data.assignments as Record<string, any>)) {
				const slot = this.#slots.find(s => s.skill === skill);
				if (slot && info.member) {
					slot.assignedMember = info.member;
					slot.commitmentId = info.commitmentId;
				}
			}
			this.#renderSlots();
		}
	}
}
