import * as plugins from '../../../plugins.js';
import type { IRouteContext } from '../../../core/models/route-context.js';

// Re-export IRouteContext for convenience
export type { IRouteContext };

/**
 * Supported action types for route configurations
 */
export type TRouteActionType = 'forward' | 'socket-handler';

/**
 * Socket handler function type
 */
export type TSocketHandler = (socket: plugins.net.Socket, context: IRouteContext) => void | Promise<void>;

/**
 * TLS handling modes for route configurations
 */
export type TTlsMode = 'passthrough' | 'terminate' | 'terminate-and-reencrypt';

/**
 * Transport protocol for route matching
 */
export type TTransportProtocol = 'tcp' | 'udp' | 'all';

/**
 * Inbound PROXY protocol handling for a listener.
 */
export type TInboundProxyProtocolMode = 'reject' | 'optional' | 'required';

export interface IInboundProxyProtocolPolicy {
  mode: TInboundProxyProtocolMode;
  trustedProxyIPs?: string[];
}

/**
 * Port range specification format.
 * Supports: single number, array of numbers, array of ranges, or mixed arrays.
 */
export type TPortRange = number | Array<number | { from: number; to: number }>;

/**
 * Route match criteria for incoming requests
 */
export interface IRouteMatch {
  // Listen on these ports (required)
  ports: TPortRange;

  // Transport protocol: 'tcp' (default), 'udp', or 'all' (both TCP and UDP)
  transport?: TTransportProtocol;

  // Inbound PROXY protocol policy for this listener. Routes sharing the same
  // port/transport must declare the same policy.
  inboundProxyProtocol?: IInboundProxyProtocolPolicy;

  // Optional domain patterns to match (default: all domains)
  domains?: string | string[];

  // Advanced matching criteria
  path?: string;           // Match specific paths
  clientIp?: string[];     // Match specific client IPs
  tlsVersion?: string[];   // Match specific TLS versions
  headers?: Record<string, string | RegExp>; // Match specific HTTP headers
  protocol?: 'http' | 'tcp' | 'udp' | 'quic' | 'http3';  // Match specific protocol
}


/**
 * Target-specific match criteria for sub-routing within a route
 */
export interface ITargetMatch {
  ports?: number[];                             // Match specific ports from the route
  path?: string;                                // Match specific paths (supports wildcards like /api/*)
  headers?: Record<string, string | RegExp>;    // Match specific HTTP headers
  method?: string[];                            // Match specific HTTP methods (GET, POST, etc.)
}

/**
 * Target configuration for forwarding with sub-matching and overrides
 */
export interface IRouteTarget {
  // Optional sub-matching criteria within the route
  match?: ITargetMatch;
  
  // Target destination
  host: string | string[] | ((context: IRouteContext) => string | string[]);  // Host or hosts with optional function for dynamic resolution
  port: number | 'preserve' | ((context: IRouteContext) => number);  // Port with optional function for dynamic mapping (use 'preserve' to keep the incoming port)
  
  // Optional target-specific overrides (these override route-level settings)
  tls?: IRouteTls;                             // Override route-level TLS settings
  websocket?: IRouteWebSocket;                 // Override route-level WebSocket settings
  loadBalancing?: IRouteLoadBalancing;         // Override route-level load balancing
  sendProxyProtocol?: boolean;                 // Override route-level proxy protocol setting
  headers?: IRouteHeaders;                     // Override route-level headers
  advanced?: IRouteAdvanced;                   // Override route-level advanced settings
  
  // Override transport for backend connection (e.g., receive QUIC but forward as HTTP/1.1 via TCP)
  backendTransport?: 'tcp' | 'udp';

  // Priority for matching (higher values are checked first, default: 0)
  priority?: number;
}

/**
 * ACME configuration for automatic certificate provisioning
 */
export interface IRouteAcme {
  email: string;                    // Contact email for ACME account
  useProduction?: boolean;          // Use production ACME servers (default: false)
  challengePort?: number;           // Port for HTTP-01 challenges (default: 80)
  renewBeforeDays?: number;         // Days before expiry to renew (default: 30)
}

/**
 * TLS configuration for route actions
 */
export interface IRouteTls {
  mode: TTlsMode;
  certificate?: 'auto' | {          // Auto = use ACME
    key: string;                   // PEM-encoded private key
    cert: string;                  // PEM-encoded certificate
    ca?: string;                   // PEM-encoded CA chain
    keyFile?: string;              // Path to key file (overrides key)
    certFile?: string;             // Path to cert file (overrides cert)
  };
  acme?: IRouteAcme;               // ACME options when certificate is 'auto'
  versions?: string[];             // Allowed TLS versions (e.g., ['TLSv1.2', 'TLSv1.3'])
  ciphers?: string;                // OpenSSL cipher string
  honorCipherOrder?: boolean;      // Use server's cipher preferences
  sessionTimeout?: number;         // TLS session timeout in seconds
}

/**
 * Authentication options
 */
export interface IRouteAuthentication {
  type: 'basic' | 'digest' | 'oauth' | 'jwt';
  credentials?: {
    username: string;
    password: string;
  }[];
  realm?: string;
  jwtSecret?: string;
  jwtIssuer?: string;
  oauthProvider?: string;
  oauthClientId?: string;
  oauthClientSecret?: string;
  oauthRedirectUri?: string;
  // Specific options for different auth types
  options?: Record<string, unknown>;
}

/**
 * Security options for routes
 */
export interface IRouteSecurity {
  // Access control lists.
  // Entries can be plain IP/CIDR strings (full route access) or
  // objects { ip, domains } to scope access to specific domains on this route.
  ipAllowList?: Array<string | { ip: string; domains: string[] }>;
  ipBlockList?: string[];       // IP addresses that are blocked from connecting
  
  // Connection limits
  maxConnections?: number;      // Maximum concurrent connections
  
  // Authentication
  authentication?: IRouteAuthentication;
  
  // Rate limiting
  rateLimit?: IRouteRateLimit;
  
  // Authentication methods
  basicAuth?: {
    enabled: boolean;
    users: Array<{ username: string; password: string }>;
    realm?: string;
    excludePaths?: string[];
  };
  
  jwtAuth?: {
    enabled: boolean;
    secret: string;
    algorithm?: string;
    issuer?: string;
    audience?: string;
    expiresIn?: number;
    excludePaths?: string[];
  };

  vpn?: {
    /** Require authenticated VPN metadata from a trusted PROXY v2 TLV. */
    required?: boolean;
    /** Allowed VPN client IDs. Entries can be full-route or domain-scoped. */
    allowedClients?: Array<string | { clientId: string; domains: string[] }>;
    /** Allowed VPN tunnel IPs, kept for compatibility. Prefer allowedClients. */
    allowedAssignedIps?: string[];
  };

  /** Browser-facing challenge policy. SmartProxy enforces this in the HTTP path. */
  challenge?: plugins.smartchallenge.IRouteChallengeConfig;
}

/**
 * Static file server configuration
 */
export interface IRouteStaticFiles {
  root: string;
  index?: string[];
  headers?: Record<string, string>;
  directory?: string;
  indexFiles?: string[];
  cacheControl?: string;
  expires?: number;
  followSymlinks?: boolean;
  disableDirectoryListing?: boolean;
}

/**
 * Test route response configuration
 */
export interface IRouteTestResponse {
  status: number;
  headers: Record<string, string>;
  body: string;
}

/**
 * URL rewriting configuration
 */
export interface IRouteUrlRewrite {
  pattern: string;            // RegExp pattern to match in URL
  target: string;             // Replacement pattern (supports template variables like {domain})
  flags?: string;             // RegExp flags like 'g' for global replacement
  onlyRewritePath?: boolean;  // Only apply to path, not query string
}

/**
 * Advanced options for route actions
 */
export interface IRouteAdvanced {
  timeout?: number;
  headers?: Record<string, string>;
  keepAlive?: boolean;
  staticFiles?: IRouteStaticFiles;
  testResponse?: IRouteTestResponse;
  urlRewrite?: IRouteUrlRewrite; // URL rewriting configuration
  // Additional advanced options would go here
}

/**
 * WebSocket configuration
 */
export interface IRouteWebSocket {
  enabled: boolean;                   // Whether WebSockets are enabled for this route
  pingInterval?: number;              // Interval for sending ping frames (ms)
  pingTimeout?: number;               // Timeout for pong response (ms)
  maxPayloadSize?: number;            // Maximum message size in bytes
  customHeaders?: Record<string, string>; // Custom headers for WebSocket handshake
  subprotocols?: string[];            // Supported subprotocols
  rewritePath?: string;               // Path rewriting for WebSocket connections
  allowedOrigins?: string[];          // Allowed origins for WebSocket connections
  authenticateRequest?: boolean;      // Whether to apply route security to WebSocket connections
}

/**
 * Load balancing configuration
 */
export interface IRouteLoadBalancing {
  algorithm: 'round-robin' | 'least-connections' | 'ip-hash';
  healthCheck?: {
    path: string;
    interval: number;
    timeout: number;
    unhealthyThreshold: number;
    healthyThreshold: number;
  };
}

/**
 * Action configuration for route handling
 */
export interface IRouteAction {
  // Basic routing
  type: TRouteActionType;

  // Targets for forwarding (array supports multiple targets with sub-matching)
  // Required for 'forward' action type
  targets?: IRouteTarget[];

  // TLS handling (default for all targets, can be overridden per target)
  tls?: IRouteTls;

  // WebSocket support (default for all targets, can be overridden per target)
  websocket?: IRouteWebSocket;

  // Load balancing options (default for all targets, can be overridden per target)
  loadBalancing?: IRouteLoadBalancing;

  // Advanced options (default for all targets, can be overridden per target)
  advanced?: IRouteAdvanced;
  
  // Additional options for backend-specific settings
  options?: {
    backendProtocol?: 'http1' | 'http2' | 'http3' | 'auto';
    [key: string]: any;
  };

  // Forwarding engine specification
  forwardingEngine?: 'node' | 'nftables';

  // NFTables-specific options
  nftables?: INfTablesOptions;

  // Socket handler function (when type is 'socket-handler')
  socketHandler?: TSocketHandler;

  // Datagram handler function for UDP (when type is 'socket-handler' and transport is 'udp')
  datagramHandler?: TDatagramHandler;

  // PROXY protocol support (default for all targets, can be overridden per target)
  sendProxyProtocol?: boolean;

  // UDP-specific settings (session tracking, datagram limits, QUIC config)
  udp?: IRouteUdp;
}

/**
 * Rate limiting configuration
 */
export interface IRouteRateLimit {
  enabled: boolean;
  maxRequests: number;
  window: number; // Time window in seconds
  keyBy?: 'ip' | 'path' | 'header';
  headerName?: string;
  errorMessage?: string;
}

// IRouteSecurity is defined above - unified definition is used for all routes

/**
 * NFTables-specific configuration options
 */
export interface INfTablesOptions {
  preserveSourceIP?: boolean;     // Preserve original source IP address
  protocol?: 'tcp' | 'udp' | 'all'; // Protocol to forward
  maxRate?: string;               // QoS rate limiting (e.g. "10mbps")
  priority?: number;              // QoS priority (1-10, lower is higher priority)
  tableName?: string;             // Optional custom table name
  useIPSets?: boolean;            // Use IP sets for performance
  useAdvancedNAT?: boolean;       // Use connection tracking for stateful NAT
}

/**
 * CORS configuration for a route
 */
export interface IRouteCors {
  enabled: boolean;                     // Whether CORS is enabled for this route
  allowOrigin?: string | string[];      // Allowed origins (*,domain.com,[domain1,domain2])
  allowMethods?: string;                // Allowed methods (GET,POST,etc.)
  allowHeaders?: string;                // Allowed headers
  allowCredentials?: boolean;           // Whether to allow credentials
  exposeHeaders?: string;               // Headers to expose to the client
  maxAge?: number;                      // Preflight cache duration in seconds
  preflight?: boolean;                  // Whether to respond to preflight requests
}

/**
 * Headers configuration
 */
export interface IRouteHeaders {
  request?: Record<string, string>;     // Headers to add/modify for requests to backend
  response?: Record<string, string>;    // Headers to add/modify for responses to client
  cors?: IRouteCors;                    // CORS configuration
}

/**
 * The core unified configuration interface
 */
export interface IRouteConfig {
  // Unique identifier
  id?: string;

  // What to match
  match: IRouteMatch;

  // What to do with matched traffic
  action: IRouteAction;

  // Custom headers
  headers?: IRouteHeaders;

  // Security features
  security?: IRouteSecurity;

  // Optional metadata
  name?: string;             // Human-readable name for this route
  description?: string;      // Description of the route's purpose
  priority?: number;         // Controls matching order (higher = matched first)
  tags?: string[];           // Arbitrary tags for categorization
  enabled?: boolean;         // Whether the route is active (default: true)
}

// ─── UDP & QUIC Types ─────────────────────────────────────────────────

/**
 * Handler for individual UDP datagrams.
 * Called for each incoming datagram on a socket-handler route with UDP transport.
 */
export type TDatagramHandler = (
  datagram: Buffer,
  info: IDatagramInfo,
  reply: (data: Buffer) => void
) => void | Promise<void>;

/**
 * Metadata for a received UDP datagram
 */
export interface IDatagramInfo {
  /** Source IP address */
  sourceIp: string;
  /** Source port */
  sourcePort: number;
  /** Destination (local) port the datagram arrived on */
  destPort: number;
  /** Route context */
  context: IRouteContext;
}

/**
 * UDP-specific settings for route actions
 */
export interface IRouteUdp {
  /** Idle timeout for a UDP session/flow (keyed by src IP:port), in ms. Default: 60000 */
  sessionTimeout?: number;
  /** Max concurrent UDP sessions per source IP. Default: 1000 */
  maxSessionsPerIP?: number;
  /** Max accepted datagram size in bytes. Oversized datagrams are dropped. Default: 65535 */
  maxDatagramSize?: number;
  /** QUIC-specific configuration. When present, traffic is treated as QUIC. */
  quic?: IRouteQuic;
}

/**
 * QUIC and HTTP/3 settings
 */
export interface IRouteQuic {
  /** QUIC connection idle timeout in ms. Default: 30000 */
  maxIdleTimeout?: number;
  /** Max concurrent bidirectional streams per QUIC connection. Default: 100 */
  maxConcurrentBidiStreams?: number;
  /** Max concurrent unidirectional streams per QUIC connection. Default: 100 */
  maxConcurrentUniStreams?: number;
  /** Enable HTTP/3 over this QUIC endpoint. Default: false */
  enableHttp3?: boolean;
  /** Port to advertise in Alt-Svc header on TCP HTTP responses. Default: listening port */
  altSvcPort?: number;
  /** Max age for Alt-Svc advertisement in seconds. Default: 86400 */
  altSvcMaxAge?: number;
  /** Initial congestion window size in bytes. Default: implementation-defined */
  initialCongestionWindow?: number;
}

// Configuration moved to models/interfaces.ts as ISmartProxyOptions
