import { spawnSync } from "node:child_process";
import { settings } from "./config.js";
import { deviceCidrs, normalizeCidr } from "./devices.js";

const DEFAULT_NAT_BYPASS_CIDRS =
  "0.0.0.0/8 10.0.0.0/8 100.64.0.0/10 127.0.0.0/8 169.254.0.0/16 172.16.0.0/12 192.168.0.0/16 224.0.0.0/4 240.0.0.0/4";

function splitCidrs(value) {
  return String(value || "")
    .split(/[\s,]+/)
    .map((item) => normalizeCidr(item))
    .filter(Boolean);
}

function unique(list) {
  return [...new Set(list)];
}

export function sourceBypassCidrs(
  profiles,
  envCidrs = process.env.TPROXY_BYPASS_SOURCE_CIDRS || "",
) {
  return unique([
    ...splitCidrs(envCidrs),
    ...deviceCidrs(profiles?.devices || [], "bypass"),
  ]);
}

export function buildSourceBypassIptablesCommands(
  cidrs,
  {
    chain = settings.tproxySourceBypassChain,
    forwardChain = settings.tproxySourceForwardChain,
    natChain = settings.tproxySourceNatChain,
    natBypassCidrs = splitCidrs(
      process.env.BYPASS_CIDRS || DEFAULT_NAT_BYPASS_CIDRS,
    ),
  } = {},
) {
  return [
    ["-w", "-t", "mangle", "-F", chain],
    ["-w", "-F", forwardChain],
    ["-w", "-t", "nat", "-F", natChain],
    ...cidrs.map((cidr) => [
      "-w",
      "-t",
      "mangle",
      "-A",
      chain,
      "-s",
      cidr,
      "-j",
      "ACCEPT",
    ]),
    ...cidrs.flatMap((cidr) => [
      ["-w", "-A", forwardChain, "-s", cidr, "-j", "ACCEPT"],
      [
        "-w",
        "-A",
        forwardChain,
        "-d",
        cidr,
        "-m",
        "conntrack",
        "--ctstate",
        "RELATED,ESTABLISHED",
        "-j",
        "ACCEPT",
      ],
    ]),
    ...natBypassCidrs.map((cidr) => [
      "-w",
      "-t",
      "nat",
      "-A",
      natChain,
      "-d",
      cidr,
      "-j",
      "RETURN",
    ]),
    ...cidrs.map((cidr) => [
      "-w",
      "-t",
      "nat",
      "-A",
      natChain,
      "-s",
      cidr,
      "-j",
      "MASQUERADE",
    ]),
  ];
}

export function syncTproxySourceBypass(profiles, options = {}) {
  if (settings.appMode !== "gateway") {
    return { success: true, skipped: true, cidrs: [] };
  }

  const cidrs = sourceBypassCidrs(
    profiles,
    options.envCidrs ?? process.env.TPROXY_BYPASS_SOURCE_CIDRS,
  );
  const commands = buildSourceBypassIptablesCommands(cidrs, options);

  for (const args of commands) {
    const result = spawnSync("iptables", args, {
      encoding: "utf8",
      timeout: 1000,
    });
    if (result.error || result.status !== 0) {
      return {
        success: false,
        cidrs,
        error:
          result.error?.message ||
          (result.stderr || result.stdout || "iptables command failed").trim(),
      };
    }
  }

  return { success: true, cidrs };
}
