import type { OriginGuardOptions } from './origin-guard';
import type { RedirectRouteConfig } from './redirect';
import type { TlsConfig, TlsOption } from '@stacksjs/tlsx';
export type { TlsConfig, TlsOption };
export declare interface StartOptions {
  command: string
  cwd?: string
  env?: Record<string, string>
}
export declare interface PathRewrite {
  from: string
  to: string
  stripPrefix?: boolean
}
export declare interface StaticRouteConfig {
  dir: string
  spa?: boolean
  pathRewriteStyle?: PathRewriteStyle
  maxAge?: number
}
/**
 * HTTP Basic auth for a single route. When set, rpx challenges every request to
 * the route with `401`/`WWW-Authenticate` until valid credentials are supplied
 * (proxy, static, and WebSocket transports are all gated). Credentials may be
 * given inline, as a `users[]` list, and/or via an Apache `htpasswd` file.
 */
export declare interface BasicAuthConfig {
  realm?: string
  username?: string
  password?: string
  users?: Array<{ username: string, password: string }>
  htpasswdFile?: string
}
/**
 * One upstream in a load-balanced pool, with an optional relative weight.
 */
export declare interface UpstreamTarget {
  url: string
  weight?: number
}
/**
 * Health checking for a load-balanced pool. Passive checks (derived from live
 * traffic outcomes) are always on; active checks (periodic out-of-band probes)
 * are opt-in via {@link enabled}.
 */
export declare interface HealthCheckConfig {
  enabled?: boolean
  path?: string
  interval?: number
  timeout?: number
  healthyThreshold?: number
  unhealthyThreshold?: number
}
/** Per-route load-balancing configuration. */
export declare interface LoadBalancerConfig {
  strategy?: LoadBalancerStrategy
  healthCheck?: HealthCheckConfig
}
export declare interface BaseProxyConfig {
  from?: ProxyFrom
  to: string
  loadBalancer?: LoadBalancerConfig
  auth?: BasicAuthConfig
  path?: string
  start?: StartOptions
  pathRewrites?: PathRewrite[]
  static?: string | StaticRouteConfig
  redirect?: string | RedirectRouteConfig
  id?: string
}
export declare interface CleanupConfig {
  domains: string[]
  hosts: boolean
  certs: boolean
  verbose: boolean
  vitePluginUsage?: boolean
}
/**
 * A real PEM cert+key pair on disk for one SNI server name.
 */
export declare interface DomainCert {
  certPath: string
  keyPath: string
}
/**
 * Production TLS using real certs (e.g. Let's Encrypt) served per-domain via
 * SNI on a single listener. Provide either an explicit `domains` map or a
 * `certsDir` convention.
 */
export declare interface ProductionTlsConfig {
  domains?: Record<string, DomainCert>
  certsDir?: string
}
/**
 * On-demand TLS: issue a real (Let's Encrypt, http-01) certificate for an
 * unknown host the first time it's needed, gated by an `ask` callback and/or an
 * `allowedSuffixes` allowlist to prevent abuse.
 *
 * ## Why this is "ask-gated issuance + listener recreate", not at-handshake
 *
 * Bun (verified on 1.3.14 + 1.4.0) has **no working SNICallback** and
 * `server.reload({ tls })` does **not** update certs at runtime. So rpx cannot
 * mint a cert during the TLS handshake the way Caddy's on-demand TLS does.
 * Instead rpx:
 *   1. Sees the first plaintext request for the host on its `:80` listener.
 *   2. Asks `ask(host)` / checks `allowedSuffixes`; if approved, drives the
 *      ACME http-01 flow (serving the challenge from its own `:80`).
 *   3. Writes the PEMs into `certsDir` and rebuilds the `:443` listener with the
 *      augmented SNI cert set (a sub-second `server.stop()` + re-`Bun.serve`).
 * The subsequent HTTPS request then finds the freshly-issued cert.
 *
 * Issuance can also be triggered programmatically via the manager's
 * `ensureCert(host)` (e.g. a tunnel server pre-warming a subdomain's cert on
 * registration) so the cert exists before the first browser hit.
 */
export declare interface OnDemandTlsConfig {
  enabled?: boolean
  ask?: (host: string) => boolean | Promise<boolean>
  allowedSuffixes?: string[]
  email?: string
  staging?: boolean
  certsDir?: string
}
/**
 * One backend a site exposes, mapped to a request path. rpx picks a free port,
 * exports it to the dev command via {@link portEnv}, and proxies {@link path}
 * (under the site host) to `localhost:<port>`.
 *
 * A Stacks app, for example, has three: the frontend at `/` (env `PORT`), the
 * API at `/api` (env `PORT_API`), and the docs at `/docs` (env `PORT_DOCS`).
 */
export declare interface SiteRouteTemplate {
  path?: string
  portEnv: string
  defaultPort?: number
  stripPrefix?: boolean
  readyGate?: boolean
}
/**
 * A lazily-booted project. The first request to {@link to} starts {@link command}
 * in {@link dir}; rpx holds the request behind a "starting…" splash until the
 * site's ready gate passes, then proxies it. After {@link idleTimeoutMs} with no
 * traffic the process is stopped again — so a machine can "have" dozens of sites
 * but only run the ones in active use.
 */
export declare interface SiteConfig {
  to: string
  dir: string
  command: string
  env?: Record<string, string>
  routes?: SiteRouteTemplate[]
  selfRegisters?: boolean
  idleTimeoutMs?: number
}
/**
 * On-demand sites: lazily boot a project's dev server the first time its host is
 * visited, then proxy to it (Valet/puma-dev style). Opt-in via {@link enabled}.
 */
export declare interface OnDemandSitesConfig {
  enabled?: boolean
  sites?: SiteConfig[]
  roots?: string[]
  tlds?: string[]
  idleTimeoutMs?: number
  startupTimeoutMs?: number
}
export declare interface SharedProxyConfig {
  https: boolean | TlsOption
  cleanup: boolean | CleanupOptions
  vitePluginUsage: boolean
  verbose: boolean
  _cachedSSLConfig?: SSLConfig | null
  start?: StartOptions
  cleanUrls: boolean
  changeOrigin?: boolean
  regenerateUntrustedCerts?: boolean
  singlePortMode?: boolean
  httpPort?: number
  httpsPort?: number
  acmeChallengeWebroot?: string
  viaDaemon?: boolean
  hostsManagement?: boolean
  productionCerts?: ProductionTlsConfig
  onDemandTls?: OnDemandTlsConfig
  onDemand?: OnDemandSitesConfig
  originGuard?: OriginGuardOptions
}
export declare interface SingleProxyConfig extends BaseProxyConfig, SharedProxyConfig {}
export declare interface MultiProxyConfig extends SharedProxyConfig {
  proxies: Array<BaseProxyConfig & { cleanUrls: boolean, pathRewrites?: PathRewrite[] }>
}
export declare interface SSLConfig {
  key: string
  cert: string
  ca?: string | string[]
}
export declare interface ProxySetupOptions extends Omit<ProxyOption, 'from'> {
  fromPort: number
  sourceUrl: Pick<URL, 'hostname' | 'host'>
  ssl: SSLConfig | null
  from: string
  originalFrom?: ProxyFrom
  to: string
  portManager?: PortManager
}
export declare interface PortManager {
  usedPorts: Set<number>
  getNextAvailablePort: (startPort: number) => Promise<number>
}
/**
 * How a static-file route maps request paths to files on disk.
 *
 * - `directory` (default): `/about` → `<root>/about/index.html` (SSG dir style).
 * - `flat`: `/about` → `<root>/about.html` (flat-file style).
 */
export type PathRewriteStyle = 'directory' | 'flat';
/**
 * Upstream `host:port` to forward to. Either a single string (backward
 * compatible, degenerate one-item pool) or an array of upstreams — either
 * plain `host:port` strings or {@link UpstreamTarget} objects with a weight —
 * to load-balance across.
 */
export type ProxyFrom = string | Array<string | UpstreamTarget>;
/** How {@link selectUpstream} picks the next upstream from a healthy pool. */
export type LoadBalancerStrategy = 'round-robin' | 'weighted-round-robin' | 'least-connections';
export type BaseProxyOptions = Partial<BaseProxyConfig>;
export type CleanupOptions = Partial<CleanupConfig>;
export type SharedProxyOptions = Partial<SharedProxyConfig>;
export type ProxyConfig = SingleProxyConfig;
export type ProxyConfigs = SingleProxyConfig | MultiProxyConfig;
export type BaseProxyOption = Partial<BaseProxyConfig>;
export type ProxyOption = Partial<SingleProxyConfig>;
export type ProxyOptions = Partial<SingleProxyConfig> | Partial<MultiProxyConfig>;
/**
 * Internal shape used by `startProxies` after merging the built-in defaults with
 * the caller's single- or multi-proxy options. Every field is optional, and the
 * `proxies` array elements tolerate the per-proxy `cleanUrls`/`changeOrigin`
 * overrides the runtime reads — so the merged object can be accessed across both
 * single and multi shapes without falling back to `any`.
 */
export type ResolvedProxyOptions = Partial<SingleProxyConfig> & {
  proxies?: Array<BaseProxyConfig & { cleanUrls?: boolean, changeOrigin?: boolean, pathRewrites?: PathRewrite[] }>
}
