/**
 * Manual API verification script for RHL-008.
 *
 * What this does:
 * - Uses the frontend apiClient helpers against a real running app instance.
 * - Implements a tiny cookie jar so login sessions work in Node (fetch has no cookie jar).
 * - Runs a happy-path flow:
 *   login -> branches -> years -> months -> days -> files -> logout
 * - Also runs a few negative tests (401/403/400/404).
 *
 * Usage:
 *   node scripts/manual-api-client-flow.mjs \
 *     --baseUrl=http://localhost:3000 \
 *     --username=branchuser \
 *     --password=secret-password \
 *     --branch=NL01
 */

import * as apiClientNs from "../lib/frontend/apiClient.js";

/**
 * Compatibility layer:
 * - If apiClient.js is loaded as CommonJS, Node exposes it as { default: module.exports }.
 * - If apiClient.js is loaded as ESM, Node exposes named exports directly.
 */
const apiClient = apiClientNs.default ?? apiClientNs;

const {
	apiFetch,
	login,
	logout,
	getBranches,
	getYears,
	getMonths,
	getDays,
	getFiles,
	ApiClientError,
} = apiClient;

function getArg(name, fallback = null) {
	const prefix = `--${name}=`;
	const hit = process.argv.find((a) => a.startsWith(prefix));
	if (!hit) return fallback;
	return hit.slice(prefix.length);
}

function pickLatest(items) {
	if (!Array.isArray(items) || items.length === 0) return null;
	// Backend currently returns ascending; "latest" is the last one.
	return items[items.length - 1];
}

function logStep(title) {
	console.log(`\n=== ${title} ===`);
}

/**
 * Minimal cookie jar:
 * - Extracts auth_session from Set-Cookie
 * - Sends it back via Cookie header on subsequent requests
 */
function createCookieJarFetch() {
	const jar = new Map(); // cookieName -> cookieValue

	return async function cookieFetch(url, init = {}) {
		const headers = new Headers(init.headers || {});

		// Attach cookies (only what we stored).
		if (jar.size > 0) {
			const cookieHeader = Array.from(jar.entries())
				.map(([k, v]) => `${k}=${v}`)
				.join("; ");
			headers.set("cookie", cookieHeader);
		}

		const res = await fetch(url, { ...init, headers });

		// Node/undici provides getSetCookie() in many versions. Fallback to get('set-cookie').
		const setCookies =
			typeof res.headers.getSetCookie === "function"
				? res.headers.getSetCookie()
				: res.headers.get("set-cookie")
				? [res.headers.get("set-cookie")]
				: [];

		for (const sc of setCookies) {
			if (!sc) continue;

			// We only need auth_session for this project.
			const match = sc.match(/(?:^|;\s*)auth_session=([^;]+)/);
			if (match && match[1]) {
				jar.set("auth_session", match[1]);
			}
		}

		return res;
	};
}

async function expectApiError(promise, { status, code }) {
	try {
		await promise;
		console.error("Expected an error, but the call succeeded.");
		process.exitCode = 1;
	} catch (err) {
		if (!(err instanceof ApiClientError)) {
			console.error("Expected ApiClientError, got:", err);
			process.exitCode = 1;
			return;
		}

		console.log("Got expected error:", {
			status: err.status,
			code: err.code,
			message: err.message,
		});

		if (status !== undefined && err.status !== status) {
			console.error(`Expected status=${status}, got ${err.status}`);
			process.exitCode = 1;
		}
		if (code !== undefined && err.code !== code) {
			console.error(`Expected code=${code}, got ${err.code}`);
			process.exitCode = 1;
		}
	}
}

async function main() {
	const baseUrl = getArg("baseUrl", "http://localhost:3000");
	const username = getArg("username");
	const password = getArg("password");
	const preferredBranch = getArg("branch"); // optional

	if (!username || !password) {
		console.error(
			"Missing credentials. Provide --username=... and --password=..."
		);
		process.exit(1);
	}

	const fetchImpl = createCookieJarFetch();

	logStep("NEGATIVE: Unauthenticated call should be 401");
	await expectApiError(getBranches({ baseUrl, fetchImpl }), {
		status: 401,
		code: "AUTH_UNAUTHENTICATED",
	});

	logStep("LOGIN");
	const loginRes = await login({ username, password }, { baseUrl, fetchImpl });
	console.log("login:", loginRes);

	logStep("OPTIONAL: /api/auth/me (if implemented)");
	try {
		const me = await apiFetch("/api/auth/me", { baseUrl, fetchImpl });
		console.log("me:", me);
	} catch (err) {
		// If the endpoint doesn't exist, Next may return HTML 404 -> CLIENT_HTTP_ERROR.
		console.log("me endpoint not available (ok):", {
			status: err?.status,
			code: err?.code,
		});
	}

	logStep("GET BRANCHES");
	const branchesRes = await getBranches({ baseUrl, fetchImpl });
	console.log("branches:", branchesRes);

	const branch =
		preferredBranch ||
		(Array.isArray(branchesRes?.branches) ? branchesRes.branches[0] : null);

	if (!branch) {
		console.error("No branch found. Check NAS mount / fixtures.");
		process.exit(1);
	}

	logStep("NEGATIVE: Forbidden branch access should be 403 (branch users)");
	// Even if only one branch exists, NL99 should trigger forbidden for branch role.
	await expectApiError(getYears("NL99", { baseUrl, fetchImpl }), {
		status: 403,
		code: "AUTH_FORBIDDEN_BRANCH",
	});

	logStep(`GET YEARS for ${branch}`);
	const yearsRes = await getYears(branch, { baseUrl, fetchImpl });
	console.log("years:", yearsRes);

	const year = pickLatest(yearsRes?.years);
	if (!year) {
		console.error("No year folders found for branch:", branch);
		process.exit(1);
	}

	logStep("NEGATIVE: Not found year should be 404 (authorized)");
	await expectApiError(getMonths(branch, "2099", { baseUrl, fetchImpl }), {
		status: 404,
		code: "FS_NOT_FOUND",
	});

	logStep(`GET MONTHS for ${branch}/${year}`);
	const monthsRes = await getMonths(branch, year, { baseUrl, fetchImpl });
	console.log("months:", monthsRes);

	const month = pickLatest(monthsRes?.months);
	if (!month) {
		console.error("No month folders found for:", branch, year);
		process.exit(1);
	}

	logStep(`GET DAYS for ${branch}/${year}/${month}`);
	const daysRes = await getDays(branch, year, month, { baseUrl, fetchImpl });
	console.log("days:", daysRes);

	const day = pickLatest(daysRes?.days);
	if (!day) {
		console.error("No day folders found for:", branch, year, month);
		process.exit(1);
	}

	logStep(
		"NEGATIVE: Validation error on /api/files (missing query params) -> 400"
	);
	await expectApiError(apiFetch("/api/files", { baseUrl, fetchImpl }), {
		status: 400,
		code: "VALIDATION_MISSING_QUERY",
	});

	logStep(`GET FILES for ${branch}/${year}/${month}/${day}`);
	const filesRes = await getFiles(branch, year, month, day, {
		baseUrl,
		fetchImpl,
	});
	console.log("files:", filesRes);

	logStep("LOGOUT");
	const logoutRes = await logout({ baseUrl, fetchImpl });
	console.log("logout:", logoutRes);

	console.log("\nDone.");
}

main().catch((err) => {
	console.error("Script failed:", err);
	process.exit(1);
});
