import assert from "node:assert/strict";
import test from "node:test";

const {
  deviceCidrs,
  normalizeDeviceProfiles,
} = await import("../../src/server/devices.js");
const { matchRoute } = await import("../../src/server/routeMatcher.js");
const {
  sourceBypassCidrs,
  buildSourceBypassIptablesCommands,
} = await import("../../src/server/tproxySourceBypass.js");
const { settings } = await import("../../src/server/config.js");

test("default source bypass chain name fits iptables chain length limit", () => {
  assert.equal(settings.tproxySourceBypassChain, "VPN_PROXY_SRC_BYPASS");
  assert.equal(settings.tproxySourceForwardChain, "VPN_PROXY_FWD_BYPASS");
  assert.equal(settings.tproxySourceNatChain, "VPN_PROXY_NAT_BYPASS");
  assert.ok(settings.tproxySourceBypassChain.length <= 28);
  assert.ok(settings.tproxySourceForwardChain.length <= 28);
  assert.ok(settings.tproxySourceNatChain.length <= 28);
});

test("device profiles preserve bypass mode for kernel-level TProxy bypass", () => {
  const profiles = normalizeDeviceProfiles({
    devices: [
      {
        id: "pc",
        name: "PC",
        enabled: true,
        ip: "192.168.50.25",
        mode: "bypass",
      },
    ],
  });

  assert.equal(profiles.devices[0].mode, "bypass");
  assert.deepEqual(deviceCidrs(profiles.devices, "bypass"), [
    "192.168.50.25/32",
  ]);
});

test("route checker reports transparent bypass before sing-box rules", () => {
  const result = matchRoute(
    {
      host: "example.com",
      ip: "93.184.216.34",
      sourceIp: "192.168.50.25",
      inbound: "tproxy-in",
    },
    [
      {
        id: "vpn-all",
        enabled: true,
        name: "VPN all",
        domains: ["example.com"],
        outbound: "vpn",
      },
    ],
    {
      vpnTag: "test-vpn",
      deviceProfiles: {
        defaultTransparentMode: "vpn",
        proxyDefaultMode: "vpn",
        devices: [
          {
            id: "pc",
            name: "PC",
            enabled: true,
            ip: "192.168.50.25",
            mode: "bypass",
          },
        ],
      },
    },
  );

  assert.equal(result.matched, "kernel-bypass");
  assert.equal(result.ruleName, "PC -> bypass TProxy");
  assert.equal(result.outbound, "direct");
});

test("source bypass sync combines env CIDRs and bypass-mode devices", () => {
  const cidrs = sourceBypassCidrs(
    {
      devices: [
        { enabled: true, ip: "192.168.50.25", mode: "bypass" },
        { enabled: false, ip: "192.168.50.26", mode: "bypass" },
        { enabled: true, ip: "192.168.50.27", mode: "direct" },
      ],
    },
    "192.168.50.30/32",
  );

  assert.deepEqual(cidrs, ["192.168.50.30/32", "192.168.50.25/32"]);
});

test("source bypass iptables commands use ACCEPT inside the managed subchain", () => {
  assert.deepEqual(
    buildSourceBypassIptablesCommands(["192.168.50.25/32"], {
      chain: "VPN_PROXY_SOURCE_BYPASS",
      forwardChain: "VPN_PROXY_FWD_BYPASS",
      natChain: "VPN_PROXY_NAT_BYPASS",
      natBypassCidrs: ["10.0.0.0/8"],
    }),
    [
      ["-w", "-t", "mangle", "-F", "VPN_PROXY_SOURCE_BYPASS"],
      ["-w", "-F", "VPN_PROXY_FWD_BYPASS"],
      ["-w", "-t", "nat", "-F", "VPN_PROXY_NAT_BYPASS"],
      [
        "-w",
        "-t",
        "mangle",
        "-A",
        "VPN_PROXY_SOURCE_BYPASS",
        "-s",
        "192.168.50.25/32",
        "-j",
        "ACCEPT",
      ],
      [
        "-w",
        "-A",
        "VPN_PROXY_FWD_BYPASS",
        "-s",
        "192.168.50.25/32",
        "-j",
        "ACCEPT",
      ],
      [
        "-w",
        "-A",
        "VPN_PROXY_FWD_BYPASS",
        "-d",
        "192.168.50.25/32",
        "-m",
        "conntrack",
        "--ctstate",
        "RELATED,ESTABLISHED",
        "-j",
        "ACCEPT",
      ],
      [
        "-w",
        "-t",
        "nat",
        "-A",
        "VPN_PROXY_NAT_BYPASS",
        "-d",
        "10.0.0.0/8",
        "-j",
        "RETURN",
      ],
      [
        "-w",
        "-t",
        "nat",
        "-A",
        "VPN_PROXY_NAT_BYPASS",
        "-s",
        "192.168.50.25/32",
        "-j",
        "MASQUERADE",
      ],
    ],
  );
});
