/**
 * The configuration pages' URL state, validated as pure functions so each
 * route's `validateSearch` stays a one-liner and every rejection is testable
 * without a router.
 *
 * A tab that is not one of the page's tabs falls back to that page's default:
 * a hand-typed `?tab=upstreams` on Protection opens Protection's first tab
 * rather than an empty panel.
 */

export const PROTECTION_TABS = ["groups", "sources"] as const;
export type ProtectionTab = (typeof PROTECTION_TABS)[number];

export const RESOLUTION_TABS = ["upstreams", "records", "zones"] as const;
export type ResolutionTab = (typeof RESOLUTION_TABS)[number];

/**
 * Both fields are optional so a link to the page need not spell out a tab it
 * does not care about — the same shape Overview's `period` uses. Validation
 * still resolves an unknown tab to the page default, so a *rendered* page
 * always has one.
 */
export interface ProtectionSearch {
	tab?: ProtectionTab;
	/**
	 * The selected group. Absent until the page resolves one, which it then
	 * writes back so a view of the page is a link.
	 */
	group?: number;
}

export interface ResolutionSearch {
	tab?: ResolutionTab;
}

function validateTab<T extends string>(options: readonly T[], value: unknown, fallback: T): T {
	return options.includes(value as T) ? (value as T) : fallback;
}

/**
 * A group row id. Ids are positive integers, so anything else — a fraction, a
 * string, a zero — is not an id that could ever exist and becomes `undefined`.
 * An id that is well-formed but unknown is a different case: the page has to
 * load the groups before it can tell, so it falls back there, not here.
 */
export function validateGroupId(value: unknown): number | undefined {
	return Number.isSafeInteger(value) && (value as number) > 0 ? (value as number) : undefined;
}

export function validateProtectionSearch(search: Record<string, unknown>): ProtectionSearch {
	return {
		tab: validateTab(PROTECTION_TABS, search["tab"], "groups"),
		group: validateGroupId(search["group"]),
	};
}

export function validateResolutionSearch(search: Record<string, unknown>): ResolutionSearch {
	return { tab: validateTab(RESOLUTION_TABS, search["tab"], "upstreams") };
}
