/**
 * <folk-power-indices> — Coalitional power analysis visualization.
 *
 * Shows Banzhaf power index, Shapley-Shubik power index, raw voting weight,
 * Gini coefficient, HHI concentration, and a power-vs-weight scatter plot.
 * Fetches from EncryptID /api/power-indices endpoint.
 */

interface PowerResult {
	did: string;
	weight: number;
	banzhaf: number;
	shapleyShubik: number;
	swingCount: number;
	pivotalCount: number;
	label?: string;
}

interface PowerData {
	space: string;
	authority: string;
	totalWeight: number;
	playerCount: number;
	giniCoefficient: number;
	herfindahlIndex: number;
	lastComputed: number;
	results: PowerResult[];
}

const AUTHORITIES = ["gov-ops", "fin-ops", "dev-ops"] as const;
const AUTHORITY_META: Record<string, { label: string; color: string }> = {
	"gov-ops": { label: "Governance", color: "#a78bfa" },
	"fin-ops": { label: "Economic", color: "#10b981" },
	"dev-ops": { label: "Technical", color: "#3b82f6" },
};

const ENCRYPTID_URL = (typeof window !== "undefined" && (window as any).__ENCRYPTID_URL) || "";

class FolkPowerIndices extends HTMLElement {
	private shadow: ShadowRoot;
	private space = "";
	private authority = "gov-ops";
	private data: PowerData | null = null;
	private loading = true;
	private error = "";
	private userMap = new Map<string, string>(); // did → display name

	constructor() {
		super();
		this.shadow = this.attachShadow({ mode: "open" });
	}

	static get observedAttributes() { return ["space", "authority"]; }

	attributeChangedCallback(name: string, _old: string, val: string) {
		if (name === "space" && val !== this.space) { this.space = val; this.fetchData(); }
		if (name === "authority" && val !== this.authority) { this.authority = val; this.fetchData(); }
	}

	connectedCallback() {
		this.space = this.getAttribute("space") || "demo";
		this.authority = this.getAttribute("authority") || "gov-ops";
		this.fetchData();
	}

	private async fetchData() {
		this.loading = true;
		this.error = "";
		this.render();

		try {
			// Fetch user directory for name resolution
			const usersUrl = ENCRYPTID_URL
				? `${ENCRYPTID_URL}/api/users/directory?space=${encodeURIComponent(this.space)}`
				: `/api/power-indices/users?space=${encodeURIComponent(this.space)}`;
			try {
				const uRes = await fetch(ENCRYPTID_URL ? usersUrl : `/rnetwork/api/users?space=${encodeURIComponent(this.space)}`);
				if (uRes.ok) {
					const uData = await uRes.json();
					for (const u of uData.users || []) {
						this.userMap.set(u.did, u.displayName || u.username || u.did.slice(0, 12));
					}
				}
			} catch { /* best-effort */ }

			const piUrl = ENCRYPTID_URL
				? `${ENCRYPTID_URL}/api/power-indices?space=${encodeURIComponent(this.space)}&authority=${encodeURIComponent(this.authority)}`
				: `/rnetwork/api/power-indices?space=${encodeURIComponent(this.space)}&authority=${encodeURIComponent(this.authority)}`;
			const res = await fetch(piUrl);
			if (!res.ok) throw new Error(`HTTP ${res.status}`);
			this.data = await res.json();
			this.loading = false;
		} catch (e) {
			this.error = (e as Error).message;
			this.loading = false;
		}
		this.render();
	}

	private render() {
		const meta = AUTHORITY_META[this.authority] || { label: this.authority, color: "#888" };

		this.shadow.innerHTML = `
		<style>
			:host { display: block; font-family: system-ui, -apple-system, sans-serif; color: #e2e8f0; }
			.container { padding: 16px; }
			.tabs { display: flex; gap: 4px; margin-bottom: 16px; }
			.tab {
				padding: 6px 16px; border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 500;
				background: rgba(255,255,255,0.06); border: 1px solid rgba(255,255,255,0.1); color: #94a3b8;
				transition: all 0.2s;
			}
			.tab:hover { background: rgba(255,255,255,0.1); }
			.tab.active { background: ${meta.color}22; border-color: ${meta.color}; color: ${meta.color}; }
			.metrics { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 12px; margin-bottom: 20px; }
			.metric {
				background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.08); border-radius: 8px;
				padding: 12px; text-align: center;
			}
			.metric-value { font-size: 24px; font-weight: 700; color: #f1f5f9; }
			.metric-label { font-size: 11px; color: #64748b; margin-top: 4px; text-transform: uppercase; letter-spacing: 0.5px; }
			.gauge-bar { height: 4px; border-radius: 2px; background: rgba(255,255,255,0.1); margin-top: 8px; overflow: hidden; }
			.gauge-fill { height: 100%; border-radius: 2px; transition: width 0.6s ease; }

			.chart-section { margin-bottom: 24px; }
			.section-title { font-size: 13px; font-weight: 600; color: #94a3b8; margin-bottom: 10px; text-transform: uppercase; letter-spacing: 0.5px; }

			/* Bar chart */
			.bar-chart { display: flex; flex-direction: column; gap: 6px; }
			.bar-row { display: flex; align-items: center; gap: 8px; font-size: 12px; }
			.bar-label { width: 120px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: #cbd5e1; text-align: right; flex-shrink: 0; }
			.bar-group { flex: 1; display: flex; flex-direction: column; gap: 2px; }
			.bar-track { height: 14px; background: rgba(255,255,255,0.04); border-radius: 3px; overflow: hidden; position: relative; }
			.bar-fill { height: 100%; border-radius: 3px; transition: width 0.5s ease; display: flex; align-items: center; padding-left: 4px; font-size: 10px; color: rgba(255,255,255,0.8); min-width: 0; }
			.bar-fill.weight { background: rgba(148,163,184,0.4); }
			.bar-fill.banzhaf { background: ${meta.color}aa; }
			.bar-fill.shapley { background: ${meta.color}55; }
			.bar-value { font-size: 11px; color: #64748b; width: 48px; text-align: right; flex-shrink: 0; }

			/* Scatter plot */
			.scatter-container { position: relative; width: 100%; padding-top: 100%; max-width: 400px; max-height: 400px; }
			.scatter-svg { position: absolute; inset: 0; width: 100%; height: 100%; }
			.scatter-point { cursor: pointer; transition: r 0.2s; }
			.scatter-point:hover { r: 6; }

			/* Legend */
			.legend { display: flex; gap: 16px; margin-bottom: 12px; flex-wrap: wrap; }
			.legend-item { display: flex; align-items: center; gap: 4px; font-size: 11px; color: #94a3b8; }
			.legend-swatch { width: 12px; height: 12px; border-radius: 2px; }

			.loading, .error-msg { text-align: center; padding: 40px; color: #64748b; }
			.error-msg { color: #f87171; }
			.empty-state { text-align: center; padding: 40px; color: #64748b; }
			.tooltip {
				position: fixed; z-index: 100; background: #1e293b; border: 1px solid rgba(255,255,255,0.15);
				border-radius: 6px; padding: 8px 12px; font-size: 12px; pointer-events: none;
				box-shadow: 0 4px 12px rgba(0,0,0,0.4); display: none;
			}
		</style>
		<div class="container">
			${this.renderTabs()}
			${this.loading ? '<div class="loading">Computing power indices...</div>'
			: this.error ? `<div class="error-msg">Error: ${this.error}</div>`
			: this.data && this.data.results.length > 0 ? this.renderContent()
			: '<div class="empty-state">No delegation data available for power analysis.</div>'}
		</div>
		<div class="tooltip" id="tooltip"></div>
		`;

		this.attachEvents();
	}

	private renderTabs(): string {
		return `<div class="tabs">${AUTHORITIES.map(a => {
			const m = AUTHORITY_META[a];
			return `<div class="tab ${a === this.authority ? 'active' : ''}" data-authority="${a}">${m.label}</div>`;
		}).join("")}</div>`;
	}

	private renderContent(): string {
		if (!this.data) return "";
		const d = this.data;
		const effectiveN = d.herfindahlIndex > 0 ? (1 / d.herfindahlIndex).toFixed(1) : "—";
		const giniColor = d.giniCoefficient > 0.6 ? "#f87171" : d.giniCoefficient > 0.3 ? "#fbbf24" : "#34d399";
		const hhiColor = d.herfindahlIndex > 0.25 ? "#f87171" : d.herfindahlIndex > 0.15 ? "#fbbf24" : "#34d399";

		return `
		${this.renderMetrics(d, effectiveN, giniColor, hhiColor)}
		<div class="chart-section">
			<div class="section-title">Power Distribution</div>
			${this.renderLegend()}
			${this.renderBarChart(d)}
		</div>
		<div class="chart-section">
			<div class="section-title">Power vs Weight (deviation from proportionality)</div>
			${this.renderScatter(d)}
		</div>
		`;
	}

	private renderMetrics(d: PowerData, effectiveN: string, giniColor: string, hhiColor: string): string {
		return `<div class="metrics">
			<div class="metric">
				<div class="metric-value">${d.playerCount}</div>
				<div class="metric-label">Voters</div>
			</div>
			<div class="metric">
				<div class="metric-value">${effectiveN}</div>
				<div class="metric-label">Effective Voters</div>
				<div class="gauge-bar"><div class="gauge-fill" style="width:${Math.min(100, (1/d.herfindahlIndex/d.playerCount)*100)}%; background:${hhiColor}"></div></div>
			</div>
			<div class="metric">
				<div class="metric-value">${(d.giniCoefficient * 100).toFixed(0)}%</div>
				<div class="metric-label">Gini (Power)</div>
				<div class="gauge-bar"><div class="gauge-fill" style="width:${d.giniCoefficient*100}%; background:${giniColor}"></div></div>
			</div>
			<div class="metric">
				<div class="metric-value">${(d.herfindahlIndex * 100).toFixed(0)}%</div>
				<div class="metric-label">HHI Concentration</div>
				<div class="gauge-bar"><div class="gauge-fill" style="width:${d.herfindahlIndex*100}%; background:${hhiColor}"></div></div>
			</div>
		</div>`;
	}

	private renderLegend(): string {
		const meta = AUTHORITY_META[this.authority];
		return `<div class="legend">
			<div class="legend-item"><div class="legend-swatch" style="background:rgba(148,163,184,0.4)"></div>Raw Weight</div>
			<div class="legend-item"><div class="legend-swatch" style="background:${meta.color}aa"></div>Banzhaf Index</div>
			<div class="legend-item"><div class="legend-swatch" style="background:${meta.color}55"></div>Shapley-Shubik</div>
		</div>`;
	}

	private renderBarChart(d: PowerData): string {
		const maxVal = Math.max(...d.results.map(r => Math.max(r.weight / d.totalWeight, r.banzhaf, r.shapleyShubik)), 0.01);
		const top = d.results.slice(0, 20); // show top 20

		return `<div class="bar-chart">${top.map(r => {
			const name = this.userMap.get(r.did) || r.label || r.did.slice(0, 16);
			const wPct = ((r.weight / d.totalWeight) / maxVal * 100).toFixed(1);
			const bPct = (r.banzhaf / maxVal * 100).toFixed(1);
			const sPct = (r.shapleyShubik / maxVal * 100).toFixed(1);
			return `<div class="bar-row" data-did="${r.did}">
				<div class="bar-label" title="${name}">${name}</div>
				<div class="bar-group">
					<div class="bar-track"><div class="bar-fill weight" style="width:${wPct}%">${(r.weight/d.totalWeight*100).toFixed(1)}%</div></div>
					<div class="bar-track"><div class="bar-fill banzhaf" style="width:${bPct}%">${(r.banzhaf*100).toFixed(1)}%</div></div>
					<div class="bar-track"><div class="bar-fill shapley" style="width:${sPct}%">${(r.shapleyShubik*100).toFixed(1)}%</div></div>
				</div>
				<div class="bar-value">${(r.banzhaf*100).toFixed(1)}%</div>
			</div>`;
		}).join("")}</div>`;
	}

	private renderScatter(d: PowerData): string {
		// SVG scatter: X = weight%, Y = Banzhaf%
		const pad = 40;
		const size = 300;
		const meta = AUTHORITY_META[this.authority];

		const points = d.results.map(r => ({
			x: r.weight / d.totalWeight,
			y: r.banzhaf,
			did: r.did,
		}));

		const maxX = Math.max(...points.map(p => p.x), 0.01) * 1.1;
		const maxY = Math.max(...points.map(p => p.y), 0.01) * 1.1;

		const sx = (v: number) => pad + (v / maxX) * (size - pad * 2);
		const sy = (v: number) => size - pad - (v / maxY) * (size - pad * 2);

		// Diagonal line (proportional power)
		const diagEnd = Math.min(maxX, maxY);

		const pointsSvg = points.map(p => {
			const overpower = p.y > p.x / d.totalWeight * d.playerCount; // more power than weight would suggest
			const color = p.y > (p.x / d.totalWeight) * 1.2 ? "#f87171" : p.y < (p.x / d.totalWeight) * 0.8 ? "#60a5fa" : "#94a3b8";
			return `<circle class="scatter-point" cx="${sx(p.x)}" cy="${sy(p.y)}" r="4" fill="${color}" data-did="${p.did}" />`;
		}).join("");

		return `<div class="scatter-container" style="max-width:${size}px; padding-top:${size}px;">
			<svg class="scatter-svg" viewBox="0 0 ${size} ${size}">
				<!-- axes -->
				<line x1="${pad}" y1="${size-pad}" x2="${size-pad}" y2="${size-pad}" stroke="rgba(255,255,255,0.15)" />
				<line x1="${pad}" y1="${pad}" x2="${pad}" y2="${size-pad}" stroke="rgba(255,255,255,0.15)" />
				<!-- diagonal (proportional) -->
				<line x1="${sx(0)}" y1="${sy(0)}" x2="${sx(diagEnd)}" y2="${sy(diagEnd)}" stroke="rgba(255,255,255,0.1)" stroke-dasharray="4,4" />
				<!-- labels -->
				<text x="${size/2}" y="${size-6}" fill="#64748b" font-size="10" text-anchor="middle">Weight %</text>
				<text x="10" y="${size/2}" fill="#64748b" font-size="10" text-anchor="middle" transform="rotate(-90 10 ${size/2})">Banzhaf %</text>
				${pointsSvg}
			</svg>
		</div>
		<div class="legend" style="margin-top:8px">
			<div class="legend-item"><div class="legend-swatch" style="background:#f87171"></div>Overrepresented</div>
			<div class="legend-item"><div class="legend-swatch" style="background:#94a3b8"></div>Proportional</div>
			<div class="legend-item"><div class="legend-swatch" style="background:#60a5fa"></div>Underrepresented</div>
		</div>`;
	}

	private attachEvents() {
		// Tab clicks
		this.shadow.querySelectorAll<HTMLElement>(".tab").forEach(tab => {
			tab.addEventListener("click", () => {
				const auth = tab.dataset.authority;
				if (auth && auth !== this.authority) {
					this.authority = auth;
					this.setAttribute("authority", auth);
					this.fetchData();
				}
			});
		});

		// Tooltip on scatter points
		const tooltip = this.shadow.getElementById("tooltip");
		this.shadow.querySelectorAll<SVGCircleElement>(".scatter-point").forEach(pt => {
			pt.addEventListener("mouseenter", (e) => {
				if (!tooltip || !this.data) return;
				const did = pt.dataset.did || "";
				const r = this.data.results.find(x => x.did === did);
				if (!r) return;
				const name = this.userMap.get(did) || did.slice(0, 16);
				tooltip.innerHTML = `<b>${name}</b><br>Weight: ${(r.weight / this.data.totalWeight * 100).toFixed(1)}%<br>Banzhaf: ${(r.banzhaf * 100).toFixed(1)}%<br>Shapley-Shubik: ${(r.shapleyShubik * 100).toFixed(1)}%<br>Swings: ${r.swingCount}`;
				tooltip.style.display = "block";
				tooltip.style.left = `${(e as MouseEvent).clientX + 12}px`;
				tooltip.style.top = `${(e as MouseEvent).clientY - 10}px`;
			});
			pt.addEventListener("mouseleave", () => {
				if (tooltip) tooltip.style.display = "none";
			});
		});
	}
}

customElements.define("folk-power-indices", FolkPowerIndices);
