/**
 * NFTables Route Helper Functions
 *
 * This module provides utility functions for creating NFTables-based route configurations
 * for high-performance packet forwarding at the kernel level.
 */

import type { IRouteConfig, IRouteMatch, IRouteAction, TPortRange } from '../../models/route-types.js';
import { createHttpToHttpsRedirect } from './https-helpers.js';

/**
 * Create an NFTables-based route for high-performance packet forwarding
 * @param nameOrDomains Name or domain(s) to match
 * @param target Target host and port
 * @param options Additional route options
 * @returns Route configuration object
 */
export function createNfTablesRoute(
  nameOrDomains: string | string[],
  target: { host: string; port: number | 'preserve' },
  options: {
    ports?: TPortRange;
    protocol?: 'tcp' | 'udp' | 'all';
    preserveSourceIP?: boolean;
    ipAllowList?: string[];
    ipBlockList?: string[];
    maxRate?: string;
    priority?: number;
    useTls?: boolean;
    tableName?: string;
    useIPSets?: boolean;
    useAdvancedNAT?: boolean;
  } = {}
): IRouteConfig {
  // Determine if this is a name or domain
  let name: string;
  let domains: string | string[] | undefined;

  if (Array.isArray(nameOrDomains) || (typeof nameOrDomains === 'string' && nameOrDomains.includes('.'))) {
    domains = nameOrDomains;
    name = Array.isArray(nameOrDomains) ? nameOrDomains[0] : nameOrDomains;
  } else {
    name = nameOrDomains;
    domains = undefined; // No domains
  }

  // Create route match
  const match: IRouteMatch = {
    domains,
    ports: options.ports || 80
  };

  // Create route action
  const action: IRouteAction = {
    type: 'forward',
    targets: [{
      host: target.host,
      port: target.port
    }],
    forwardingEngine: 'nftables',
    nftables: {
      protocol: options.protocol || 'tcp',
      preserveSourceIP: options.preserveSourceIP,
      maxRate: options.maxRate,
      priority: options.priority,
      tableName: options.tableName,
      useIPSets: options.useIPSets,
      useAdvancedNAT: options.useAdvancedNAT
    }
  };

  // Add TLS options if needed
  if (options.useTls) {
    action.tls = {
      mode: 'passthrough'
    };
  }

  // Create the route config
  const routeConfig: IRouteConfig = {
    name,
    match,
    action
  };

  // Add security if allowed or blocked IPs are specified
  if (options.ipAllowList?.length || options.ipBlockList?.length) {
    routeConfig.security = {
      ipAllowList: options.ipAllowList,
      ipBlockList: options.ipBlockList
    };
  }

  return routeConfig;
}

/**
 * Create an NFTables-based TLS termination route
 * @param nameOrDomains Name or domain(s) to match
 * @param target Target host and port
 * @param options Additional route options
 * @returns Route configuration object
 */
export function createNfTablesTerminateRoute(
  nameOrDomains: string | string[],
  target: { host: string; port: number | 'preserve' },
  options: {
    ports?: TPortRange;
    protocol?: 'tcp' | 'udp' | 'all';
    preserveSourceIP?: boolean;
    ipAllowList?: string[];
    ipBlockList?: string[];
    maxRate?: string;
    priority?: number;
    tableName?: string;
    useIPSets?: boolean;
    useAdvancedNAT?: boolean;
    certificate?: 'auto' | { key: string; cert: string };
  } = {}
): IRouteConfig {
  // Create basic NFTables route
  const route = createNfTablesRoute(
    nameOrDomains,
    target,
    {
      ...options,
      ports: options.ports || 443,
      useTls: false
    }
  );

  // Set TLS termination
  route.action.tls = {
    mode: 'terminate',
    certificate: options.certificate || 'auto'
  };

  return route;
}

/**
 * Create a complete NFTables-based HTTPS setup with HTTP redirect
 * @param nameOrDomains Name or domain(s) to match
 * @param target Target host and port
 * @param options Additional route options
 * @returns Array of two route configurations (HTTPS and HTTP redirect)
 */
export function createCompleteNfTablesHttpsServer(
  nameOrDomains: string | string[],
  target: { host: string; port: number | 'preserve' },
  options: {
    httpPort?: TPortRange;
    httpsPort?: TPortRange;
    protocol?: 'tcp' | 'udp' | 'all';
    preserveSourceIP?: boolean;
    ipAllowList?: string[];
    ipBlockList?: string[];
    maxRate?: string;
    priority?: number;
    tableName?: string;
    useIPSets?: boolean;
    useAdvancedNAT?: boolean;
    certificate?: 'auto' | { key: string; cert: string };
  } = {}
): IRouteConfig[] {
  // Create the HTTPS route using NFTables
  const httpsRoute = createNfTablesTerminateRoute(
    nameOrDomains,
    target,
    {
      ...options,
      ports: options.httpsPort || 443
    }
  );

  // Determine the domain(s) for HTTP redirect
  const domains = typeof nameOrDomains === 'string' && !nameOrDomains.includes('.')
    ? undefined
    : nameOrDomains;

  // Extract the HTTPS port for the redirect destination
  const httpsPort = typeof options.httpsPort === 'number'
    ? options.httpsPort
    : Array.isArray(options.httpsPort) && typeof options.httpsPort[0] === 'number'
      ? options.httpsPort[0]
      : 443;

  // Create the HTTP redirect route (this uses standard forwarding, not NFTables)
  const httpRedirectRoute = createHttpToHttpsRedirect(
    domains as any, // Type cast needed since domains can be undefined now
    httpsPort,
    {
      match: {
        ports: options.httpPort || 80,
        domains: domains as any // Type cast needed since domains can be undefined now
      },
      name: `HTTP to HTTPS Redirect for ${Array.isArray(domains) ? domains.join(', ') : domains || 'all domains'}`
    }
  );

  return [httpsRoute, httpRedirectRoute];
}
