import { describe, expect, it } from "bun:test";

import type { CheckerValidationInput } from "../../../../../src/server/checker/runner/types";

import { validateDnsConfig } from "../../../../../src/server/checker/runner/dns/validate";

function makeInput(overrides: { targets?: Array<Record<string, unknown>> }): CheckerValidationInput {
  return { targets: (overrides.targets ?? []) as CheckerValidationInput["targets"] };
}

describe("validateDnsConfig", () => {
  it("接受合法的 system 目标（resolver/name/family）", () => {
    const issues = validateDnsConfig(
      makeInput({
        targets: [{ dns: { family: "ipv4", name: "example.com", resolver: "system" }, id: "t1", type: "dns" }],
      }),
    );
    expect(issues).toHaveLength(0);
  });

  it("接受合法的 server 目标（resolver/name/server/port/recordType）", () => {
    const issues = validateDnsConfig(
      makeInput({
        targets: [
          {
            dns: {
              name: "example.com",
              port: 53,
              protocol: "udp",
              recordType: "A",
              resolver: "server",
              server: "8.8.8.8",
            },
            id: "t2",
            type: "dns",
          },
        ],
      }),
    );
    expect(issues).toHaveLength(0);
  });

  it("拒绝缺少 dns 配置分组", () => {
    const issues = validateDnsConfig(makeInput({ targets: [{ id: "t3", type: "dns" }] }));
    expect(issues).toHaveLength(1);
    expect(issues[0]!.code).toBe("required");
    expect(issues[0]!.path).toContain("dns");
    expect(issues[0]!.message).toContain("dns");
  });

  it("拒绝缺少 dns.resolver", () => {
    const issues = validateDnsConfig(makeInput({ targets: [{ dns: { name: "example.com" }, id: "t4", type: "dns" }] }));
    expect(issues).toHaveLength(1);
    expect(issues[0]!.code).toBe("required");
    expect(issues[0]!.path).toContain("resolver");
  });

  it("拒绝无效的 dns.resolver 值", () => {
    const issues = validateDnsConfig(
      makeInput({ targets: [{ dns: { name: "example.com", resolver: "unknown" }, id: "t5", type: "dns" }] }),
    );
    expect(issues).toHaveLength(1);
    expect(issues[0]!.code).toBe("invalid-value");
    expect(issues[0]!.path).toContain("resolver");
    expect(issues[0]!.message).toContain("system");
    expect(issues[0]!.message).toContain("server");
  });

  it("拒绝缺少 dns.name", () => {
    const issues = validateDnsConfig(makeInput({ targets: [{ dns: { resolver: "system" }, id: "t6", type: "dns" }] }));
    expect(issues.length).toBeGreaterThanOrEqual(1);
    expect(issues.some((i) => i.code === "required" && i.path.includes("name"))).toBe(true);
  });

  it("System 模式：拒绝空白 dns.name", () => {
    const issues = validateDnsConfig(
      makeInput({ targets: [{ dns: { name: "  ", resolver: "system" }, id: "t7", type: "dns" }] }),
    );
    expect(issues.some((i) => i.code === "required" && i.path.includes("name"))).toBe(true);
  });

  it("System 模式：接受 family any/ipv4/ipv6，拒绝无效 family", () => {
    for (const family of ["any", "ipv4", "ipv6"]) {
      const issues = validateDnsConfig(
        makeInput({ targets: [{ dns: { family, name: "x.com", resolver: "system" }, id: "tf", type: "dns" }] }),
      );
      expect(issues).toHaveLength(0);
    }
    const issues = validateDnsConfig(
      makeInput({ targets: [{ dns: { family: "ipx", name: "x.com", resolver: "system" }, id: "tf", type: "dns" }] }),
    );
    expect(issues.some((i) => i.code === "invalid-value" && i.path.includes("family"))).toBe(true);
  });

  it("System 模式：拒绝 dns 中的未知字段", () => {
    const issues = validateDnsConfig(
      makeInput({
        targets: [{ dns: { bogus: true, name: "x.com", resolver: "system" }, id: "t8", type: "dns" }],
      }),
    );
    expect(issues.some((i) => i.code === "unknown-field" && i.path.includes("bogus"))).toBe(true);
  });

  it("System 模式：拒绝 server 专用字段（server/port/protocol/recordType）", () => {
    const issues = validateDnsConfig(
      makeInput({
        targets: [
          {
            dns: { name: "x.com", port: 53, protocol: "udp", recordType: "A", resolver: "system", server: "8.8.8.8" },
            id: "t9",
            type: "dns",
          },
        ],
      }),
    );
    expect(issues.some((i) => i.code === "unknown-field" && i.path.includes("server"))).toBe(true);
    expect(issues.some((i) => i.code === "unknown-field" && i.path.includes("port"))).toBe(true);
    expect(issues.some((i) => i.code === "unknown-field" && i.path.includes("protocol"))).toBe(true);
    expect(issues.some((i) => i.code === "unknown-field" && i.path.includes("recordType"))).toBe(true);
  });

  it("Server 模式：拒绝缺少 dns.server", () => {
    const issues = validateDnsConfig(
      makeInput({ targets: [{ dns: { name: "x.com", resolver: "server" }, id: "t10", type: "dns" }] }),
    );
    expect(issues.some((i) => i.code === "required" && i.path.includes("server"))).toBe(true);
  });

  it("Server 模式：拒绝无效 port（0、-1、65536、1.5）", () => {
    for (const port of [0, -1, 65536, 1.5]) {
      const issues = validateDnsConfig(
        makeInput({
          targets: [{ dns: { name: "x.com", port, resolver: "server", server: "8.8.8.8" }, id: "tp", type: "dns" }],
        }),
      );
      expect(issues.some((i) => i.code === "invalid-value" && i.path.includes("port"))).toBe(true);
    }
  });

  it("Server 模式：接受 protocol udp/tcp，拒绝无效值", () => {
    for (const protocol of ["udp", "tcp"]) {
      const issues = validateDnsConfig(
        makeInput({
          targets: [
            { dns: { name: "x.com", protocol, resolver: "server", server: "8.8.8.8" }, id: "tpr", type: "dns" },
          ],
        }),
      );
      expect(issues).toHaveLength(0);
    }
    const issues = validateDnsConfig(
      makeInput({
        targets: [
          { dns: { name: "x.com", protocol: "http", resolver: "server", server: "8.8.8.8" }, id: "tpr", type: "dns" },
        ],
      }),
    );
    expect(issues.some((i) => i.code === "invalid-value" && i.path.includes("protocol"))).toBe(true);
  });

  it("Server 模式：接受有效 recordType，拒绝无效 recordType", () => {
    const issues = validateDnsConfig(
      makeInput({
        targets: [
          { dns: { name: "x.com", recordType: "A", resolver: "server", server: "8.8.8.8" }, id: "trt", type: "dns" },
        ],
      }),
    );
    expect(issues).toHaveLength(0);

    const badIssues = validateDnsConfig(
      makeInput({
        targets: [
          { dns: { name: "x.com", recordType: "FAKE", resolver: "server", server: "8.8.8.8" }, id: "trt", type: "dns" },
        ],
      }),
    );
    expect(badIssues.some((i) => i.code === "invalid-value" && i.path.includes("recordType"))).toBe(true);
  });

  it("Server 模式：接受有效 maxResponseBytes（数字和字符串）", () => {
    const issues1 = validateDnsConfig(
      makeInput({
        targets: [
          {
            dns: { maxResponseBytes: 512, name: "x.com", resolver: "server", server: "8.8.8.8" },
            id: "tmr",
            type: "dns",
          },
        ],
      }),
    );
    expect(issues1).toHaveLength(0);

    const issues2 = validateDnsConfig(
      makeInput({
        targets: [
          {
            dns: { maxResponseBytes: "1KB", name: "x.com", resolver: "server", server: "8.8.8.8" },
            id: "tmr",
            type: "dns",
          },
        ],
      }),
    );
    expect(issues2).toHaveLength(0);
  });

  it("Server 模式：拒绝无效 maxResponseBytes 字符串", () => {
    const issues = validateDnsConfig(
      makeInput({
        targets: [
          {
            dns: { maxResponseBytes: "1kb", name: "x.com", resolver: "server", server: "8.8.8.8" },
            id: "tmr-invalid",
            type: "dns",
          },
        ],
      }),
    );
    expect(issues.some((i) => i.code === "invalid-value" && i.path.includes("maxResponseBytes"))).toBe(true);
  });

  it("Server 模式：拒绝 recursionDesired/tcpFallback 非布尔值", () => {
    const issues = validateDnsConfig(
      makeInput({
        targets: [
          {
            dns: {
              name: "x.com",
              recursionDesired: "false",
              resolver: "server",
              server: "8.8.8.8",
              tcpFallback: "true",
            },
            id: "tb-dns",
            type: "dns",
          },
        ],
      }),
    );
    expect(issues.some((i) => i.code === "invalid-type" && i.path.includes("recursionDesired"))).toBe(true);
    expect(issues.some((i) => i.code === "invalid-type" && i.path.includes("tcpFallback"))).toBe(true);
  });

  it("Server 模式：拒绝 dns 中的未知字段", () => {
    const issues = validateDnsConfig(
      makeInput({
        targets: [
          { dns: { name: "x.com", resolver: "server", server: "8.8.8.8", unknown: true }, id: "t16", type: "dns" },
        ],
      }),
    );
    expect(issues.some((i) => i.code === "unknown-field" && i.path.includes("unknown"))).toBe(true);
  });

  it("System 模式 expect：拒绝 rcode/ttlMin/ttlMax/answerCount/authoritative/recursionAvailable/truncated/authenticatedData/result/responded → dns-unsupported-expect", () => {
    const serverOnlyFields = [
      "rcode",
      "ttlMin",
      "ttlMax",
      "answerCount",
      "authoritative",
      "recursionAvailable",
      "truncated",
      "authenticatedData",
      "result",
      "responded",
    ];
    for (const field of serverOnlyFields) {
      const expectObj: Record<string, unknown> = {};
      expectObj[field] = field === "rcode" ? ["NOERROR"] : field === "result" ? ["ok"] : true;
      const issues = validateDnsConfig(
        makeInput({
          targets: [
            {
              dns: { name: "x.com", resolver: "system" },
              expect: expectObj,
              id: "tse",
              name: "sys-target",
              type: "dns",
            },
          ],
        }),
      );
      const matched = issues.find((i) => i.code === "dns-unsupported-expect" && i.path.includes(field));
      expect(matched).toBeDefined();
      expect(matched!.message).toContain("system");
    }
  });

  it("System 模式 expect：接受 values/valueCount/durationMs", () => {
    const issues = validateDnsConfig(
      makeInput({
        targets: [
          {
            dns: { name: "x.com", resolver: "system" },
            expect: { durationMs: { lte: 100 }, valueCount: { gte: 1 }, values: { exact: ["1.2.3.4"] } },
            id: "t18",
            type: "dns",
          },
        ],
      }),
    );
    expect(issues).toHaveLength(0);
  });

  it("Server 模式 expect：接受所有 server expect 字段", () => {
    const issues = validateDnsConfig(
      makeInput({
        targets: [
          {
            dns: { name: "x.com", resolver: "server", server: "8.8.8.8" },
            expect: {
              answerCount: { gte: 1 },
              authenticatedData: false,
              authoritative: false,
              durationMs: { lte: 100 },
              rcode: ["NOERROR"],
              recursionAvailable: true,
              responded: true,
              result: [{ contains: "ok" }],
              truncated: false,
              ttlMax: { lte: 3600 },
              ttlMin: { gte: 0 },
              valueCount: { gte: 1 },
              values: { exact: ["1.2.3.4"] },
            },
            id: "t19",
            type: "dns",
          },
        ],
      }),
    );
    expect(issues).toHaveLength(0);
  });

  it("Server 模式 expect：responded=false 时拒绝协议级响应断言", () => {
    const issues = validateDnsConfig(
      makeInput({
        targets: [
          {
            dns: { name: "x.com", resolver: "server", server: "8.8.8.8" },
            expect: { rcode: ["NOERROR"], responded: false },
            id: "trf",
            type: "dns",
          },
        ],
      }),
    );
    expect(issues.some((i) => i.code === "invalid-value" && i.path.includes("responded"))).toBe(true);
  });

  it("Server 模式 expect：拒绝未知 expect 字段", () => {
    const issues = validateDnsConfig(
      makeInput({
        targets: [
          {
            dns: { name: "x.com", resolver: "server", server: "8.8.8.8" },
            expect: { bogusField: 123 },
            id: "t20",
            type: "dns",
          },
        ],
      }),
    );
    expect(issues.some((i) => i.code === "unknown-field" && i.path.includes("bogusField"))).toBe(true);
  });

  it("验证 rcode 值必须为已知 RCODE", () => {
    const issues = validateDnsConfig(
      makeInput({
        targets: [
          {
            dns: { name: "x.com", resolver: "server", server: "8.8.8.8" },
            expect: { rcode: ["NOTAREALCODE"] },
            id: "trc",
            type: "dns",
          },
        ],
      }),
    );
    expect(issues.some((i) => i.code === "invalid-value" && i.path.includes("rcode"))).toBe(true);
  });

  it("验证布尔字段（responded/authoritative 等）", () => {
    const fields = ["responded", "authoritative", "recursionAvailable", "truncated", "authenticatedData"];
    for (const field of fields) {
      const expectObj: Record<string, unknown> = {};
      expectObj[field] = "notbool";
      const issues = validateDnsConfig(
        makeInput({
          targets: [
            {
              dns: { name: "x.com", resolver: "server", server: "8.8.8.8" },
              expect: expectObj,
              id: "tb",
              type: "dns",
            },
          ],
        }),
      );
      expect(issues.some((i) => i.code === "invalid-type" && i.path.includes(field))).toBe(true);
    }
  });

  it("验证 ValueExpectation 字段（durationMs/valueCount/answerCount/ttlMin/ttlMax）", () => {
    const issues = validateDnsConfig(
      makeInput({
        targets: [
          {
            dns: { name: "x.com", resolver: "server", server: "8.8.8.8" },
            expect: { answerCount: [4], durationMs: [1, 2], ttlMax: [6], ttlMin: [5], valueCount: [3] },
            id: "tve",
            type: "dns",
          },
        ],
      }),
    );
    expect(issues.some((i) => i.code === "invalid-type" && i.path.includes("durationMs"))).toBe(true);
    expect(issues.some((i) => i.code === "invalid-type" && i.path.includes("valueCount"))).toBe(true);
    expect(issues.some((i) => i.code === "invalid-type" && i.path.includes("answerCount"))).toBe(true);
    expect(issues.some((i) => i.code === "invalid-type" && i.path.includes("ttlMin"))).toBe(true);
    expect(issues.some((i) => i.code === "invalid-type" && i.path.includes("ttlMax"))).toBe(true);
  });

  it("验证 ContentExpectations 字段（result）", () => {
    const issues = validateDnsConfig(
      makeInput({
        targets: [
          {
            dns: { name: "x.com", resolver: "server", server: "8.8.8.8" },
            expect: { result: "not-array" },
            id: "tce",
            type: "dns",
          },
        ],
      }),
    );
    expect(issues.some((i) => i.code === "invalid-type" && i.path.includes("result"))).toBe(true);
  });

  it("验证 DnsValuesExpectation（exact/include/exclude 数组）", () => {
    const issues = validateDnsConfig(
      makeInput({
        targets: [
          {
            dns: { name: "x.com", resolver: "server", server: "8.8.8.8" },
            expect: { values: { exact: "not-array", exclude: true, include: 123, unknownKey: "x" } },
            id: "tdv",
            type: "dns",
          },
        ],
      }),
    );
    expect(issues.some((i) => i.code === "invalid-type" && i.path.includes("exact"))).toBe(true);
    expect(issues.some((i) => i.code === "invalid-type" && i.path.includes("include"))).toBe(true);
    expect(issues.some((i) => i.code === "invalid-type" && i.path.includes("exclude"))).toBe(true);
    expect(issues.some((i) => i.code === "unknown-field" && i.path.includes("unknownKey"))).toBe(true);
  });

  it("跳过非 dns 类型目标", () => {
    const issues = validateDnsConfig(
      makeInput({ targets: [{ http: { url: "http://example.com" }, id: "tother", type: "http" }] }),
    );
    expect(issues).toHaveLength(0);
  });

  it("跳过非对象目标", () => {
    const issues = validateDnsConfig(makeInput({ targets: ["not-an-object" as unknown as Record<string, unknown>] }));
    expect(issues).toHaveLength(0);
  });
});
