/**
 * rVote demo — client-side WebSocket controller.
 *
 * Connects via DemoSync, renders poll cards into #rd-polls-container,
 * and handles vote +/- button clicks via event delegation.
 */

import { DemoSync } from "@lib/demo-sync-vanilla";
import type { DemoShape } from "@lib/demo-sync-vanilla";

// ── Types ──

interface PollOption {
	label: string;
	votes: number;
}

interface DemoPoll extends DemoShape {
	question: string;
	options: PollOption[];
	totalVoters: number;
	status: "active" | "closed";
	endsAt: string;
}

function isPoll(shape: DemoShape): shape is DemoPoll {
	return shape.type === "demo-poll" && Array.isArray((shape as DemoPoll).options);
}

// ── Helpers ──

function formatDeadline(dateStr: string): string {
	const diff = new Date(dateStr).getTime() - Date.now();
	if (diff <= 0) return "Voting closed";
	const days = Math.ceil(diff / (1000 * 60 * 60 * 24));
	return `${days} day${days !== 1 ? "s" : ""} left`;
}

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

// ── DOM refs ──

const connBadge = document.getElementById("rd-conn-badge") as HTMLElement;
const resetBtn = document.getElementById("rd-reset-btn") as HTMLButtonElement;
const loadingEl = document.getElementById("rd-loading") as HTMLElement;
const emptyEl = document.getElementById("rd-empty") as HTMLElement;
const container = document.getElementById("rd-polls-container") as HTMLElement;

// ── DemoSync ──

const sync = new DemoSync({ filter: ["demo-poll"] });

// Show loading spinner immediately
loadingEl.style.display = "";

// ── Connection events ──

sync.addEventListener("connected", () => {
	connBadge.className = "rd-status rd-status--connected";
	connBadge.textContent = "Connected";
	resetBtn.disabled = false;
});

sync.addEventListener("disconnected", () => {
	connBadge.className = "rd-status rd-status--disconnected";
	connBadge.textContent = "Disconnected";
	resetBtn.disabled = true;
});

// ── Snapshot → render ──

sync.addEventListener("snapshot", ((e: CustomEvent) => {
	const shapes: Record<string, DemoShape> = e.detail.shapes;
	const polls = Object.values(shapes).filter(isPoll);

	// Hide loading
	loadingEl.style.display = "none";

	// Show/hide empty state
	if (polls.length === 0) {
		emptyEl.style.display = "";
		container.innerHTML = "";
		return;
	}
	emptyEl.style.display = "none";

	// Render poll cards
	container.innerHTML = polls.map((poll) => renderPollCard(poll)).join("");
}) as EventListener);

// ── Render a single poll card ──

function renderPollCard(poll: DemoPoll): string {
	const total = poll.options.reduce((sum, opt) => sum + opt.votes, 0);
	const maxVotes = Math.max(...poll.options.map((o) => o.votes), 1);
	const statusBadge =
		poll.status === "active"
			? `<span class="rd-badge rd-badge--orange">Active</span>`
			: `<span class="rd-badge rd-badge--slate">Closed</span>`;

	const optionsHTML = poll.options
		.map((opt, idx) => {
			const pct = total > 0 ? (opt.votes / total) * 100 : 0;
			const barPct = maxVotes > 0 ? (opt.votes / maxVotes) * 100 : 0;

			return `<div style="display:flex; align-items:center; gap:0.75rem">
  <div style="display:flex; align-items:center; gap:0.25rem; flex-shrink:0">
    <button class="rd-btn rd-btn--ghost" data-vote="-1" data-poll="${esc(poll.id)}" data-opt="${idx}" style="width:2rem;height:2rem;padding:0;justify-content:center"${!sync.connected || opt.votes <= 0 ? " disabled" : ""}>&#8722;</button>
    <span style="width:2rem; text-align:center; font-weight:700; font-size:0.875rem">${opt.votes}</span>
    <button class="rd-btn rd-btn--ghost" data-vote="1" data-poll="${esc(poll.id)}" data-opt="${idx}" style="width:2rem;height:2rem;padding:0;justify-content:center"${!sync.connected ? " disabled" : ""}>+</button>
  </div>
  <div style="flex:1; min-width:0">
    <div style="display:flex; justify-content:space-between; margin-bottom:0.25rem">
      <span style="font-size:0.875rem; font-weight:500">${esc(opt.label)}</span>
      <span style="font-size:0.75rem; color:#94a3b8">${Math.round(pct)}%</span>
    </div>
    <div class="rd-progress--sm" style="height:0.625rem; border-radius:9999px; background:#334155; overflow:hidden">
      <div style="height:100%; border-radius:9999px; background:linear-gradient(90deg,#fb923c,#f97316); width:${barPct}%; transition:width 0.3s"></div>
    </div>
  </div>
</div>`;
		})
		.join("");

	return `<div class="rd-card" data-poll-id="${esc(poll.id)}" style="border:2px solid rgba(249,115,22,0.2)">
  <div class="rd-card-header">
    <div class="rd-card-title"><span class="rd-icon">🗳</span> ${esc(poll.question)}</div>
    ${statusBadge}
  </div>
  <div style="padding:0.5rem 1.25rem; display:flex; gap:1rem; font-size:0.875rem; color:#94a3b8">
    <span>👥 ${poll.totalVoters} voter${poll.totalVoters !== 1 ? "s" : ""}</span>
    <span>⏱ ${formatDeadline(poll.endsAt)}</span>
    <span style="margin-left:auto; color:rgba(255,255,255,0.7); font-weight:500">${total} total vote${total !== 1 ? "s" : ""}</span>
  </div>
  <div class="rd-card-body" style="display:flex; flex-direction:column; gap:0.75rem">
    ${optionsHTML}
  </div>
</div>`;
}

// ── Event delegation for vote buttons ──

container.addEventListener("click", (e) => {
	const btn = (e.target as HTMLElement).closest<HTMLButtonElement>("button[data-vote]");
	if (!btn || btn.disabled) return;

	const pollId = btn.dataset.poll!;
	const optIdx = parseInt(btn.dataset.opt!, 10);
	const delta = parseInt(btn.dataset.vote!, 10);

	const shape = sync.shapes[pollId];
	if (!shape || !isPoll(shape)) return;

	const updatedOptions = shape.options.map((opt, i) => {
		if (i !== optIdx) return opt;
		return { ...opt, votes: Math.max(0, opt.votes + delta) };
	});

	sync.updateShape(pollId, { options: updatedOptions });
});

// ── Reset button ──

resetBtn.addEventListener("click", async () => {
	resetBtn.disabled = true;
	try {
		await sync.resetDemo();
	} catch (err) {
		console.error("Reset failed:", err);
	} finally {
		if (sync.connected) resetBtn.disabled = false;
	}
});

// ── Connect ──

sync.connect();
