import { z } from "zod";
import { isReservedName } from "./reserved-names";
/** Account name shape only; `accountName` also refuses reserved names. */
export const accountNameFormat = z
.string()
.trim()
.min(1)
.max(39)
.regex(/^[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?$/, "Use letters, numbers and single hyphens");
export const accountName = accountNameFormat.refine((name) => !isReservedName(name), "That name is reserved");
export const repoName = z
.string()
.trim()
.min(1)
.max(100)
.regex(/^[A-Za-z0-9._-]+$/, "Use letters, numbers, '.', '-' and '_'")
.refine((name) => !name.endsWith(".git") && name !== "." && name !== "..", "Invalid repository name");
export const repoRef = z.object({ owner: z.string().min(1), repo: z.string().min(1) });
export const password = z.string().min(8, "Use at least 8 characters").max(200);
const DISPLAY_NAME_FORBIDDEN = /[<>\u0000-\u001f\u007f]/g;
/**
* Display names end up in git author/committer idents and `Co-authored-by` trailers, where `<`, `>` and
* newlines would forge a mailbox or start a new header line. Strip them and collapse whitespace; an empty
* result becomes `null` so callers fall back to the login.
*/
export function cleanDisplayName(value: string | null | undefined): string | null {
if (value === null || value === undefined) return null;
const cleaned = value.replace(DISPLAY_NAME_FORBIDDEN, "").replace(/\s+/g, " ").trim();
return cleaned.length > 0 ? cleaned : null;
}
export const displayName = z.string().max(100).nullable().transform(cleanDisplayName);