/**
 * What each event code means to the operator, in three fixed fields: what the
 * episode is (`title`), what it costs while it stays open (`impact`), and what
 * to do about it (`remediation`). The server sends a code and an error string;
 * every word of explanation the page shows comes from here.
 *
 * The record is exhaustive over `DiagnosticCode` by type, and a test walks
 * `DIAGNOSTIC_CODES` to prove it at runtime too. A sixteenth code added to the
 * enum fails `tsc` here before it can reach the page as a bare dotted string.
 *
 * `link` points at the configuration surface that governs the failure. The
 * path alone decides which tab of that surface the detail page opens, so the
 * copy never carries a tab of its own to drift from it.
 */

import { DIAGNOSTIC_CODES, type DiagnosticCode } from "@/lib/types";

/** The literal paths keep `link.to` assignable to a typed router `Link`. */
export type CopyLinkPath =
	"/configuration/system" | "/configuration/protection" | "/configuration/resolution" | "/clients";

export interface EventCopy {
	title: string;
	impact: string;
	remediation: string;
	link?: { to: CopyLinkPath; label: string };
}

/**
 * The copy for a code, with a floor under it. `tsc` proves the record covers
 * the union, but a server one release ahead can send a code this build has
 * never heard of; showing the raw code beats rendering "undefined".
 */
export function copyFor(code: DiagnosticCode): EventCopy {
	return (
		EVENT_COPY[code] ?? {
			title: code,
			impact: "This build has no description for this event code.",
			remediation: "The error detail below is the whole of what the server reported.",
		}
	);
}

const SETTINGS = { to: "/configuration/system", label: "System" } as const;
const BLOCKLISTS = { to: "/configuration/protection", label: "Blocklist sources" } as const;
const UPSTREAMS = { to: "/configuration/resolution", label: "Upstreams" } as const;
const CLIENTS = { to: "/clients", label: "Clients" } as const;

/**
 * The component filter's options, derived from the codes rather than listed
 * again: the server matches `component` against the part of `code` before the
 * dot, so any list written by hand here could drift from the enum.
 */
export const DIAGNOSTIC_COMPONENTS: readonly string[] = [
	...new Set(DIAGNOSTIC_CODES.map((code) => code.slice(0, code.indexOf(".")))),
];

/** `query_log` → "Query log". Display only; the filter sends the raw component. */
export function componentLabel(component: string): string {
	const spaced = component.replaceAll("_", " ");
	return spaced.charAt(0).toUpperCase() + spaced.slice(1);
}

export const EVENT_COPY: Record<DiagnosticCode, EventCopy> = {
	"disk.space": {
		title: "Disk space low",
		impact: "Below the critical threshold nxdns stops blocklist updates and query log flushes to protect the disk.",
		remediation: "Free space on the data volume, or lower the retention window so the query log holds fewer days.",
		link: SETTINGS,
	},
	"disk.probe": {
		title: "Disk usage probe failed",
		impact: "Free space is unknown, so the low-disk guard cannot act until a probe succeeds.",
		remediation: "Check that the data and log directories exist and that the service user can read them.",
		link: SETTINGS,
	},
	"blocklist.refresh": {
		title: "Blocklist source failed to update",
		impact: "The source keeps serving its last good snapshot, so blocking continues but the list ages.",
		remediation: "Check the source url and the machine's internet access, then update the lists again.",
		link: BLOCKLISTS,
	},
	"blocklist.snapshot": {
		title: "Filter snapshot failed to publish",
		impact: "The resolver keeps the snapshot it already holds; blocklist edits do not take effect until one publishes.",
		remediation: "Check free disk space and the data directory's permissions, then update the lists again.",
		link: BLOCKLISTS,
	},
	"blocklist.storage": {
		title: "Blocklist storage operation failed",
		impact: "Cached list files or their database rows are out of step; a later pass can redownload what is missing.",
		remediation: "Check free disk space and the data directory's permissions.",
		link: BLOCKLISTS,
	},
	"certificate.reload": {
		title: "TLS certificate reload failed",
		impact: "The endpoint keeps serving the certificate it already loaded, which expires on its own schedule.",
		remediation:
			"Check the certificate and key paths, and that renewal writes both files the service user can read.",
		link: SETTINGS,
	},
	"query_log.write": {
		title: "Query log write failed",
		impact: "Queries are resolved and answered as usual, but they are not being recorded.",
		remediation: "Check free disk space and the log database's permissions, then restart nxdns.",
		link: SETTINGS,
	},
	"query_log.maintenance": {
		title: "Query log maintenance failed",
		impact: "Old rows are not being trimmed, so the log database grows past its retention window.",
		remediation: "Check free disk space; the next maintenance pass retries on its own.",
		link: SETTINGS,
	},
	"query_log.recreated": {
		title: "Query log recreated",
		impact: "The old log database was moved aside — a release changed its schema, or the file could not be read — and the history it held is not in the new one.",
		remediation: "Keep or delete the aside file named below. Nothing else is required — logging is running.",
		link: SETTINGS,
	},
	/**
	 * Legacy. Nothing emits this code any more: milestone 30 deleted the
	 * upstream-minute history subsystem. Stored rows outlive it, and the list
	 * endpoint passes their codes through verbatim, so the copy stays — a
	 * retained episode must still read as prose rather than as a dotted string.
	 */
	"upstream_history.write": {
		title: "Upstream history write failed",
		impact: "A recorded failure of a subsystem this version no longer runs. Resolution was unaffected; the per-upstream aggregates it fed are gone.",
		remediation: "Nothing to do. Purge the entry once you have read it.",
		link: UPSTREAMS,
	},
	"upstream.exchange": {
		title: "Upstream failing",
		impact: "Queries fall through to the remaining upstreams; answers are slower while this one backs off.",
		remediation: "Check the upstream's reachability and its TLS name. Remove it if it stays down.",
		link: UPSTREAMS,
	},
	"client_names.storage": {
		title: "Client name storage failed",
		impact: "Learned reverse-DNS names are not persisted, so clients can show as bare addresses after a restart.",
		remediation: "Check free disk space and the configuration database's permissions.",
		link: CLIENTS,
	},
	"clients.storage": {
		title: "Client record storage failed",
		impact: "New clients may not appear in the list and stale ones may not be pruned.",
		remediation: "Check free disk space and the configuration database's permissions.",
		link: CLIENTS,
	},
	"listener.start": {
		title: "Encrypted DNS listener failed to start",
		impact: "That endpoint is not accepting queries. Plain DNS on port 53 is unaffected.",
		remediation:
			"Check the bind address, the port, and the certificate paths, then restart nxdns. The episode closes on a clean start.",
		link: SETTINGS,
	},
	"configuration.load": {
		title: "Configuration problem at startup",
		impact: "The setting named below was rejected or replaced by its default for this run.",
		remediation: "Correct the setting and restart nxdns. The episode closes on a clean start.",
		link: SETTINGS,
	},
};
