import { isValidBranchParam } from "@/lib/frontend/params";
import { buildSearchHref } from "@/lib/frontend/search/pageHelpers";
import {
	SEARCH_SCOPE,
	SEARCH_LIMITS,
	DEFAULT_SEARCH_LIMIT,
	serializeSearchUrlState,
} from "@/lib/frontend/search/urlState";

export const SEARCH_HISTORY_STORAGE_PREFIX = "rhl.searchHistory.v1";
export const DEFAULT_SEARCH_HISTORY_MAX_ITEMS = 10;

function normalizeUserId(userId) {
	if (typeof userId !== "string") return null;

	const trimmed = userId.trim();
	return trimmed ? trimmed : null;
}

function normalizeQuery(value) {
	if (typeof value !== "string") return null;

	const trimmed = value.trim();
	return trimmed ? trimmed : null;
}

function normalizeRouteBranch(value) {
	if (typeof value !== "string") return null;

	const normalized = value.trim().toUpperCase();
	if (!normalized) return null;
	if (!isValidBranchParam(normalized)) return null;

	return normalized;
}

function toBranchNumber(branchId) {
	const match = /^NL(\d+)$/i.exec(String(branchId || "").trim());
	if (!match) return null;

	const n = Number(match[1]);
	return Number.isInteger(n) ? n : null;
}

function compareBranchIds(a, b) {
	const aa = String(a || "");
	const bb = String(b || "");

	const na = toBranchNumber(aa);
	const nb = toBranchNumber(bb);

	if (na !== null && nb !== null) return na - nb;
	return aa.localeCompare(bb, "en");
}

function normalizeBranches(value) {
	const source = Array.isArray(value)
		? value
		: typeof value === "string"
			? value.split(",")
			: [];

	const normalized = source
		.map((item) => String(item || "").trim().toUpperCase())
		.filter((item) => item && isValidBranchParam(item));

	const unique = Array.from(new Set(normalized));
	unique.sort(compareBranchIds);
	return unique;
}

function normalizeScope(value, branches) {
	if (value === SEARCH_SCOPE.ALL) return SEARCH_SCOPE.ALL;
	if (value === SEARCH_SCOPE.MULTI) return SEARCH_SCOPE.MULTI;
	if (value === SEARCH_SCOPE.SINGLE) return SEARCH_SCOPE.SINGLE;

	return branches.length > 0 ? SEARCH_SCOPE.MULTI : SEARCH_SCOPE.SINGLE;
}

function normalizeLimit(value) {
	const n = Number(value);
	if (!Number.isInteger(n)) return DEFAULT_SEARCH_LIMIT;
	if (!SEARCH_LIMITS.includes(n)) return DEFAULT_SEARCH_LIMIT;

	return n;
}

function normalizeDate(value) {
	if (typeof value !== "string") return null;

	const trimmed = value.trim();
	return trimmed ? trimmed : null;
}

function normalizeCreatedAt(value) {
	const n = Number(value);
	if (!Number.isFinite(n) || n <= 0) return Date.now();
	return Math.trunc(n);
}

function buildEntryState(entry) {
	return {
		q: entry.q,
		scope: entry.scope,
		branches: entry.branches,
		limit: entry.limit,
		from: entry.from,
		to: entry.to,
	};
}

function buildHistoryIdentity(entry) {
	const qs = serializeSearchUrlState(buildEntryState(entry));
	return `${entry.routeBranch}|${qs}`;
}

function normalizeMaxItems(value) {
	const n = Number(value);
	if (!Number.isInteger(n) || n < 1) return DEFAULT_SEARCH_HISTORY_MAX_ITEMS;
	return n;
}

function normalizeEntries(entries, { maxItems = Infinity } = {}) {
	const safeMax = Number.isFinite(maxItems)
		? Math.max(1, Math.trunc(maxItems))
		: Infinity;

	const out = [];
	const seen = new Set();

	for (const raw of Array.isArray(entries) ? entries : []) {
		const normalized = normalizeSearchHistoryEntry(raw);
		if (!normalized) continue;

		const id = buildHistoryIdentity(normalized);
		if (seen.has(id)) continue;

		seen.add(id);
		out.push(normalized);

		if (out.length >= safeMax) break;
	}

	return out;
}

export function buildSearchHistoryStorageKey(userId) {
	const normalizedUserId = normalizeUserId(userId);
	if (!normalizedUserId) return null;

	return `${SEARCH_HISTORY_STORAGE_PREFIX}.${normalizedUserId}`;
}

/**
 * Normalize one search history entry into canonical form.
 *
 * Cursor is intentionally excluded from the schema because it is not shareable.
 *
 * @param {any} raw
 * @returns {{
 *   routeBranch: string,
 *   q: string,
 *   scope: "single"|"multi"|"all",
 *   branches: string[],
 *   limit: number,
 *   from: string|null,
 *   to: string|null,
 *   createdAt: number
 * }|null}
 */
export function normalizeSearchHistoryEntry(raw) {
	if (!raw || typeof raw !== "object") return null;

	const routeBranch = normalizeRouteBranch(raw.routeBranch);
	const q = normalizeQuery(raw.q);
	if (!routeBranch || !q) return null;

	const allBranches = normalizeBranches(raw.branches);
	const scope = normalizeScope(raw.scope, allBranches);
	const branches = scope === SEARCH_SCOPE.MULTI ? allBranches : [];

	return {
		routeBranch,
		q,
		scope,
		branches,
		limit: normalizeLimit(raw.limit),
		from: normalizeDate(raw.from),
		to: normalizeDate(raw.to),
		createdAt: normalizeCreatedAt(raw.createdAt),
	};
}

export function loadSearchHistory(userId) {
	const storageKey = buildSearchHistoryStorageKey(userId);
	if (!storageKey) return [];
	if (typeof window === "undefined") return [];

	try {
		const raw = window.localStorage.getItem(storageKey);
		if (!raw) return [];

		const parsed = JSON.parse(raw);
		return normalizeEntries(parsed);
	} catch {
		return [];
	}
}

export function saveSearchHistory(userId, entries) {
	const normalized = normalizeEntries(entries);

	const storageKey = buildSearchHistoryStorageKey(userId);
	if (!storageKey) return normalized;
	if (typeof window === "undefined") return normalized;

	try {
		if (normalized.length === 0) {
			window.localStorage.removeItem(storageKey);
		} else {
			window.localStorage.setItem(storageKey, JSON.stringify(normalized));
		}
	} catch {
		// ignore storage quota and privacy mode errors
	}

	return normalized;
}

export function addSearchHistoryEntry(
	userId,
	entry,
	{ maxItems = DEFAULT_SEARCH_HISTORY_MAX_ITEMS } = {},
) {
	const normalizedEntry = normalizeSearchHistoryEntry(entry);
	if (!normalizedEntry) return loadSearchHistory(userId);

	const current = loadSearchHistory(userId);
	const currentId = buildHistoryIdentity(normalizedEntry);

	const deduped = current.filter((it) => buildHistoryIdentity(it) !== currentId);
	const capped = [normalizedEntry, ...deduped].slice(0, normalizeMaxItems(maxItems));

	return saveSearchHistory(userId, capped);
}

export function clearSearchHistory(userId) {
	const storageKey = buildSearchHistoryStorageKey(userId);
	if (!storageKey) return false;
	if (typeof window === "undefined") return false;

	try {
		window.localStorage.removeItem(storageKey);
		return true;
	} catch {
		return false;
	}
}

export function buildSearchHrefFromEntry(entry) {
	const normalized = normalizeSearchHistoryEntry(entry);
	if (!normalized) return null;

	const href = buildSearchHref({
		routeBranch: normalized.routeBranch,
		state: buildEntryState(normalized),
	});

	// Defensive safety gate: only allow internal paths.
	if (typeof href !== "string") return null;
	if (!href.startsWith("/")) return null;
	if (href.startsWith("//")) return null;

	return href;
}

