import { browser } from "$app/environment";
import type { Did } from "@atcute/lexicons/syntax";

// atcute owns oauth sessions; this stores metadata/order and active-account cookies.

export const CURRENT_DID_KEY = "tangled.currentDid";
export const CURRENT_HANDLE_KEY = "tangled.currentHandle";
const ACCOUNTS_KEY = "tangled.accounts";

// appview account cap parity
export const MAX_ACCOUNTS = 20;

export interface AuthAccount {
	did: Did;
	handle: string;
	// unix seconds; appview parity
	addedAt: number;
}

const isDid = (value: unknown): value is Did =>
	typeof value === "string" && value.startsWith("did:");

const isAccount = (value: unknown): value is AuthAccount =>
	!!value &&
	typeof value === "object" &&
	isDid((value as AuthAccount).did) &&
	typeof (value as AuthAccount).handle === "string";

export const loadAccounts = (): AuthAccount[] => {
	if (!browser) return [];
	try {
		const raw = localStorage.getItem(ACCOUNTS_KEY);
		if (!raw) return [];
		const parsed = JSON.parse(raw) as unknown;
		if (!Array.isArray(parsed)) return [];
		return parsed.filter(isAccount).map((account) => ({
			did: account.did,
			handle: account.handle,
			addedAt: typeof account.addedAt === "number" ? account.addedAt : 0
		}));
	} catch {
		return [];
	}
};

export const saveAccounts = (accounts: readonly AuthAccount[]): void => {
	if (!browser) return;
	localStorage.setItem(ACCOUNTS_KEY, JSON.stringify(accounts));
};

// stored sessions are authoritative, metadata only supplies order and handle
export const reconcileAccounts = (
	stored: readonly Did[],
	meta: readonly AuthAccount[]
): AuthAccount[] => {
	const storedSet = new Set(stored);
	const known = new Set(meta.map((account) => account.did));
	const ordered = meta.filter((account) => storedSet.has(account.did));
	for (const did of stored) {
		if (!known.has(did)) {
			ordered.push({ did, handle: did, addedAt: Math.floor(Date.now() / 1000) });
		}
	}
	return ordered;
};

// dedupe by did, preserve insertion order, and keep the original addedAt.
export const upsertAccount = (
	accounts: readonly AuthAccount[],
	account: AuthAccount
): AuthAccount[] => {
	const index = accounts.findIndex((existing) => existing.did === account.did);
	if (index >= 0) {
		const next = accounts.slice();
		next[index] = { ...account, addedAt: accounts[index].addedAt };
		return next;
	}
	if (accounts.length >= MAX_ACCOUNTS) return accounts.slice();
	return [...accounts, account];
};

export const dropAccount = (accounts: readonly AuthAccount[], did: Did): AuthAccount[] =>
	accounts.filter((account) => account.did !== did);

export const persistActive = (did: string, handle: string): void => {
	if (!browser) return;
	localStorage.setItem(CURRENT_DID_KEY, did);
	localStorage.setItem(CURRENT_HANDLE_KEY, handle);
	const secure = location.protocol === "https:" ? "; secure" : "";
	const attrs = `; path=/; max-age=31536000; samesite=lax${secure}`;
	document.cookie = `${CURRENT_DID_KEY}=${encodeURIComponent(did)}${attrs}`;
	document.cookie = `${CURRENT_HANDLE_KEY}=${encodeURIComponent(handle)}${attrs}`;
};

export const clearActive = (): void => {
	if (!browser) return;
	localStorage.removeItem(CURRENT_DID_KEY);
	localStorage.removeItem(CURRENT_HANDLE_KEY);
	document.cookie = `${CURRENT_DID_KEY}=; path=/; max-age=0; samesite=lax`;
	document.cookie = `${CURRENT_HANDLE_KEY}=; path=/; max-age=0; samesite=lax`;
};

export const readActiveDid = (): Did | null => {
	if (!browser) return null;
	const value = localStorage.getItem(CURRENT_DID_KEY);
	return isDid(value) ? value : null;
};
