import { describe, it, expect } from "vitest";
import { CUSTOM_METHOD_CARD_WIZARD_MAX_DESCRIPTION_CHARS } from "../../lib/create/customMethodCardWizardConstants";
import {
  assertPlainJsonValue,
  DEFAULT_PLAIN_JSON_LIMITS,
} from "../../lib/server/validation/plainJson";
import {
  createFlowStateSchema,
  publishRuleBodySchema,
  putDraftBodySchema,
  uniqueStakeholderEmailsForPublish,
} from "../../lib/server/validation/createFlowSchemas";

describe("assertPlainJsonValue", () => {
  it("accepts plain JSON structures", () => {
    expect(
      assertPlainJsonValue(
        { a: [1, "x", { b: null }], c: true },
        0,
        DEFAULT_PLAIN_JSON_LIMITS,
      ),
    ).toBeNull();
  });

  it("rejects __proto__ keys", () => {
    const obj = JSON.parse('{"__proto__": {"x": 1}}') as Record<
      string,
      unknown
    >;
    expect(assertPlainJsonValue(obj, 0, DEFAULT_PLAIN_JSON_LIMITS)).toBe(
      "Unsafe object key",
    );
  });

  it("rejects non-finite numbers", () => {
    expect(assertPlainJsonValue(Number.NaN, 0, DEFAULT_PLAIN_JSON_LIMITS)).toBe(
      "Invalid number value",
    );
  });

  it("rejects excessive depth", () => {
    let v: unknown = 1;
    for (let i = 0; i < 50; i++) {
      v = { x: v };
    }
    expect(assertPlainJsonValue(v, 0, DEFAULT_PLAIN_JSON_LIMITS)).toBe(
      "Maximum nesting depth exceeded",
    );
  });
});

describe("createFlowStateSchema", () => {
  it("accepts empty object", () => {
    const r = createFlowStateSchema.safeParse({});
    expect(r.success).toBe(true);
  });

  it("accepts known fields and passthrough keys", () => {
    const r = createFlowStateSchema.safeParse({
      title: "My rule",
      currentStep: "communication-methods",
      customField: { nested: [1, 2] },
    });
    expect(r.success).toBe(true);
  });

  it("rejects invalid currentStep", () => {
    const r = createFlowStateSchema.safeParse({ currentStep: "not-a-step" });
    expect(r.success).toBe(false);
  });

  it("rejects title that is too long", () => {
    const r = createFlowStateSchema.safeParse({ title: "x".repeat(600) });
    expect(r.success).toBe(false);
  });

  it("rejects communitySaveEmail longer than 320 chars", () => {
    const r = createFlowStateSchema.safeParse({
      communitySaveEmail: "x".repeat(321),
    });
    expect(r.success).toBe(false);
  });

  it("rejects communityContext longer than 200 chars", () => {
    const r = createFlowStateSchema.safeParse({
      communityContext: "x".repeat(201),
    });
    expect(r.success).toBe(false);
  });

  it("accepts custom method card support text longer than 48 characters", () => {
    const r = createFlowStateSchema.safeParse({
      customMethodCardMetaById: {
        "00000000-0000-4000-8000-000000000001": {
          label: "Signal",
          supportText:
            "A decision is assumed approved unless objections are raised within a specified timeframe.",
        },
      },
    });
    expect(r.success).toBe(true);
  });

  it("rejects custom method card support text over the description max", () => {
    const r = createFlowStateSchema.safeParse({
      customMethodCardMetaById: {
        "00000000-0000-4000-8000-000000000001": {
          label: "Signal",
          supportText: "x".repeat(
            CUSTOM_METHOD_CARD_WIZARD_MAX_DESCRIPTION_CHARS + 1,
          ),
        },
      },
    });
    expect(r.success).toBe(false);
  });

  it("accepts communityStructureChipSnapshots with custom chip rows", () => {
    const r = createFlowStateSchema.safeParse({
      communityStructureChipSnapshots: {
        organizationTypes: [
          { id: "1", label: "Co-op", state: "selected" },
          { id: "custom-uuid", label: "My type", state: "selected" },
        ],
        scale: [{ id: "1", label: "Local" }],
        maturity: [],
      },
    });
    expect(r.success).toBe(true);
  });

  it("rejects invalid chip snapshot row types", () => {
    const r = createFlowStateSchema.safeParse({
      communityStructureChipSnapshots: {
        organizationTypes: [{ id: "1", label: 123 }],
      },
    });
    expect(r.success).toBe(false);
  });

  it("accepts coreValueDetailsByChipId", () => {
    const r = createFlowStateSchema.safeParse({
      coreValueDetailsByChipId: {
        "1": { meaning: "We care about access.", signals: "Blocking access." },
        "uuid-here": { meaning: "", signals: "" },
      },
    });
    expect(r.success).toBe(true);
  });

  it("accepts templateReviewBackSlug", () => {
    const r = createFlowStateSchema.safeParse({
      templateReviewBackSlug: "mutual-aid-mondays",
    });
    expect(r.success).toBe(true);
  });

  it("accepts customMethodCardFieldBlocksById", () => {
    const r = createFlowStateSchema.safeParse({
      customMethodCardFieldBlocksById: {
        "card-uuid": [
          {
            kind: "text",
            id: "f1",
            blockTitle: "Notes",
            placeholderText: "Optional",
          },
          {
            kind: "badges",
            id: "f2",
            blockTitle: "Tags",
            options: ["a", "b"],
          },
          {
            kind: "upload",
            id: "f3",
            blockTitle: "Attachment",
            fileName: "doc.pdf",
          },
          {
            kind: "proportion",
            id: "f4",
            blockTitle: "Share",
            defaultPercent: 50,
          },
        ],
      },
    });
    expect(r.success).toBe(true);
  });

  it("accepts templateReviewEntryFromCreateFlow", () => {
    const r = createFlowStateSchema.safeParse({
      templateReviewEntryFromCreateFlow: true,
    });
    expect(r.success).toBe(true);
  });

  it("rejects core value detail strings that are too long", () => {
    const r = createFlowStateSchema.safeParse({
      coreValueDetailsByChipId: {
        "1": { meaning: "x".repeat(8001), signals: "y" },
      },
    });
    expect(r.success).toBe(false);
  });

  it("accepts stakeholderEmails on draft payload", () => {
    const r = createFlowStateSchema.safeParse({
      stakeholderEmails: ["  one@example.com  "],
    });
    expect(r.success).toBe(true);
    if (r.success) {
      expect(r.data.stakeholderEmails).toEqual(["one@example.com"]);
    }
  });
});

describe("putDraftBodySchema", () => {
  it("requires payload object", () => {
    expect(putDraftBodySchema.safeParse({}).success).toBe(false);
    expect(putDraftBodySchema.safeParse({ payload: {} }).success).toBe(true);
  });
});

describe("publishRuleBodySchema", () => {
  it("accepts minimal valid body", () => {
    const r = publishRuleBodySchema.safeParse({
      title: "  Hello  ",
      document: { body: "text" },
    });
    expect(r.success).toBe(true);
    if (r.success) {
      expect(r.data.title).toBe("Hello");
      expect(r.data.summary).toBeNull();
    }
  });

  it("trims summary and maps empty to null", () => {
    const r = publishRuleBodySchema.safeParse({
      title: "T",
      summary: "   ",
      document: {},
    });
    expect(r.success).toBe(true);
    if (r.success) {
      expect(r.data.summary).toBeNull();
    }
  });

  it("rejects empty title", () => {
    const r = publishRuleBodySchema.safeParse({
      title: "   ",
      document: {},
    });
    expect(r.success).toBe(false);
  });

  it("rejects non-object document", () => {
    const r = publishRuleBodySchema.safeParse({
      title: "Ok",
      document: "nope",
    });
    expect(r.success).toBe(false);
  });

  it("normalizes stakeholderEmails", () => {
    const r = publishRuleBodySchema.safeParse({
      title: "Ok",
      document: {},
      stakeholderEmails: ["  A@Example.COM ", "b@example.com"],
    });
    expect(r.success).toBe(true);
    if (r.success) {
      expect(r.data.stakeholderEmails).toEqual(["a@example.com", "b@example.com"]);
    }
  });
});

describe("uniqueStakeholderEmailsForPublish", () => {
  it("dedupes and drops publisher email", () => {
    expect(
      uniqueStakeholderEmailsForPublish(
        ["a@b.c", "A@B.C", "x@y.z"],
        "a@b.c",
      ),
    ).toEqual(["x@y.z"]);
  });
});
