import { api, protectedFetch } from "./api.js?v=__ASSET_VERSION__";
import { initSideNav, loadNavUser } from "./nav.js?v=__ASSET_VERSION__";
import { showToast } from "./toast.js?v=__ASSET_VERSION__";

let currentUsers = [];
let planFilterUserId = null;
let currentUserId = null;

function fmtDate(value) {
	return value ? new Date(value).toLocaleString() : "—";
}

async function loadMetrics() {
	const grid = document.getElementById("metrics");
	try {
		const { metrics } = await api("/api/admin/metrics");
		const tiles = [
			["Users", metrics.totalUsers],
			["Plans", metrics.totalPlans],
			["Plans updated (7d)", metrics.plansUpdatedLast7Days],
			["Active sessions", metrics.activeSessions],
		];
		grid.replaceChildren(
			...tiles.map(([label, value]) => {
				const tile = document.createElement("div");
				tile.className = "stat-tile";
				tile.innerHTML = `<div class="stat-tile-label"></div><div class="stat-tile-value"></div>`;
				tile.querySelector(".stat-tile-label").textContent = label;
				tile.querySelector(".stat-tile-value").textContent = value;
				return tile;
			}),
		);
	} catch {
		grid.textContent = "";
	}
}

async function loadResetLinks() {
	try {
		const { links } = await api("/api/admin/password-resets");
		const card = document.getElementById("reset-links-card");
		if (!links.length) {
			card.hidden = true;
			return;
		}
		card.hidden = false;
		document.getElementById("resets-body").replaceChildren(
			...links.map((link) => {
				const tr = document.createElement("tr");
				const email = document.createElement("td");
				email.textContent = link.email;
				const url = document.createElement("td");
				// Not a clickable <a>: an admin's own session immediately 302s /reset to /app,
				// so the link is only useful copied out and handed to the actual user.
				const copyBtn = document.createElement("button");
				copyBtn.className = "ghost-btn small";
				copyBtn.type = "button";
				copyBtn.textContent = "Copy link";
				copyBtn.addEventListener("click", async () => {
					try {
						await navigator.clipboard.writeText(link.url);
						copyBtn.textContent = "Copied!";
					} catch {
						copyBtn.textContent = link.url;
					}
					setTimeout(() => (copyBtn.textContent = "Copy link"), 2000);
				});
				url.append(copyBtn);
				const expires = document.createElement("td");
				expires.textContent = fmtDate(link.expiresAt);
				tr.append(email, url, expires);
				return tr;
			}),
		);
	} catch {
		/* optional feature; ignore failures */
	}
}

function wireSignupToggle() {
	const toggle = document.getElementById("signup-toggle");
	toggle.addEventListener("change", async () => {
		try {
			await api("/api/admin/signup-enabled", {
				method: "PUT",
				body: JSON.stringify({ enabled: toggle.checked }),
			});
			showToast(toggle.checked ? "Signups enabled." : "Signups disabled.");
		} catch (error) {
			toggle.checked = !toggle.checked;
			showToast(error.message, "fail");
		}
	});
}

async function loadLlm() {
	const select = document.getElementById("llm-model-select");
	const note = document.getElementById("llm-model-note");
	try {
		const { current, models, modelsError } = await api("/api/admin/llm");
		const auto = document.createElement("option");
		auto.value = "";
		auto.textContent = "Auto (first available model)";
		select.replaceChildren(
			auto,
			...models.map((model) => {
				const option = document.createElement("option");
				option.value = model.key;
				option.textContent = `${model.name} (${model.provider})`;
				return option;
			}),
		);
		// A previously-saved model that is no longer configured must still show as selected
		// rather than silently displaying Auto while the server keeps trying the saved one.
		if (current && !models.some((model) => model.key === current)) {
			const missing = document.createElement("option");
			missing.value = current;
			missing.textContent = `${current} (no longer configured)`;
			select.append(missing);
		}
		select.value = current;
		note.textContent = modelsError
			? "No models are configured on the server — reviews and prefill will fail until one is set up."
			: current
				? ""
				: models.length
					? `Auto currently resolves to ${models[0].name} (${models[0].provider}).`
					: "";
	} catch {
		note.textContent = "Could not load LLM settings.";
	}
}

function wireLlmSave() {
	const button = document.getElementById("llm-model-save");
	button.addEventListener("click", async () => {
		button.disabled = true;
		try {
			const model = document.getElementById("llm-model-select").value;
			await api("/api/admin/llm", {
				method: "PUT",
				body: JSON.stringify({ model }),
			});
			showToast(model ? "LLM model saved." : "LLM model set to auto.");
			await loadLlm();
		} catch (error) {
			showToast(error.message, "fail");
		} finally {
			button.disabled = false;
		}
	});
}

function wireBackupImport() {
	const input = document.getElementById("backup-import-file");
	const note = document.getElementById("backup-note");
	input.addEventListener("change", async (event) => {
		const file = event.target.files?.[0];
		event.target.value = "";
		if (!file) return;
		let backup;
		try {
			backup = JSON.parse(await file.text());
		} catch {
			note.textContent = "That file is not valid JSON.";
			return;
		}
		if (backup.format !== "roast-planner-backup") {
			note.textContent =
				"That file is not a Roast Planner backup export (expected the file downloaded by “Export full backup”).";
			return;
		}
		const userCount = backup.tables?.users?.length ?? 0;
		if (
			!confirm(
				`Replace the ENTIRE database with "${file.name}" (${userCount} user${userCount === 1 ? "" : "s"}, exported ${backup.exportedAt ?? "unknown date"})?\n\nEverything currently stored will be deleted. This cannot be undone.`,
			)
		)
			return;
		note.textContent = "Importing…";
		try {
			const result = await api("/api/admin/backup/import", {
				method: "POST",
				body: JSON.stringify(backup),
			});
			note.textContent = `Imported: ${Object.entries(result.counts)
				.map(([table, count]) => `${table} ${count}`)
				.join(", ")}.`;
			showToast("Backup imported.");
			if (!result.sessionKept) {
				alert("The restored data does not include your current session's account — log in again with the restored credentials.");
				location.assign("/login");
				return;
			}
			await Promise.all([loadMetrics(), loadUsers(), loadPlans(), loadAudit()]);
		} catch (error) {
			note.textContent = `Import failed: ${error.message}. Nothing was changed.`;
			showToast(error.message, "fail");
		}
	});
}

function roleBadge(user) {
	return user.role === "admin"
		? `<span class="badge badge-admin">Admin</span>`
		: `<span class="badge badge-user">User</span>`;
}
function statusBadge(user) {
	return user.disabled_at
		? `<span class="badge badge-disabled">Disabled</span>`
		: `<span class="badge badge-current">Active</span>`;
}

async function loadUsers() {
	const body = document.getElementById("users-body");
	try {
		const { users, signupEnabled } = await api("/api/admin/users");
		currentUsers = users;
		document.getElementById("signup-toggle").checked = signupEnabled;
		renderUsers();
	} catch {
		body.innerHTML = `<tr><td colspan="6" class="empty-state">Could not load users.</td></tr>`;
	}
}

function renderUsers() {
	const body = document.getElementById("users-body");
	const query = document
		.getElementById("user-search")
		.value.trim()
		.toLowerCase();
	const filtered = query
		? currentUsers.filter((u) => u.email.toLowerCase().includes(query))
		: currentUsers;
	if (!filtered.length) {
		body.innerHTML = `<tr><td colspan="6" class="empty-state">No matching users.</td></tr>`;
		return;
	}
	body.replaceChildren(
		...filtered.map((user) => {
			const tr = document.createElement("tr");
			const email = document.createElement("td");
			email.textContent = user.email;
			const role = document.createElement("td");
			role.innerHTML = roleBadge(user);
			const status = document.createElement("td");
			status.innerHTML = statusBadge(user);
			const count = document.createElement("td");
			count.className = "num";
			count.textContent = user.plan_count;
			const joined = document.createElement("td");
			joined.textContent = fmtDate(user.created_at);
			const actions = document.createElement("td");
			actions.className = "data-table-actions";

			const isBootstrapAdmin = user.email === "snowspeeder@gmail.com";
			const isSelf = user.id === currentUserId;
			const viewPlans = document.createElement("button");
			viewPlans.className = "ghost-btn small";
			viewPlans.type = "button";
			viewPlans.textContent = "View plans";
			viewPlans.addEventListener("click", () => filterPlansByUser(user));
			actions.append(viewPlans);

			// The server refuses role/disable/delete on the bootstrap admin and on your own
			// account (so an admin can never lock themselves out) — don't offer buttons the
			// server will always reject.
			if (!isBootstrapAdmin && !isSelf) {
				const roleBtn = document.createElement("button");
				roleBtn.className = "ghost-btn small";
				roleBtn.type = "button";
				const promoting = user.role !== "admin";
				roleBtn.textContent = promoting ? "Promote" : "Demote";
				roleBtn.addEventListener("click", async () => {
					if (
						promoting &&
						!confirm(
							`Grant ${user.email} full admin access, including user management and password-reset links?`,
						)
					)
						return;
					try {
						await api(`/api/admin/users/${user.id}/role`, {
							method: "PUT",
							body: JSON.stringify({
								role: promoting ? "admin" : "user",
							}),
						});
						showToast("Role updated.");
						loadUsers();
					} catch (error) {
						showToast(error.message, "fail");
					}
				});
				const disableBtn = document.createElement("button");
				disableBtn.className = "ghost-btn small";
				disableBtn.type = "button";
				const disabling = !user.disabled_at;
				disableBtn.textContent = disabling ? "Disable" : "Enable";
				disableBtn.addEventListener("click", async () => {
					if (
						disabling &&
						!confirm(`Disable ${user.email}? This signs them out everywhere immediately.`)
					)
						return;
					try {
						await api(`/api/admin/users/${user.id}/disabled`, {
							method: "PUT",
							body: JSON.stringify({ disabled: disabling }),
						});
						showToast(disabling ? "User disabled." : "User enabled.");
						loadUsers();
					} catch (error) {
						showToast(error.message, "fail");
					}
				});
				const deleteBtn = document.createElement("button");
				deleteBtn.className = "ghost-btn small";
				deleteBtn.type = "button";
				deleteBtn.textContent = "Delete";
				deleteBtn.addEventListener("click", async () => {
					if (
						!confirm(
							`Permanently delete ${user.email} and all of their plans?`,
						)
					)
						return;
					try {
						await api(`/api/admin/users/${user.id}`, { method: "DELETE" });
						showToast("User deleted.");
						loadUsers();
						loadMetrics();
					} catch (error) {
						showToast(error.message, "fail");
					}
				});
				actions.append(roleBtn, disableBtn, deleteBtn);
			}

			tr.append(email, role, status, count, joined, actions);
			return tr;
		}),
	);
}

async function loadPlans() {
	const body = document.getElementById("plans-body");
	try {
		const url = planFilterUserId
			? `/api/admin/plans?user=${encodeURIComponent(planFilterUserId)}`
			: "/api/admin/plans";
		const { plans } = await api(url);
		if (!plans.length) {
			body.innerHTML = `<tr><td colspan="3" class="empty-state">No plans.</td></tr>`;
			return;
		}
		body.replaceChildren(
			...plans.map((plan) => {
				const tr = document.createElement("tr");
				const owner = document.createElement("td");
				owner.textContent = plan.email;
				const title = document.createElement("td");
				title.textContent = plan.title || "Untitled plan";
				const updated = document.createElement("td");
				updated.textContent = fmtDate(plan.updated_at);
				tr.append(owner, title, updated);
				return tr;
			}),
		);
	} catch {
		body.innerHTML = `<tr><td colspan="3" class="empty-state">Could not load plans.</td></tr>`;
	}
}

function filterPlansByUser(user) {
	planFilterUserId = user.id;
	document.getElementById("clear-plan-filter").classList.remove("hidden");
	document.getElementById("plans").scrollIntoView({ behavior: "smooth" });
	loadPlans();
}

async function loadAudit() {
	const body = document.getElementById("audit-body");
	try {
		const { events } = await api("/api/admin/audit");
		if (!events.length) {
			body.innerHTML = `<tr><td colspan="4" class="empty-state">No activity yet.</td></tr>`;
			return;
		}
		body.replaceChildren(
			...events.map((event) => {
				const tr = document.createElement("tr");
				const when = document.createElement("td");
				when.textContent = fmtDate(event.created_at);
				const actor = document.createElement("td");
				actor.textContent = event.actor_email || "—";
				const action = document.createElement("td");
				action.textContent = event.action.replaceAll("_", " ");
				const target = document.createElement("td");
				target.textContent = event.target || "—";
				tr.append(when, actor, action, target);
				return tr;
			}),
		);
	} catch {
		body.innerHTML = `<tr><td colspan="4" class="empty-state">Could not load activity.</td></tr>`;
	}
}

document.getElementById("btn-logout").addEventListener("click", async () => {
	await protectedFetch("/api/auth/logout", { method: "POST" });
	location.assign("/");
});
document.getElementById("user-search").addEventListener("input", renderUsers);
document.getElementById("clear-plan-filter").addEventListener("click", () => {
	planFilterUserId = null;
	document.getElementById("clear-plan-filter").classList.add("hidden");
	loadPlans();
});

async function init() {
	initSideNav();
	const user = await loadNavUser();
	if (!user) return;
	currentUserId = user.id;
	wireSignupToggle();
	wireLlmSave();
	wireBackupImport();
	await Promise.all([
		loadMetrics(),
		loadResetLinks(),
		loadUsers(),
		loadPlans(),
		loadAudit(),
		loadLlm(),
	]);
}

init();
