import { dirname } from "node:path";
import { rootPath } from "./config";
import { createYamlFieldReader, readYamlRecord } from "./platform-infra-ops-library";
import { validateGiteaWebhookTiming } from "./platform-infra-gitea-caddy";
import type { GiteaWebhookIngressRetry } from "./platform-infra-gitea-caddy";

export const GITEA_CONFIG_LABEL = "config/platform-infra/gitea.yaml";
const configFile = rootPath("config", "platform-infra", "gitea.yaml");
const configLabel = GITEA_CONFIG_LABEL;
const y = createYamlFieldReader(configLabel);

export interface GiteaTarget {
  id: string;
  route: string;
  namespace: string;
  role: string;
  enabled: boolean;
  createNamespace: boolean;
  storageClassName: string;
  publicExposureEnabled: boolean;
  webhookSync: GiteaTargetWebhookSync | null;
}

export interface GiteaTargetWebhookSync {
  publicPath: string;
}

export interface GiteaPublicExposure {
  enabled: boolean;
  publicBaseUrl: string;
  upstream: string;
  healthPath: string;
  dns: {
    hostname: string;
    expectedA: string;
    resolvers: string[];
  };
  routes: Array<{
    path: string;
    upstream: string;
    responseHeaderTimeout?: string;
  }>;
  legacyCleanup: {
    enabled: boolean;
    execute: boolean;
    jobName: string;
    serviceAccountName: string;
    image: string;
    activeDeadlineSeconds: number;
    ttlSecondsAfterFinished: number;
    deploymentName: string;
    secretName: string;
  };
}

export interface GiteaConfig {
  version: number;
  kind: "platform-infra-gitea";
  metadata: {
    id: string;
    owner: string;
    spec: string;
    relatedIssues: number[];
  };
  defaults: {
    targetId: string;
  };
  migration: {
    role: string;
    replaces: string;
    parentConfigRef: string;
    envReusePolicy: string;
    buildPlane: string;
    runtimePlane: string;
  };
  sourceAuthority: {
    enabled: boolean;
    stage: string;
    statusAuthority: string;
    firstCiConsumer: string;
    credentials: {
      sourceRoot: string;
      admin: GiteaAdminCredential;
      github: GiteaGithubCredential;
    };
    githubProxy: {
      enabled: boolean;
      url: string;
      noProxy: string[];
    };
    webhookSync: GiteaWebhookSync;
    responsibilities: GiteaSourceResponsibility[];
    repositories: GiteaMirrorRepository[];
  };
  targets: GiteaTarget[];
  app: {
    name: string;
    statefulSetName: string;
    serviceName: string;
    replicas: number;
    image: {
      repository: string;
      tag: string;
      pullPolicy: "Always" | "IfNotPresent" | "Never";
    };
    service: {
      type: "ClusterIP";
      httpPort: number;
      sshPort: number;
    };
    server: {
      domain: string;
      rootUrl: string;
      sshDomain: string;
      protocol: "http" | "https";
      startSshServer: boolean;
    };
    publicExposure: GiteaPublicExposure;
    database: {
      type: "sqlite3";
      path: string;
    };
    actions: {
      enabled: boolean;
    };
    webhook: {
      allowedHostList: string;
    };
    registration: {
      disabled: boolean;
    };
    storage: {
      data: { size: string; mountPath: string };
      config: { size: string; mountPath: string };
    };
    securityContext: {
      runAsUser: number;
      runAsGroup: number;
      fsGroup: number;
    };
    resources: {
      requests: { cpu: string; memory: string };
      limits: { cpu: string; memory: string };
    };
    probes: {
      healthPath: string;
      initialDelaySeconds: number;
      periodSeconds: number;
      timeoutSeconds: number;
      failureThreshold: number;
    };
  };
  validation: {
    waitTimeoutSeconds: number;
    healthPath: string;
  };
}

export interface GiteaAdminCredential {
  sourceRef: string;
  format: "line-pair";
  usernameLine: number;
  passwordLine: number;
  requiredFor: string[];
}

export interface GiteaGithubCredential {
  transport: "https-token";
  sourceRef: string;
  sourceKey: string;
  format: "env" | "raw-token";
  requiredFor: string[];
  gitFetchCredential: {
    apiVersion: "unidesk.ai/v1";
    kind: "GitFetchCredential";
    authMode: "github-https-token";
    host: "github.com";
    secretRef: {
      namespace: string;
      name: string;
      key: string;
    };
  };
}

export interface GiteaGithubPermissions {
  contents: "read";
  metadata: "read";
  webhooks: "read-write";
}

export function githubTokenEnvNameForSecretKey(secretKey: string): string {
  return `UNIDESK_GITEA_GITHUB_TOKEN_${secretKey.replace(/[^A-Za-z0-9_]/gu, "_").toUpperCase()}`;
}

export interface GiteaWebhookSync {
  enabled: boolean;
  direction: "github-to-gitea";
  responseBudgetMs: number;
  publicPath: string;
  events: string[];
  ingressRetry: GiteaWebhookIngressRetry;
  secret: {
    sourceRef: string;
    sourceKey: string;
    createIfMissing: boolean;
  };
  hookReconcile: {
    enabled: boolean;
    ownerTargetId: string;
    intervalMs: number;
    requestTimeoutMs: number;
    listMaxPages: number;
    failedDeliveryRecovery: {
      enabled: boolean;
      targetId: string;
      statePath: string;
      lookbackSeconds: number;
      historyPerHook: number;
      maxRedeliveriesPerCycle: number;
      cooldownMs: number;
      maxAttemptsPerDelivery: number;
      retryStatusCodes: number[];
      stateRetentionSeconds: number;
    };
  };
  bridge: {
    deploymentName: string;
    serviceName: string;
    secretName: string;
    configMapName: string;
    image: string;
    initCommand: string[];
    replicas: number;
    httpPort: number;
    serviceAccountName: string;
    shutdownGraceMs: number;
    candidateGate: {
      enabled: boolean;
      configMapName: string;
      jobName: string;
      activeDeadlineSeconds: number;
      ttlSecondsAfterFinished: number;
      healthTimeoutMs: number;
      pollIntervalMs: number;
    };
    inbox: {
      claimName: string;
      path: string;
      storageSize: string;
      maxBytes: number;
      committedRetentionSeconds: number;
      cleanupIntervalMs: number;
    };
    retry: {
      maxAttempts: number;
      initialDelayMs: number;
      maxDelayMs: number;
      terminalRetryDelayMs: number;
      attemptTimeoutMs: number;
      scanIntervalMs: number;
    };
  };
  gitOpsDelivery: GiteaWebhookGitOpsDelivery;
}

export interface GiteaWebhookGitOpsDelivery {
  enabled: boolean;
  targetId: string;
  readUrl: string;
  writeUrl: string;
  branch: string;
  sourceSnapshotPrefix: string;
  desiredManifestPath: string;
  bootstrapApplicationPath: string;
  releaseStatePath: string;
  application: {
    name: string;
    namespace: string;
    project: string;
    repoUrl: string;
    targetRevision: string;
    path: string;
    destinationNamespace: string;
    automated: boolean;
  };
  author: {
    name: string;
    email: string;
  };
  cas: {
    maxAttempts: number;
  };
}

export interface GiteaSourceResponsibility {
  name: string;
  current: string;
  target: string;
  disposition: "replaced-by-gitea" | "retained-for-gitops-flush" | "migration-readonly";
}

export interface GiteaMirrorRepository {
  key: string;
  targetId: string;
  credentialOverride: {
    github: GiteaGithubCredential & { permissions: GiteaGithubPermissions };
  } | null;
  upstream: {
    repository: string;
    cloneUrl: string;
    branch: string;
    visibility: "public" | "private";
  };
    gitea: {
      owner: string;
      name: string;
      mirrorMode: "controlled-push";
      publicRead: boolean;
      readUrl: string;
    };
  gitops: {
    branch: string;
    flushDisposition: string;
  };
  snapshot: {
    prefix: string;
  };
  legacyGitMirror: {
    readUrl: string;
    configRef: string | null;
    disposition: "replaced-by-gitea" | "retained-for-gitops-flush" | "migration-readonly";
  } | null;
}

export function readGiteaConfig(): GiteaConfig {
  const root = readYamlRecord<Record<string, unknown>>(configFile, "platform-infra-gitea");
  const version = y.integerField(root, "version", "");
  if (version !== 1) throw new Error(`${configLabel}.version must be 1`);
  const metadata = y.objectField(root, "metadata", "");
  const defaults = y.objectField(root, "defaults", "");
  const migration = y.objectField(root, "migration", "");
  const sourceAuthority = y.objectField(root, "sourceAuthority", "");
  const sourceCredentials = y.objectField(sourceAuthority, "credentials", "sourceAuthority");
  const githubProxy = y.objectField(sourceAuthority, "githubProxy", "sourceAuthority");
  const webhookSync = y.objectField(sourceAuthority, "webhookSync", "sourceAuthority");
  const app = y.objectField(root, "app", "");
  const image = y.objectField(app, "image", "app");
  const service = y.objectField(app, "service", "app");
  const server = y.objectField(app, "server", "app");
  const database = y.objectField(app, "database", "app");
  const actions = y.objectField(app, "actions", "app");
  const webhook = y.objectField(app, "webhook", "app");
  const registration = y.objectField(app, "registration", "app");
  const storage = y.objectField(app, "storage", "app");
  const dataStorage = y.objectField(storage, "data", "app.storage");
  const configStorage = y.objectField(storage, "config", "app.storage");
  const publicExposure = y.objectField(app, "publicExposure", "app");
  const securityContext = y.objectField(app, "securityContext", "app");
  const resources = y.objectField(app, "resources", "app");
  const requests = y.objectField(resources, "requests", "app.resources");
  const limits = y.objectField(resources, "limits", "app.resources");
  const probes = y.objectField(app, "probes", "app");
  const validation = y.objectField(root, "validation", "");
  const parsed: GiteaConfig = {
    version,
    kind: "platform-infra-gitea",
    metadata: {
      id: y.stringField(metadata, "id", "metadata"),
      owner: y.stringField(metadata, "owner", "metadata"),
      spec: y.stringField(metadata, "spec", "metadata"),
      relatedIssues: y.numberArrayField(metadata, "relatedIssues", "metadata"),
    },
    defaults: {
      targetId: y.stringField(defaults, "targetId", "defaults"),
    },
    migration: {
      role: y.stringField(migration, "role", "migration"),
      replaces: y.stringField(migration, "replaces", "migration"),
      parentConfigRef: y.stringField(migration, "parentConfigRef", "migration"),
      envReusePolicy: y.stringField(migration, "envReusePolicy", "migration"),
      buildPlane: y.stringField(migration, "buildPlane", "migration"),
      runtimePlane: y.stringField(migration, "runtimePlane", "migration"),
    },
    sourceAuthority: {
      enabled: y.booleanField(sourceAuthority, "enabled", "sourceAuthority"),
      stage: y.stringField(sourceAuthority, "stage", "sourceAuthority"),
      statusAuthority: y.stringField(sourceAuthority, "statusAuthority", "sourceAuthority"),
      firstCiConsumer: y.stringField(sourceAuthority, "firstCiConsumer", "sourceAuthority"),
      credentials: {
        sourceRoot: y.absolutePathField(sourceCredentials, "sourceRoot", "sourceAuthority.credentials"),
        admin: parseAdminCredential(y.objectField(sourceCredentials, "admin", "sourceAuthority.credentials"), "sourceAuthority.credentials.admin"),
        github: parseGithubCredential(y.objectField(sourceCredentials, "github", "sourceAuthority.credentials"), "sourceAuthority.credentials.github"),
      },
      githubProxy: {
        enabled: y.booleanField(githubProxy, "enabled", "sourceAuthority.githubProxy"),
        url: urlField(githubProxy, "url", "sourceAuthority.githubProxy"),
        noProxy: y.stringArrayField(githubProxy, "noProxy", "sourceAuthority.githubProxy"),
      },
      webhookSync: parseWebhookSync(webhookSync, "sourceAuthority.webhookSync"),
      responsibilities: y.arrayOfRecords(sourceAuthority.responsibilities, "sourceAuthority.responsibilities").map(parseResponsibility),
      repositories: y.arrayOfRecords(sourceAuthority.repositories, "sourceAuthority.repositories").map(parseMirrorRepository),
    },
    targets: y.arrayOfRecords(root.targets, "targets").map(parseTarget),
    app: {
      name: y.kubernetesNameField(app, "name", "app"),
      statefulSetName: y.kubernetesNameField(app, "statefulSetName", "app"),
      serviceName: y.kubernetesNameField(app, "serviceName", "app"),
      replicas: positiveInteger(app, "replicas", "app"),
      image: {
        repository: y.stringField(image, "repository", "app.image"),
        tag: y.stringField(image, "tag", "app.image"),
        pullPolicy: y.enumField(image, "pullPolicy", "app.image", ["Always", "IfNotPresent", "Never"] as const),
      },
      service: {
        type: y.enumField(service, "type", "app.service", ["ClusterIP"] as const),
        httpPort: y.portField(service, "httpPort", "app.service"),
        sshPort: y.portField(service, "sshPort", "app.service"),
      },
      server: {
        domain: y.hostField(server, "domain", "app.server"),
        rootUrl: urlField(server, "rootUrl", "app.server"),
        sshDomain: y.hostField(server, "sshDomain", "app.server"),
        protocol: y.enumField(server, "protocol", "app.server", ["http", "https"] as const),
        startSshServer: y.booleanField(server, "startSshServer", "app.server"),
      },
      publicExposure: parsePublicExposure(publicExposure, "app.publicExposure"),
      database: {
        type: y.enumField(database, "type", "app.database", ["sqlite3"] as const),
        path: y.absolutePathField(database, "path", "app.database"),
      },
      actions: {
        enabled: y.booleanField(actions, "enabled", "app.actions"),
      },
      webhook: {
        allowedHostList: y.stringField(webhook, "allowedHostList", "app.webhook"),
      },
      registration: {
        disabled: y.booleanField(registration, "disabled", "app.registration"),
      },
      storage: {
        data: { size: quantity(dataStorage, "size", "app.storage.data"), mountPath: y.absolutePathField(dataStorage, "mountPath", "app.storage.data") },
        config: { size: quantity(configStorage, "size", "app.storage.config"), mountPath: y.absolutePathField(configStorage, "mountPath", "app.storage.config") },
      },
      securityContext: {
        runAsUser: positiveInteger(securityContext, "runAsUser", "app.securityContext"),
        runAsGroup: positiveInteger(securityContext, "runAsGroup", "app.securityContext"),
        fsGroup: positiveInteger(securityContext, "fsGroup", "app.securityContext"),
      },
      resources: {
        requests: { cpu: y.stringField(requests, "cpu", "app.resources.requests"), memory: quantity(requests, "memory", "app.resources.requests") },
        limits: { cpu: y.stringField(limits, "cpu", "app.resources.limits"), memory: quantity(limits, "memory", "app.resources.limits") },
      },
      probes: {
        healthPath: y.apiPathField(probes, "healthPath", "app.probes"),
        initialDelaySeconds: positiveInteger(probes, "initialDelaySeconds", "app.probes"),
        periodSeconds: positiveInteger(probes, "periodSeconds", "app.probes"),
        timeoutSeconds: positiveInteger(probes, "timeoutSeconds", "app.probes"),
        failureThreshold: positiveInteger(probes, "failureThreshold", "app.probes"),
      },
    },
    validation: {
      waitTimeoutSeconds: boundedTimeout(validation, "waitTimeoutSeconds", "validation"),
      healthPath: y.apiPathField(validation, "healthPath", "validation"),
    },
  };
  validateConfig(parsed);
  return parsed;
}

function parseAdminCredential(record: Record<string, unknown>, path: string): GiteaAdminCredential {
  return {
    sourceRef: secretSourceRefField(record, "sourceRef", path),
    format: literalField(record, "format", path, ["line-pair"]),
    usernameLine: positiveInteger(record, "usernameLine", path),
    passwordLine: positiveInteger(record, "passwordLine", path),
    requiredFor: y.stringArrayField(record, "requiredFor", path),
  };
}

function parseGithubCredential(record: Record<string, unknown>, path: string): GiteaGithubCredential {
  const gitFetchCredential = y.objectField(record, "gitFetchCredential", path);
  const secretRef = y.objectField(gitFetchCredential, "secretRef", `${path}.gitFetchCredential`);
  return {
    transport: y.enumField(record, "transport", path, ["https-token"] as const),
    sourceRef: secretSourceRefField(record, "sourceRef", path),
    sourceKey: y.envKeyField(record, "sourceKey", path),
    format: optionalLiteralField(record, "format", path, ["env", "raw-token"] as const) ?? "env",
    requiredFor: y.stringArrayField(record, "requiredFor", path),
    gitFetchCredential: {
      apiVersion: y.enumField(gitFetchCredential, "apiVersion", `${path}.gitFetchCredential`, ["unidesk.ai/v1"] as const),
      kind: y.enumField(gitFetchCredential, "kind", `${path}.gitFetchCredential`, ["GitFetchCredential"] as const),
      authMode: y.enumField(gitFetchCredential, "authMode", `${path}.gitFetchCredential`, ["github-https-token"] as const),
      host: y.enumField(gitFetchCredential, "host", `${path}.gitFetchCredential`, ["github.com"] as const),
      secretRef: {
        namespace: y.kubernetesNameField(secretRef, "namespace", `${path}.gitFetchCredential.secretRef`),
        name: y.kubernetesNameField(secretRef, "name", `${path}.gitFetchCredential.secretRef`),
        key: y.stringField(secretRef, "key", `${path}.gitFetchCredential.secretRef`),
      },
    },
  };
}

function parseWebhookSync(record: Record<string, unknown>, path: string): GiteaWebhookSync {
  const ingressRetry = y.objectField(record, "ingressRetry", path);
  const secret = y.objectField(record, "secret", path);
  const hookReconcile = y.objectField(record, "hookReconcile", path);
  const failedDeliveryRecovery = y.objectField(hookReconcile, "failedDeliveryRecovery", `${path}.hookReconcile`);
  const bridge = y.objectField(record, "bridge", path);
  const candidateGate = y.objectField(bridge, "candidateGate", `${path}.bridge`);
  const inbox = y.objectField(bridge, "inbox", `${path}.bridge`);
  const gitOpsDelivery = y.objectField(record, "gitOpsDelivery", path);
  return {
    enabled: y.booleanField(record, "enabled", path),
    direction: y.enumField(record, "direction", path, ["github-to-gitea"] as const),
    responseBudgetMs: positiveInteger(record, "responseBudgetMs", path),
    publicPath: y.apiPathField(record, "publicPath", path),
    events: y.stringArrayField(record, "events", path),
    ingressRetry: {
      enabled: y.booleanField(ingressRetry, "enabled", `${path}.ingressRetry`),
      maxBodyBytes: positiveInteger(ingressRetry, "maxBodyBytes", `${path}.ingressRetry`),
      maxRetries: positiveInteger(ingressRetry, "maxRetries", `${path}.ingressRetry`),
      tryIntervalMs: positiveInteger(ingressRetry, "tryIntervalMs", `${path}.ingressRetry`),
      dialTimeoutMs: positiveInteger(ingressRetry, "dialTimeoutMs", `${path}.ingressRetry`),
      writeTimeoutMs: positiveInteger(ingressRetry, "writeTimeoutMs", `${path}.ingressRetry`),
      retryStatusCodes: y.numberArrayField(ingressRetry, "retryStatusCodes", `${path}.ingressRetry`),
    },
    secret: {
      sourceRef: secretSourceRefField(secret, "sourceRef", `${path}.secret`),
      sourceKey: y.envKeyField(secret, "sourceKey", `${path}.secret`),
      createIfMissing: y.booleanField(secret, "createIfMissing", `${path}.secret`),
    },
    hookReconcile: {
      enabled: y.booleanField(hookReconcile, "enabled", `${path}.hookReconcile`),
      ownerTargetId: y.stringField(hookReconcile, "ownerTargetId", `${path}.hookReconcile`),
      intervalMs: positiveInteger(hookReconcile, "intervalMs", `${path}.hookReconcile`),
      requestTimeoutMs: positiveInteger(hookReconcile, "requestTimeoutMs", `${path}.hookReconcile`),
      listMaxPages: positiveInteger(hookReconcile, "listMaxPages", `${path}.hookReconcile`),
      failedDeliveryRecovery: {
        enabled: y.booleanField(failedDeliveryRecovery, "enabled", `${path}.hookReconcile.failedDeliveryRecovery`),
        targetId: y.stringField(failedDeliveryRecovery, "targetId", `${path}.hookReconcile.failedDeliveryRecovery`),
        statePath: y.absolutePathField(failedDeliveryRecovery, "statePath", `${path}.hookReconcile.failedDeliveryRecovery`),
        lookbackSeconds: positiveInteger(failedDeliveryRecovery, "lookbackSeconds", `${path}.hookReconcile.failedDeliveryRecovery`),
        historyPerHook: positiveInteger(failedDeliveryRecovery, "historyPerHook", `${path}.hookReconcile.failedDeliveryRecovery`),
        maxRedeliveriesPerCycle: positiveInteger(failedDeliveryRecovery, "maxRedeliveriesPerCycle", `${path}.hookReconcile.failedDeliveryRecovery`),
        cooldownMs: positiveInteger(failedDeliveryRecovery, "cooldownMs", `${path}.hookReconcile.failedDeliveryRecovery`),
        maxAttemptsPerDelivery: positiveInteger(failedDeliveryRecovery, "maxAttemptsPerDelivery", `${path}.hookReconcile.failedDeliveryRecovery`),
        retryStatusCodes: y.numberArrayField(failedDeliveryRecovery, "retryStatusCodes", `${path}.hookReconcile.failedDeliveryRecovery`),
        stateRetentionSeconds: positiveInteger(failedDeliveryRecovery, "stateRetentionSeconds", `${path}.hookReconcile.failedDeliveryRecovery`),
      },
    },
    bridge: {
      deploymentName: y.kubernetesNameField(bridge, "deploymentName", `${path}.bridge`),
      serviceName: y.kubernetesNameField(bridge, "serviceName", `${path}.bridge`),
      secretName: y.kubernetesNameField(bridge, "secretName", `${path}.bridge`),
      configMapName: y.kubernetesNameField(bridge, "configMapName", `${path}.bridge`),
      image: y.stringField(bridge, "image", `${path}.bridge`),
      initCommand: y.stringArrayField(bridge, "initCommand", `${path}.bridge`),
      replicas: positiveInteger(bridge, "replicas", `${path}.bridge`),
      httpPort: y.portField(bridge, "httpPort", `${path}.bridge`),
      serviceAccountName: y.kubernetesNameField(bridge, "serviceAccountName", `${path}.bridge`),
      shutdownGraceMs: positiveInteger(bridge, "shutdownGraceMs", `${path}.bridge`),
      candidateGate: {
        enabled: y.booleanField(candidateGate, "enabled", `${path}.bridge.candidateGate`),
        configMapName: y.kubernetesNameField(candidateGate, "configMapName", `${path}.bridge.candidateGate`),
        jobName: y.kubernetesNameField(candidateGate, "jobName", `${path}.bridge.candidateGate`),
        activeDeadlineSeconds: positiveInteger(candidateGate, "activeDeadlineSeconds", `${path}.bridge.candidateGate`),
        ttlSecondsAfterFinished: positiveInteger(candidateGate, "ttlSecondsAfterFinished", `${path}.bridge.candidateGate`),
        healthTimeoutMs: positiveInteger(candidateGate, "healthTimeoutMs", `${path}.bridge.candidateGate`),
        pollIntervalMs: positiveInteger(candidateGate, "pollIntervalMs", `${path}.bridge.candidateGate`),
      },
      inbox: {
        claimName: y.kubernetesNameField(inbox, "claimName", `${path}.bridge.inbox`),
        path: y.absolutePathField(inbox, "path", `${path}.bridge.inbox`),
        storageSize: quantity(inbox, "storageSize", `${path}.bridge.inbox`),
        maxBytes: positiveInteger(inbox, "maxBytes", `${path}.bridge.inbox`),
        committedRetentionSeconds: positiveInteger(inbox, "committedRetentionSeconds", `${path}.bridge.inbox`),
        cleanupIntervalMs: positiveInteger(inbox, "cleanupIntervalMs", `${path}.bridge.inbox`),
      },
      retry: parseWebhookRetry(y.objectField(bridge, "retry", `${path}.bridge`), `${path}.bridge.retry`),
    },
    gitOpsDelivery: parseWebhookGitOpsDelivery(gitOpsDelivery, `${path}.gitOpsDelivery`),
  };
}

function parseWebhookGitOpsDelivery(record: Record<string, unknown>, path: string): GiteaWebhookGitOpsDelivery {
  const application = y.objectField(record, "application", path);
  const author = y.objectField(record, "author", path);
  const cas = y.objectField(record, "cas", path);
  return {
    enabled: y.booleanField(record, "enabled", path),
    targetId: y.stringField(record, "targetId", path),
    readUrl: urlField(record, "readUrl", path),
    writeUrl: urlField(record, "writeUrl", path),
    branch: gitBranchField(record, "branch", path),
    sourceSnapshotPrefix: refPrefixField(record, "sourceSnapshotPrefix", path),
    desiredManifestPath: safeRelativePathField(record, "desiredManifestPath", path),
    bootstrapApplicationPath: safeRelativePathField(record, "bootstrapApplicationPath", path),
    releaseStatePath: safeRelativePathField(record, "releaseStatePath", path),
    application: {
      name: y.kubernetesNameField(application, "name", `${path}.application`),
      namespace: y.kubernetesNameField(application, "namespace", `${path}.application`),
      project: y.kubernetesNameField(application, "project", `${path}.application`),
      repoUrl: urlField(application, "repoUrl", `${path}.application`),
      targetRevision: gitBranchField(application, "targetRevision", `${path}.application`),
      path: safeRelativePathField(application, "path", `${path}.application`),
      destinationNamespace: y.kubernetesNameField(application, "destinationNamespace", `${path}.application`),
      automated: y.booleanField(application, "automated", `${path}.application`),
    },
    author: {
      name: y.stringField(author, "name", `${path}.author`),
      email: y.stringField(author, "email", `${path}.author`),
    },
    cas: {
      maxAttempts: positiveInteger(cas, "maxAttempts", `${path}.cas`),
    },
  };
}

function parseWebhookRetry(record: Record<string, unknown>, path: string): GiteaWebhookSync["bridge"]["retry"] {
  return {
    maxAttempts: positiveInteger(record, "maxAttempts", path),
    initialDelayMs: positiveInteger(record, "initialDelayMs", path),
    maxDelayMs: positiveInteger(record, "maxDelayMs", path),
    terminalRetryDelayMs: positiveInteger(record, "terminalRetryDelayMs", path),
    attemptTimeoutMs: positiveInteger(record, "attemptTimeoutMs", path),
    scanIntervalMs: positiveInteger(record, "scanIntervalMs", path),
  };
}

function parsePublicExposure(record: Record<string, unknown>, path: string): GiteaPublicExposure {
  const dns = y.objectField(record, "dns", path);
  const legacyCleanup = y.objectField(record, "legacyCleanup", path);
  const publicBaseUrl = httpsUrlField(record, "publicBaseUrl", path);
  const hostname = y.hostField(dns, "hostname", `${path}.dns`);
  if (new URL(publicBaseUrl).hostname !== hostname) throw new Error(`${configLabel}.${path}.dns.hostname must match publicBaseUrl`);
  const routes = y.arrayOfRecords(record.routes, `${path}.routes`).map((route, index) => {
    const routePath = `${path}.routes[${index}]`;
    return {
      path: y.stringField(route, "path", routePath),
      upstream: urlField(route, "upstream", routePath),
      ...(route.responseHeaderTimeout === undefined
        ? {}
        : { responseHeaderTimeout: y.stringField(route, "responseHeaderTimeout", routePath) }),
    };
  });
  return {
    enabled: y.booleanField(record, "enabled", path),
    publicBaseUrl,
    upstream: urlField(record, "upstream", path),
    healthPath: y.apiPathField(record, "healthPath", path),
    dns: {
      hostname,
      expectedA: y.stringField(dns, "expectedA", `${path}.dns`),
      resolvers: y.stringArrayField(dns, "resolvers", `${path}.dns`),
    },
    routes,
    legacyCleanup: {
      enabled: y.booleanField(legacyCleanup, "enabled", `${path}.legacyCleanup`),
      execute: y.booleanField(legacyCleanup, "execute", `${path}.legacyCleanup`),
      jobName: y.kubernetesNameField(legacyCleanup, "jobName", `${path}.legacyCleanup`),
      serviceAccountName: y.kubernetesNameField(legacyCleanup, "serviceAccountName", `${path}.legacyCleanup`),
      image: y.stringField(legacyCleanup, "image", `${path}.legacyCleanup`),
      activeDeadlineSeconds: positiveInteger(legacyCleanup, "activeDeadlineSeconds", `${path}.legacyCleanup`),
      ttlSecondsAfterFinished: positiveInteger(legacyCleanup, "ttlSecondsAfterFinished", `${path}.legacyCleanup`),
      deploymentName: y.kubernetesNameField(legacyCleanup, "deploymentName", `${path}.legacyCleanup`),
      secretName: y.kubernetesNameField(legacyCleanup, "secretName", `${path}.legacyCleanup`),
    },
  };
}

function parseResponsibility(record: Record<string, unknown>, index: number): GiteaSourceResponsibility {
  const path = `sourceAuthority.responsibilities[${index}]`;
  return {
    name: y.stringField(record, "name", path),
    current: y.stringField(record, "current", path),
    target: y.stringField(record, "target", path),
    disposition: y.enumField(record, "disposition", path, ["replaced-by-gitea", "retained-for-gitops-flush", "migration-readonly"] as const),
  };
}

function parseMirrorRepository(record: Record<string, unknown>, index: number): GiteaMirrorRepository {
  const path = `sourceAuthority.repositories[${index}]`;
  const upstream = y.objectField(record, "upstream", path);
  const gitea = y.objectField(record, "gitea", path);
  const gitops = y.objectField(record, "gitops", path);
  const snapshot = y.objectField(record, "snapshot", path);
  const legacyGitMirror = record.legacyGitMirror === null || record.legacyGitMirror === undefined
    ? null
    : y.objectField(record, "legacyGitMirror", path);
  const credentialOverride = record.credentialOverride === undefined
    ? null
    : y.objectField(record, "credentialOverride", path);
  const githubOverride = credentialOverride === null
    ? null
    : y.objectField(credentialOverride, "github", `${path}.credentialOverride`);
  const githubPermissions = githubOverride === null
    ? null
    : y.objectField(githubOverride, "permissions", `${path}.credentialOverride.github`);
  return {
    key: y.stringField(record, "key", path),
    targetId: y.stringField(record, "targetId", path),
    credentialOverride: githubOverride === null || githubPermissions === null ? null : {
      github: {
        ...parseGithubCredential(githubOverride, `${path}.credentialOverride.github`),
        permissions: {
          contents: y.enumField(githubPermissions, "contents", `${path}.credentialOverride.github.permissions`, ["read"] as const),
          metadata: y.enumField(githubPermissions, "metadata", `${path}.credentialOverride.github.permissions`, ["read"] as const),
          webhooks: y.enumField(githubPermissions, "webhooks", `${path}.credentialOverride.github.permissions`, ["read-write"] as const),
        },
      },
    },
    upstream: {
      repository: repositoryField(upstream, "repository", `${path}.upstream`),
      cloneUrl: gitCloneUrlField(upstream, "cloneUrl", `${path}.upstream`),
      branch: y.stringField(upstream, "branch", `${path}.upstream`),
      visibility: upstream.visibility === undefined ? "public" : y.enumField(upstream, "visibility", `${path}.upstream`, ["public", "private"] as const),
    },
    gitea: {
      owner: y.stringField(gitea, "owner", `${path}.gitea`),
      name: giteaRepoNameField(gitea, "name", `${path}.gitea`),
      mirrorMode: y.enumField(gitea, "mirrorMode", `${path}.gitea`, ["controlled-push"] as const),
      publicRead: y.booleanField(gitea, "publicRead", `${path}.gitea`),
      readUrl: urlField(gitea, "readUrl", `${path}.gitea`),
    },
    gitops: {
      branch: y.stringField(gitops, "branch", `${path}.gitops`),
      flushDisposition: y.stringField(gitops, "flushDisposition", `${path}.gitops`),
    },
    snapshot: {
      prefix: refPrefixField(snapshot, "prefix", `${path}.snapshot`),
    },
    legacyGitMirror: legacyGitMirror === null ? null : {
      readUrl: urlField(legacyGitMirror, "readUrl", `${path}.legacyGitMirror`),
      configRef: legacyGitMirror.configRef === undefined ? null : y.stringField(legacyGitMirror, "configRef", `${path}.legacyGitMirror`),
      disposition: y.enumField(legacyGitMirror, "disposition", `${path}.legacyGitMirror`, ["replaced-by-gitea", "retained-for-gitops-flush", "migration-readonly"] as const),
    },
  };
}

function parseTarget(record: Record<string, unknown>, index: number): GiteaTarget {
  const path = `targets[${index}]`;
  const publicExposureRaw = record.publicExposure;
  if (publicExposureRaw !== undefined && (typeof publicExposureRaw !== "object" || publicExposureRaw === null || Array.isArray(publicExposureRaw))) {
    throw new Error(`${configLabel}.${path}.publicExposure must be an object`);
  }
  const webhookSyncRaw = record.webhookSync;
  if (webhookSyncRaw !== undefined && (typeof webhookSyncRaw !== "object" || webhookSyncRaw === null || Array.isArray(webhookSyncRaw))) {
    throw new Error(`${configLabel}.${path}.webhookSync must be an object`);
  }
  const publicExposure = publicExposureRaw as Record<string, unknown> | undefined;
  return {
    id: y.stringField(record, "id", path),
    route: y.stringField(record, "route", path),
    namespace: y.kubernetesNameField(record, "namespace", path),
    role: y.stringField(record, "role", path),
    enabled: y.booleanField(record, "enabled", path),
    createNamespace: y.booleanField(record, "createNamespace", path),
    storageClassName: y.stringField(record, "storageClassName", path),
    publicExposureEnabled: publicExposure === undefined ? true : y.booleanField(publicExposure, "enabled", `${path}.publicExposure`),
    webhookSync: webhookSyncRaw === undefined ? null : parseTargetWebhookSync(webhookSyncRaw as Record<string, unknown>, `${path}.webhookSync`),
  };
}

function parseTargetWebhookSync(record: Record<string, unknown>, path: string): GiteaTargetWebhookSync {
  return {
    publicPath: y.apiPathField(record, "publicPath", path),
  };
}

function validateConfig(gitea: GiteaConfig): void {
  resolveTarget(gitea, gitea.defaults.targetId);
  const gitFetchCredential = gitea.sourceAuthority.credentials.github.gitFetchCredential;
  if (!gitea.sourceAuthority.credentials.github.requiredFor.includes("managed-repository-fetch")) {
    throw new Error(`${configLabel}.sourceAuthority.credentials.github.requiredFor must include managed-repository-fetch`);
  }
  if (!/^[A-Za-z0-9._-]+$/u.test(gitFetchCredential.secretRef.key)) {
    throw new Error(`${configLabel}.sourceAuthority.credentials.github.gitFetchCredential.secretRef.key must be a Kubernetes Secret data key`);
  }
  if (gitFetchCredential.secretRef.name !== gitea.sourceAuthority.webhookSync.bridge.secretName) {
    throw new Error(`${configLabel}.sourceAuthority.credentials.github.gitFetchCredential.secretRef.name must match sourceAuthority.webhookSync.bridge.secretName`);
  }
  if (gitea.targets.some((target) => target.enabled && target.namespace !== gitFetchCredential.secretRef.namespace)) {
    throw new Error(`${configLabel}.sourceAuthority.credentials.github.gitFetchCredential.secretRef.namespace must match every enabled Gitea target namespace`);
  }
  const credentialKeys = new Set([gitFetchCredential.secretRef.key]);
  const credentialEnvNames = new Set([githubTokenEnvNameForSecretKey(gitFetchCredential.secretRef.key)]);
  for (const repo of gitea.sourceAuthority.repositories) {
    const override = repo.credentialOverride?.github;
    if (override === undefined) continue;
    if (!override.requiredFor.includes("managed-repository-fetch")
      || !override.requiredFor.includes("github-head-observe")
      || !override.requiredFor.includes("github-hooks-list")
      || !override.requiredFor.includes("github-hooks-reconcile")) {
      throw new Error(`${configLabel}.sourceAuthority.repositories.${repo.key}.credentialOverride.github.requiredFor must cover fetch, head observation and hook reconciliation`);
    }
    if (override.gitFetchCredential.secretRef.name !== gitea.sourceAuthority.webhookSync.bridge.secretName) {
      throw new Error(`${configLabel}.sourceAuthority.repositories.${repo.key}.credentialOverride.github.gitFetchCredential.secretRef.name must match sourceAuthority.webhookSync.bridge.secretName`);
    }
    const target = resolveTarget(gitea, repo.targetId);
    if (override.gitFetchCredential.secretRef.namespace !== target.namespace) {
      throw new Error(`${configLabel}.sourceAuthority.repositories.${repo.key}.credentialOverride.github.gitFetchCredential.secretRef.namespace must match repository target namespace`);
    }
    const key = override.gitFetchCredential.secretRef.key;
    if (!/^[A-Za-z0-9._-]+$/u.test(key) || credentialKeys.has(key)) {
      throw new Error(`${configLabel}.sourceAuthority.repositories.${repo.key}.credentialOverride.github.gitFetchCredential.secretRef.key must be a unique Kubernetes Secret data key`);
    }
    credentialKeys.add(key);
    const envName = githubTokenEnvNameForSecretKey(key);
    if (credentialEnvNames.has(envName)) {
      throw new Error(`${configLabel}.sourceAuthority.repositories.${repo.key}.credentialOverride.github.gitFetchCredential.secretRef.key collides after environment variable normalization`);
    }
    credentialEnvNames.add(envName);
  }
  if (!gitea.sourceAuthority.enabled) throw new Error(`${configLabel}.sourceAuthority.enabled must be true for GH-1550`);
  if (gitea.sourceAuthority.repositories.length < 1) throw new Error(`${configLabel}.sourceAuthority.repositories must not be empty`);
  if (!/^docker\.gitea\.com\/gitea$/u.test(gitea.app.image.repository)) throw new Error(`${configLabel}.app.image.repository must use the official Gitea image registry`);
  if (!/-rootless$/u.test(gitea.app.image.tag)) throw new Error(`${configLabel}.app.image.tag must use a rootless Gitea image`);
  if (gitea.app.service.type !== "ClusterIP") throw new Error(`${configLabel}.app.service.type must stay ClusterIP`);
  if (!gitea.app.actions.enabled) throw new Error(`${configLabel}.app.actions.enabled must stay explicit while Gitea is the source-authority service`);
  if (!gitea.app.registration.disabled) throw new Error(`${configLabel}.app.registration.disabled must be true for the internal POC service`);
  if (gitea.app.probes.healthPath !== gitea.validation.healthPath) throw new Error(`${configLabel}.app.probes.healthPath must match validation.healthPath`);
  if (gitea.app.server.rootUrl !== gitea.app.publicExposure.publicBaseUrl.replace(/\/?$/u, "/")) throw new Error(`${configLabel}.app.server.rootUrl must match app.publicExposure.publicBaseUrl for the Web UI`);
  if (gitea.sourceAuthority.webhookSync.enabled) {
    const events = new Set(gitea.sourceAuthority.webhookSync.events);
    if (!events.has("push")) throw new Error(`${configLabel}.sourceAuthority.webhookSync.events must include push for GitHub -> Gitea source sync`);
    const ingressRetry = gitea.sourceAuthority.webhookSync.ingressRetry;
    const hookReconcile = gitea.sourceAuthority.webhookSync.hookReconcile;
    const bridge = gitea.sourceAuthority.webhookSync.bridge;
    if (bridge.replicas !== 1) throw new Error(`${configLabel}.sourceAuthority.webhookSync.bridge.replicas must be 1 because the durable inbox uses a single-writer Recreate deployment`);
    if (bridge.retry.terminalRetryDelayMs < bridge.retry.maxDelayMs) throw new Error(`${configLabel}.sourceAuthority.webhookSync.bridge.retry.terminalRetryDelayMs must be >= maxDelayMs`);
    if (hookReconcile.enabled) {
      const owner = resolveTarget(gitea, hookReconcile.ownerTargetId);
      if (!gitea.sourceAuthority.repositories.some((repo) => repo.targetId.toLowerCase() === owner.id.toLowerCase())) {
        throw new Error(`${configLabel}.sourceAuthority.webhookSync.hookReconcile.ownerTargetId must own at least one repository`);
      }
      if (hookReconcile.intervalMs < 30_000) throw new Error(`${configLabel}.sourceAuthority.webhookSync.hookReconcile.intervalMs must be >= 30000`);
      if (hookReconcile.requestTimeoutMs > 30_000) throw new Error(`${configLabel}.sourceAuthority.webhookSync.hookReconcile.requestTimeoutMs must be <= 30000`);
      if (hookReconcile.listMaxPages > 20) throw new Error(`${configLabel}.sourceAuthority.webhookSync.hookReconcile.listMaxPages must be <= 20`);
      const recovery = hookReconcile.failedDeliveryRecovery;
      if (recovery.enabled) {
        if (recovery.targetId.toLowerCase() !== owner.id.toLowerCase()) throw new Error(`${configLabel}.sourceAuthority.webhookSync.hookReconcile.failedDeliveryRecovery.targetId must equal the owning target`);
        const recoveryRoot = `${bridge.inbox.path.replace(/\/+$/u, "")}/hook-recovery/`;
        if (!recovery.statePath.startsWith(recoveryRoot)) throw new Error(`${configLabel}.sourceAuthority.webhookSync.hookReconcile.failedDeliveryRecovery.statePath must be under ${recoveryRoot}`);
        if (recovery.historyPerHook > 100) throw new Error(`${configLabel}.sourceAuthority.webhookSync.hookReconcile.failedDeliveryRecovery.historyPerHook must be <= 100`);
        if (recovery.lookbackSeconds > 259_200) throw new Error(`${configLabel}.sourceAuthority.webhookSync.hookReconcile.failedDeliveryRecovery.lookbackSeconds must be <= 259200 because GitHub only supports redelivery for three days`);
        if (recovery.maxRedeliveriesPerCycle > 20) throw new Error(`${configLabel}.sourceAuthority.webhookSync.hookReconcile.failedDeliveryRecovery.maxRedeliveriesPerCycle must be <= 20`);
        if (recovery.maxAttemptsPerDelivery > 10) throw new Error(`${configLabel}.sourceAuthority.webhookSync.hookReconcile.failedDeliveryRecovery.maxAttemptsPerDelivery must be <= 10`);
        if (recovery.cooldownMs < hookReconcile.intervalMs) throw new Error(`${configLabel}.sourceAuthority.webhookSync.hookReconcile.failedDeliveryRecovery.cooldownMs must be >= hookReconcile.intervalMs`);
        if (recovery.stateRetentionSeconds < recovery.lookbackSeconds) throw new Error(`${configLabel}.sourceAuthority.webhookSync.hookReconcile.failedDeliveryRecovery.stateRetentionSeconds must be >= lookbackSeconds`);
        if (recovery.retryStatusCodes.length < 1 || recovery.retryStatusCodes.some((statusCode) => ![502, 503, 504].includes(statusCode))) {
          throw new Error(`${configLabel}.sourceAuthority.webhookSync.hookReconcile.failedDeliveryRecovery.retryStatusCodes may only contain 502, 503 and 504`);
        }
      }
    }
    if (ingressRetry.retryStatusCodes.length < 1 || ingressRetry.retryStatusCodes.some((statusCode) => ![502, 503, 504].includes(statusCode))) {
      throw new Error(`${configLabel}.sourceAuthority.webhookSync.ingressRetry.retryStatusCodes may only contain 502, 503 and 504`);
    }
    if (ingressRetry.enabled) {
      try {
        validateGiteaWebhookTiming(gitea.sourceAuthority.webhookSync.responseBudgetMs, ingressRetry);
      } catch (error) {
        throw new Error(`${configLabel}.sourceAuthority.webhookSync timing invalid: ${String((error as Error).message || error)}`);
      }
    }
    const delivery = gitea.sourceAuthority.webhookSync.gitOpsDelivery;
    if (delivery.enabled) {
      const target = resolveTarget(gitea, delivery.targetId);
      if (target.id !== "NC01") throw new Error(`${configLabel}.sourceAuthority.webhookSync.gitOpsDelivery.targetId must be NC01`);
      if (delivery.application.targetRevision !== delivery.branch) throw new Error(`${configLabel}.sourceAuthority.webhookSync.gitOpsDelivery.application.targetRevision must match branch`);
      if (delivery.application.repoUrl !== delivery.readUrl) throw new Error(`${configLabel}.sourceAuthority.webhookSync.gitOpsDelivery.application.repoUrl must match readUrl`);
      if (dirname(delivery.desiredManifestPath) !== delivery.application.path) throw new Error(`${configLabel}.sourceAuthority.webhookSync.gitOpsDelivery.desiredManifestPath must be directly under application.path`);
      if (!delivery.bootstrapApplicationPath.startsWith("deploy/gitops/unidesk-host/")) throw new Error(`${configLabel}.sourceAuthority.webhookSync.gitOpsDelivery.bootstrapApplicationPath must be under the existing unidesk-host bootstrap path`);
      if (delivery.desiredManifestPath.startsWith("deploy/gitops/unidesk-host/")) throw new Error(`${configLabel}.sourceAuthority.webhookSync.gitOpsDelivery.desiredManifestPath must use an independent GitOps path`);
      if (!/^[^@\s]+@[^@\s]+$/u.test(delivery.author.email)) throw new Error(`${configLabel}.sourceAuthority.webhookSync.gitOpsDelivery.author.email must be an email address`);
      if (delivery.cas.maxAttempts > 5) throw new Error(`${configLabel}.sourceAuthority.webhookSync.gitOpsDelivery.cas.maxAttempts must be <= 5`);
    }
  }
  const webhookPaths = new Set<string>();
  for (const publicPath of webhookPublicPaths(gitea)) {
    if (webhookPaths.has(publicPath)) throw new Error(`${configLabel} webhook publicPath must be unique: ${publicPath}`);
    webhookPaths.add(publicPath);
  }
  const ownerTarget = resolveTarget(gitea, gitea.sourceAuthority.webhookSync.hookReconcile.ownerTargetId);
  const ownerSync = targetWebhookSync(gitea, ownerTarget);
  const webhookRoute = gitea.app.publicExposure.routes.find((route) => route.path === `${ownerSync.publicPath}*`);
  if (webhookRoute === undefined) throw new Error(`${configLabel}.app.publicExposure.routes must expose the owner webhook path ${ownerSync.publicPath}*`);
  if (gitea.app.publicExposure.routes.at(-1)?.path !== "*") throw new Error(`${configLabel}.app.publicExposure.routes must end with the Gitea fallback route`);
  const repoKeys = new Set<string>();
  for (const repo of gitea.sourceAuthority.repositories) {
    if (repoKeys.has(repo.key)) throw new Error(`${configLabel}.sourceAuthority.repositories contains duplicate key ${repo.key}`);
    repoKeys.add(repo.key);
    resolveTarget(gitea, repo.targetId);
    const readUrl = new URL(repo.gitea.readUrl);
    if (readUrl.hostname !== `${gitea.app.serviceName}.${resolveTarget(gitea, repo.targetId).namespace}.svc.cluster.local`) throw new Error(`${configLabel}.sourceAuthority.repositories.${repo.key}.gitea.readUrl must use the internal k3s Gitea Service DNS`);
    if (readUrl.hostname === new URL(gitea.app.publicExposure.publicBaseUrl).hostname) throw new Error(`${configLabel}.sourceAuthority.repositories.${repo.key}.gitea.readUrl must not use the public Web UI hostname`);
    if (repo.upstream.visibility === "private" && repo.gitea.publicRead) throw new Error(`${configLabel}.sourceAuthority.repositories.${repo.key}.gitea.publicRead must be false for a private upstream`);
  }
}

export function resolveTarget(gitea: GiteaConfig, targetId: string | null): GiteaTarget {
  const resolved = targetId ?? gitea.defaults.targetId;
  const target = gitea.targets.find((item) => item.id.toLowerCase() === resolved.toLowerCase());
  if (target === undefined) throw new Error(`unknown gitea target ${resolved}; known targets: ${gitea.targets.map((item) => item.id).join(", ")}`);
  if (!target.enabled) throw new Error(`gitea target ${target.id} is disabled in ${configLabel}`);
  return target;
}

export function targetWebhookSync(gitea: GiteaConfig, target: GiteaTarget): GiteaWebhookSync {
  const base = gitea.sourceAuthority.webhookSync;
  if (target.webhookSync === null) return base;
  return {
    ...base,
    publicPath: target.webhookSync.publicPath,
  };
}

export function webhookPublicPaths(gitea: GiteaConfig): string[] {
  const sync = gitea.sourceAuthority.webhookSync;
  if (!sync.enabled) return [];
  const paths = [sync.publicPath];
  for (const target of gitea.targets) {
    if (!target.enabled || target.webhookSync === null) continue;
    const targetSync = targetWebhookSync(gitea, target);
    paths.push(targetSync.publicPath);
  }
  return paths;
}

function positiveInteger(obj: Record<string, unknown>, key: string, path: string): number {
  const value = y.integerField(obj, key, path);
  if (value < 1) throw new Error(`${configLabel}.${path}.${key} must be positive`);
  return value;
}

function boundedTimeout(obj: Record<string, unknown>, key: string, path: string): number {
  const value = positiveInteger(obj, key, path);
  if (value > 55) throw new Error(`${configLabel}.${path}.${key} must fit the 60s trans short-connection budget`);
  return value;
}

function literalField<T extends string>(obj: Record<string, unknown>, key: string, path: string, allowed: T[]): T {
  const value = y.stringField(obj, key, path);
  if (!allowed.includes(value as T)) throw new Error(`${configLabel}.${path}.${key} must be one of ${allowed.join(", ")}`);
  return value as T;
}

function optionalLiteralField<T extends string>(obj: Record<string, unknown>, key: string, path: string, allowed: readonly T[]): T | null {
  if (obj[key] === undefined || obj[key] === null) return null;
  const value = y.stringField(obj, key, path);
  if (!allowed.includes(value as T)) throw new Error(`${configLabel}.${path}.${key} must be one of ${allowed.join(", ")}`);
  return value as T;
}

function quantity(obj: Record<string, unknown>, key: string, path: string): string {
  const value = y.stringField(obj, key, path);
  if (!/^[0-9]+(?:m|Ki|Mi|Gi|Ti)?$/u.test(value)) throw new Error(`${configLabel}.${path}.${key} must be a Kubernetes quantity`);
  return value;
}

function secretSourceRefField(obj: Record<string, unknown>, key: string, path: string): string {
  const value = y.stringField(obj, key, path);
  if (value.includes("..") || !/^[A-Za-z0-9_./-]+$/u.test(value)) throw new Error(`${configLabel}.${path}.${key} must be a source ref path without ..`);
  return value;
}

function repositoryField(obj: Record<string, unknown>, key: string, path: string): string {
  const value = y.stringField(obj, key, path);
  if (!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/u.test(value)) throw new Error(`${configLabel}.${path}.${key} must be owner/repo`);
  return value;
}

function giteaRepoNameField(obj: Record<string, unknown>, key: string, path: string): string {
  const value = y.stringField(obj, key, path);
  if (!/^[A-Za-z0-9_.-]+$/u.test(value) || value.includes("..")) throw new Error(`${configLabel}.${path}.${key} must be a Gitea repository name`);
  return value;
}

function gitCloneUrlField(obj: Record<string, unknown>, key: string, path: string): string {
  const value = urlField(obj, key, path);
  if (!/^https:\/\/github\.com\/[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+\.git$/u.test(value)) throw new Error(`${configLabel}.${path}.${key} must be an official GitHub HTTPS clone URL`);
  return value;
}

function refPrefixField(obj: Record<string, unknown>, key: string, path: string): string {
  const value = y.stringField(obj, key, path);
  if (!/^refs\/[A-Za-z0-9._/-]+$/u.test(value) || value.includes("..")) throw new Error(`${configLabel}.${path}.${key} must be a git ref prefix`);
  return value.replace(/\/+$/u, "");
}

function urlField(obj: Record<string, unknown>, key: string, path: string): string {
  const value = y.stringField(obj, key, path);
  const parsed = new URL(value);
  if (!["http:", "https:"].includes(parsed.protocol) || parsed.search || parsed.hash) throw new Error(`${configLabel}.${path}.${key} must be an http(s) URL without query or hash`);
  return value;
}

function gitBranchField(obj: Record<string, unknown>, key: string, path: string): string {
  const value = y.stringField(obj, key, path);
  if (!/^[A-Za-z0-9._/-]+$/u.test(value) || value.startsWith("/") || value.endsWith("/") || value.includes("..") || value.includes("//")) {
    throw new Error(`${configLabel}.${path}.${key} must be a safe Git branch name`);
  }
  return value;
}

function safeRelativePathField(obj: Record<string, unknown>, key: string, path: string): string {
  const value = y.stringField(obj, key, path);
  if (value.startsWith("/") || value.endsWith("/") || value.split("/").some((segment) => segment === "" || segment === "." || segment === "..") || !/^[A-Za-z0-9._/-]+$/u.test(value)) {
    throw new Error(`${configLabel}.${path}.${key} must be a safe relative path`);
  }
  return value;
}

function httpsUrlField(obj: Record<string, unknown>, key: string, path: string): string {
  const value = urlField(obj, key, path);
  if (new URL(value).protocol !== "https:") throw new Error(`${configLabel}.${path}.${key} must be an https URL`);
  return value;
}
