import { POLICY_ACTIONS, POLICY_REASONS, ROUTE_KINDS } from "@/lib/types";
import type { PolicyAction, PolicyReason, RouteKind } from "@/lib/types";

/**
 * Display names for the three stored enums. `Record` over the union, so a value
 * added to `src/storage/provenance.zig` and mirrored into `lib/types.ts` fails
 * `tsc` here instead of reaching a cell as a raw tag name.
 */
const POLICY_ACTION_LABELS: Record<PolicyAction, string> = {
	not_evaluated: "Not evaluated",
	allow: "Allowed",
	block: "Blocked",
};

const POLICY_REASON_LABELS: Record<PolicyReason, string> = {
	rule_allow_exact: "Allow rule (exact)",
	rule_block_exact: "Block rule (exact)",
	rule_allow_wildcard: "Allow rule (wildcard)",
	rule_block_wildcard: "Block rule (wildcard)",
	rule_allow_regex: "Allow rule (regex)",
	rule_block_regex: "Block rule (regex)",
	blocklist_exception: "Blocklist exception",
	blocklist_domain: "Blocklist (domain)",
	blocklist_wildcard: "Blocklist (wildcard)",
	local_record: "Local record",
	forward_zone: "Forward zone",
	non_in_class: "Not class IN",
	paused: "Filtering paused",
	snapshot_unavailable: "No filter snapshot",
	no_match: "No match",
	protocol_error: "Protocol refusal",
};

const ROUTE_KIND_LABELS: Record<RouteKind, string> = {
	blocked: "Blocked locally",
	local: "Local record",
	forward_zone: "Forward zone",
	upstream: "Upstream resolver",
	cache: "Cache",
	rejected: "Rejected",
};

export function policyActionLabel(action: PolicyAction): string {
	return POLICY_ACTION_LABELS[action];
}

export function policyReasonLabel(reason: PolicyReason): string {
	return POLICY_REASON_LABELS[reason];
}

export function routeKindLabel(kind: RouteKind): string {
	return ROUTE_KIND_LABELS[kind];
}

/** The enum value sets, for tests that prove the maps exhaustive at runtime too. */
export const ENUM_VALUES = {
	policyAction: POLICY_ACTIONS,
	policyReason: POLICY_REASONS,
	routeKind: ROUTE_KINDS,
} as const;

const RCODE_NAMES: Record<number, string> = {
	0: "NOERROR",
	1: "FORMERR",
	2: "SERVFAIL",
	3: "NXDOMAIN",
	4: "NOTIMP",
	5: "REFUSED",
	6: "YXDOMAIN",
	7: "YXRRSET",
	8: "NXRRSET",
	9: "NOTAUTH",
	10: "NOTZONE",
	16: "BADVERS",
};

/**
 * The bare mnemonic, for a table cell with no room for the number. An
 * unassigned code has no mnemonic to shorten, so it keeps the same `RCODE <n>`
 * shape the long form falls back to.
 */
export function rcodeShortName(rcode: number): string {
	return RCODE_NAMES[rcode] ?? `RCODE ${rcode}`;
}

/** The twelve-bit extended code as `NXDOMAIN (3)`; an unassigned code keeps its number. */
export function rcodeName(rcode: number): string {
	const name = RCODE_NAMES[rcode];
	return name === undefined ? `RCODE ${rcode}` : `${name} (${rcode})`;
}

const QCLASS_NAMES: Record<number, string> = {
	1: "IN",
	3: "CH",
	4: "HS",
	254: "NONE",
	255: "ANY",
};

export function qclassName(qclass: number): string {
	const name = QCLASS_NAMES[qclass];
	return name === undefined ? `CLASS ${qclass}` : `${name} (${qclass})`;
}
