import { describe, expect, it } from "vitest";
import {
  classifyNetworkQuery,
  parseAsn,
  parseIpAddress,
  parsePrefix,
} from "./vrpQuery";

describe("parseAsn", () => {
  it("accepts bare and AS-prefixed numbers", () => {
    expect(parseAsn("45090")).toBe(45090);
    expect(parseAsn("AS45090")).toBe(45090);
    expect(parseAsn("as0")).toBe(0);
    expect(parseAsn("  AS64496  ")).toBe(64496);
  });

  it("rejects out-of-range and malformed ASNs", () => {
    expect(parseAsn("4294967295")).toBe(4294967295);
    expect(parseAsn("4294967296")).toBeNull();
    expect(parseAsn("AS123abc")).toBeNull();
    expect(parseAsn("AS")).toBeNull();
    expect(parseAsn("-1")).toBeNull();
  });
});

describe("parseIpAddress", () => {
  it("accepts IPv4 addresses", () => {
    expect(parseIpAddress("81.68.1.1")).toBe("81.68.1.1");
    expect(parseIpAddress("0.0.0.0")).toBe("0.0.0.0");
    expect(parseIpAddress("255.255.255.255")).toBe("255.255.255.255");
  });

  it("rejects IPv4 with bad octets or CIDR suffix", () => {
    expect(parseIpAddress("256.0.0.1")).toBeNull();
    expect(parseIpAddress("192.0.2.1/24")).toBeNull();
    expect(parseIpAddress("192.0.2")).toBeNull();
  });

  it("accepts IPv6 addresses", () => {
    expect(parseIpAddress("2001:7fa:0:1::1")).toBe("2001:7fa:0:1::1");
    expect(parseIpAddress("::")).toBe("::");
    expect(parseIpAddress("::1")).toBe("::1");
    expect(parseIpAddress("2001:db8:0:0:0:0:0:1")).toBe("2001:db8:0:0:0:0:0:1");
    expect(parseIpAddress("1:2:3:4:5:6:7::")).toBe("1:2:3:4:5:6:7::"); // "::" = one :0 group
    expect(parseIpAddress("::ffff:192.0.2.1")).toBe("::ffff:192.0.2.1");
  });

  it("rejects malformed IPv6", () => {
    expect(parseIpAddress("2001:::1")).toBeNull();
    expect(parseIpAddress("2001:db8::g1")).toBeNull();
    expect(parseIpAddress("1:2:3:4:5:6:7:8:9")).toBeNull();
    expect(parseIpAddress("1:2:3:4:5:6:7:8::")).toBeNull(); // no room for "::"
    expect(parseIpAddress("12345::")).toBeNull();
  });
});

describe("parsePrefix", () => {
  it("accepts IPv4 and IPv6 CIDR", () => {
    expect(parsePrefix("192.0.2.0/24")).toBe("192.0.2.0/24");
    expect(parsePrefix("192.0.2.1/24")).toBe("192.0.2.1/24"); // host bits: backend normalizes
    expect(parsePrefix("0.0.0.0/0")).toBe("0.0.0.0/0");
    expect(parsePrefix("2001:db8::/32")).toBe("2001:db8::/32");
    expect(parsePrefix("2001:db8::1/128")).toBe("2001:db8::1/128");
  });

  it("rejects bad lengths and addresses", () => {
    expect(parsePrefix("192.0.2.0/33")).toBeNull();
    expect(parsePrefix("2001:db8::/129")).toBeNull();
    expect(parsePrefix("192.0.2.0/")).toBeNull();
    expect(parsePrefix("192.0.2.0/24/1")).toBeNull();
    expect(parsePrefix("999.0.0.0/8")).toBeNull();
    expect(parsePrefix("/24")).toBeNull();
  });
});

describe("classifyNetworkQuery", () => {
  it("prefix wins over ip, ip over asn", () => {
    expect(classifyNetworkQuery("81.68.0.0/14")).toEqual({
      kind: "prefix",
      prefix: "81.68.0.0/14",
    });
    expect(classifyNetworkQuery("81.68.1.1")).toEqual({ kind: "ip", ip: "81.68.1.1" });
    expect(classifyNetworkQuery("2001:7fa:0:1::1")).toEqual({
      kind: "ip",
      ip: "2001:7fa:0:1::1",
    });
    expect(classifyNetworkQuery("AS45090")).toEqual({ kind: "asn", asn: 45090 });
    expect(classifyNetworkQuery("45090")).toEqual({ kind: "asn", asn: 45090 });
  });

  it("falls back to none for text queries", () => {
    expect(classifyNetworkQuery("repo-a.example")).toEqual({ kind: "none" });
    expect(classifyNetworkQuery("rsync://repo.example/ca/roa.roa")).toEqual({ kind: "none" });
    expect(classifyNetworkQuery("d1488eb0e0faabba1fbe8236")).toEqual({ kind: "none" });
    expect(classifyNetworkQuery("")).toEqual({ kind: "none" });
  });

  it("reads two tokens as network + ASN in either order", () => {
    expect(classifyNetworkQuery("1.0.0.1 AS13335")).toEqual({
      kind: "ip",
      ip: "1.0.0.1",
      asn: 13335,
    });
    expect(classifyNetworkQuery("AS13335 1.0.0.1")).toEqual({
      kind: "ip",
      ip: "1.0.0.1",
      asn: 13335,
    });
    expect(classifyNetworkQuery("192.0.2.0/24 13335")).toEqual({
      kind: "prefix",
      prefix: "192.0.2.0/24",
      asn: 13335,
    });
    expect(classifyNetworkQuery("  2001:db8::/32   as64496 ")).toEqual({
      kind: "prefix",
      prefix: "2001:db8::/32",
      asn: 64496,
    });
  });

  it("rejects multi-token queries without a network+ASN pair", () => {
    expect(classifyNetworkQuery("13335 64496")).toEqual({ kind: "none" });
    expect(classifyNetworkQuery("1.0.0.1 192.0.2.1")).toEqual({ kind: "none" });
    expect(classifyNetworkQuery("1.0.0.1 AS13335 extra")).toEqual({ kind: "none" });
    expect(classifyNetworkQuery("foo bar")).toEqual({ kind: "none" });
  });
});
