import type { AppDomain, HostKind, PathKind } from './api';

/** Guess a path kind from the literal user input. The dashboard pre-fills
 *  the kind selector but the user can override (the backend trusts the
 *  selection — selecting `exact` for `/greet/:name` matches it literally). */
export function guessPathKind(raw: string): PathKind {
	if (raw.endsWith('/*')) return 'prefix';
	if (/(^|\/):[a-zA-Z_]/.test(raw)) return 'param';
	return 'exact';
}

/** Guess a host kind. Empty → any, leading "*." → wildcard, else strict. */
export function guessHostKind(raw: string): HostKind {
	if (!raw.trim()) return 'any';
	if (raw.startsWith('*.')) return 'wildcard';
	return 'strict';
}

/** Warn-not-block: does the user's selection match what we'd guess
 *  from the input? Used by the routing form for the soft mismatch
 *  hint. Returns null when no warning is needed. */
export function pathKindMismatchWarning(raw: string, kind: PathKind): string | null {
	const guessed = guessPathKind(raw);
	if (guessed === kind) return null;
	const hint =
		guessed === 'param'
			? 'this looks like a param pattern (contains `:name`)'
			: guessed === 'prefix'
				? 'this looks like a prefix pattern (ends with `/*`)'
				: 'this looks like a literal path';
	return `Selected kind is "${kind}", but ${hint}. Routing will use the selected kind.`;
}

/** Parse the user's free-text host input into the (kind, stored host)
 *  pair the API expects. Mirrors the dashboard's pre-existing storage
 *  convention: wildcard route hosts are stored as the bare suffix
 *  (`*.foo.com` → `foo.com`); display-time formatting re-adds the `*.`. */
export interface ParsedHostInput {
	kind: HostKind;
	/** What gets sent in the API payload as `host`. */
	host: string;
	/** Canonical display form, for chips/labels. */
	display: string;
}

export function parseHostInput(raw: string): ParsedHostInput {
	const trimmed = raw.trim();
	if (trimmed === '' || trimmed === '*') {
		return { kind: 'any', host: '', display: '*' };
	}
	if (trimmed.startsWith('*.')) {
		const suffix = trimmed.slice(2);
		return { kind: 'wildcard', host: suffix, display: `*.${suffix}` };
	}
	return { kind: 'strict', host: trimmed, display: trimmed };
}

/** Frontend mirror of `validate_route_host_against_app` in
 *  crates/manager-core/src/route_admin.rs. Lets us surface a live
 *  warning instead of waiting for a 422. The server runs the same
 *  check on submit — this is purely an early signal. */
export type HostClaimCheck =
	| { ok: true; matched: string }
	| { ok: false; reason: string };

export function checkHostAgainstClaims(
	parsed: ParsedHostInput,
	claims: AppDomain[]
): HostClaimCheck {
	if (parsed.kind === 'any') return { ok: true, matched: '*' };
	if (claims.length === 0) {
		return {
			ok: false,
			reason: 'this app has no domain claims yet — add one in the app’s Domains tab'
		};
	}
	const hostLower = parsed.host.toLowerCase();
	for (const claim of claims) {
		const claimLower = claim.pattern.toLowerCase();
		const claimSuffix = claimLower.split('.').slice(1).join('.');
		if (parsed.kind === 'strict') {
			if (claim.shape === 'exact' && hostLower === claimLower) {
				return { ok: true, matched: claim.pattern };
			}
			if (
				(claim.shape === 'wildcard' || claim.shape === 'parameterized') &&
				claimSuffix &&
				hostLower.endsWith(`.${claimSuffix}`)
			) {
				return { ok: true, matched: claim.pattern };
			}
		} else {
			// wildcard route
			if (
				(claim.shape === 'wildcard' || claim.shape === 'parameterized') &&
				claimSuffix === hostLower
			) {
				return { ok: true, matched: claim.pattern };
			}
		}
	}
	return {
		ok: false,
		reason: `${parsed.display} is not covered by any of this app’s claims`
	};
}

/** Suggestion strings for the host input's <datalist>. We always
 *  include the wildcard `*` (any host) as the first option, then the
 *  app's claims rendered in the form the route input expects. */
export function hostSuggestions(claims: AppDomain[]): string[] {
	const items: string[] = ['*'];
	for (const claim of claims) {
		if (claim.shape === 'exact') {
			items.push(claim.pattern);
		} else {
			// Both wildcard and parameterized claims are usable as a
			// wildcard route. Render as `*.suffix` — we can't preserve
			// the {param} binding name through the current route form.
			const suffix = claim.pattern.split('.').slice(1).join('.');
			if (suffix) items.push(`*.${suffix}`);
		}
	}
	// Dedupe while preserving order.
	return [...new Set(items)];
}
