/**
 * Agent configuration — PLAN_RELAY_AGENT T2. Zod-validated at the startup boundary (INV9):
 *   - relayUrl  MUST be wss://    (encrypted tunnel only)
 *   - enrollUrl MUST be https://  (enrollment over TLS only)
 *   - localTargetUrl MUST be ws:// to a LOOPBACK host (anti-SSRF: the agent forwards ONLY to
 *     the local web-terminal, never an arbitrary target).
 * Fail-fast on missing/invalid values — never silently default a security-relevant field.
 */
import { homedir } from 'node:os'
import { join } from 'node:path'
import { z } from 'zod'

export interface AgentConfig {
  readonly relayUrl: string
  readonly enrollUrl: string
  readonly stateDir: string
  readonly localTargetUrl: string
  readonly subdomain: string | null
  readonly hostId: string | null
}

const LOOPBACK_HOSTNAMES: readonly string[] = ['localhost', '127.0.0.1', '::1', '[::1]']

/** True iff `url` is ws:// to a loopback host (127.0.0.0/8, localhost, or ::1). */
export function isLoopbackWsUrl(url: string): boolean {
  let parsed: URL
  try {
    parsed = new URL(url)
  } catch {
    return false
  }
  if (parsed.protocol !== 'ws:') return false
  const host = parsed.hostname
  return LOOPBACK_HOSTNAMES.includes(host) || host.startsWith('127.')
}

function hasScheme(url: string, scheme: string): boolean {
  try {
    return new URL(url).protocol === scheme
  } catch {
    return false
  }
}

export const AgentConfigSchema = z
  .object({
    relayUrl: z
      .string()
      .refine((u) => hasScheme(u, 'wss:'), 'relayUrl must be a wss:// URL'),
    enrollUrl: z
      .string()
      .refine((u) => hasScheme(u, 'https:'), 'enrollUrl must be an https:// URL'),
    stateDir: z.string().min(1),
    localTargetUrl: z
      .string()
      .refine(isLoopbackWsUrl, 'localTargetUrl must be a ws:// loopback URL (anti-SSRF)'),
    subdomain: z.string().min(1).nullable(),
    hostId: z.string().min(1).nullable(),
  })
  .strict()
  .readonly()

const DEFAULT_LOCAL_TARGET = 'ws://127.0.0.1:3000'

/** Default state dir where key/cert/content-secret live (~/.web-terminal-agent). */
export function defaultStateDir(): string {
  return join(homedir(), '.web-terminal-agent')
}

/**
 * Build + validate an AgentConfig from env + explicit argv overrides (argv wins). Throws a
 * ZodError (fail-fast) if any required field is missing or fails a scheme/loopback refinement.
 */
export function loadAgentConfig(
  env: NodeJS.ProcessEnv,
  argv: Partial<AgentConfig> = {},
): AgentConfig {
  const merged = {
    relayUrl: argv.relayUrl ?? env.RELAY_URL,
    enrollUrl: argv.enrollUrl ?? env.ENROLL_URL,
    stateDir: argv.stateDir ?? env.STATE_DIR ?? defaultStateDir(),
    localTargetUrl: argv.localTargetUrl ?? env.LOCAL_TARGET_URL ?? DEFAULT_LOCAL_TARGET,
    subdomain: argv.subdomain ?? env.SUBDOMAIN ?? null,
    hostId: argv.hostId ?? env.HOST_ID ?? null,
  }
  return AgentConfigSchema.parse(merged)
}
