import { describe, it, expect } from "vitest";
import { mapTypeToZodSchema, mapTypeToValidationSchema, appendMeta } from "./typeToZodSchema.js";
describe("mapTypeToZodSchema", () => {
    it("should return the alias name directly for typeAliasVariable", () => {
        const variableType = {
            type: "typeAliasVariable",
            aliasName: "MathResult",
        };
        const typeAliases = {
            MathResult: {
                type: "objectType",
                properties: [{ key: "answer", value: { type: "primitiveType", value: "number" } }],
            },
        };
        const result = mapTypeToZodSchema(variableType, typeAliases);
        expect(result).toBe("MathResult");
    });
    it("should return the alias name in nested contexts", () => {
        const variableType = {
            type: "objectType",
            properties: [
                { key: "result", value: { type: "typeAliasVariable", aliasName: "Coords" } },
            ],
        };
        const typeAliases = {
            Coords: {
                type: "objectType",
                properties: [
                    { key: "x", value: { type: "primitiveType", value: "number" } },
                    { key: "y", value: { type: "primitiveType", value: "number" } },
                ],
            },
        };
        const result = mapTypeToZodSchema(variableType, typeAliases);
        expect(result).toBe(`z.object({ "result": Coords })`);
    });
    it("should return the alias name inside an array type", () => {
        const variableType = {
            type: "arrayType",
            elementType: { type: "typeAliasVariable", aliasName: "Item" },
        };
        const typeAliases = {
            Item: {
                type: "objectType",
                properties: [{ key: "name", value: { type: "primitiveType", value: "string" } }],
            },
        };
        const result = mapTypeToZodSchema(variableType, typeAliases);
        expect(result).toBe("z.array(Item)");
    });
    it("should return the alias name inside a union type", () => {
        const variableType = {
            type: "unionType",
            types: [
                { type: "typeAliasVariable", aliasName: "Foo" },
                { type: "primitiveType", value: "number" },
            ],
        };
        const typeAliases = {
            Foo: { type: "primitiveType", value: "string" },
        };
        const result = mapTypeToZodSchema(variableType, typeAliases);
        expect(result).toBe("z.union([Foo, z.number()])");
    });
});
describe("mapTypeToValidationSchema", () => {
    it("should return the alias name directly for typeAliasVariable", () => {
        const variableType = {
            type: "typeAliasVariable",
            aliasName: "Category",
        };
        const typeAliases = {
            Category: {
                type: "unionType",
                types: [
                    { type: "stringLiteralType", value: "bug" },
                    { type: "stringLiteralType", value: "feature" },
                ],
            },
        };
        const result = mapTypeToValidationSchema(variableType, typeAliases);
        expect(result).toBe("Category");
    });
    it("maps the regex primitive to z.instanceof(RegExp)", () => {
        const result = mapTypeToValidationSchema({ type: "primitiveType", value: "regex" }, {});
        expect(result).toBe("z.instanceof(RegExp)");
    });
    it("maps regex inside an object property", () => {
        const result = mapTypeToValidationSchema({
            type: "objectType",
            properties: [{ key: "pattern", value: { type: "primitiveType", value: "regex" } }],
        }, {});
        expect(result).toBe(`z.object({ "pattern": z.instanceof(RegExp) })`);
    });
});
describe("mapTypeToZodSchema: Record<K, V>", () => {
    it("emits z.record for Record<string, number>", () => {
        const result = mapTypeToZodSchema({
            type: "genericType",
            name: "Record",
            typeArgs: [
                { type: "primitiveType", value: "string" },
                { type: "primitiveType", value: "number" },
            ],
        }, {});
        expect(result).toBe("z.record(z.string(), z.number())");
    });
    it("emits z.record nested inside z.array", () => {
        const result = mapTypeToZodSchema({
            type: "arrayType",
            elementType: {
                type: "genericType",
                name: "Record",
                typeArgs: [
                    { type: "primitiveType", value: "string" },
                    { type: "primitiveType", value: "string" },
                ],
            },
        }, {});
        expect(result).toBe("z.array(z.record(z.string(), z.string()))");
    });
    it("emits nested z.record for Record<string, Record<string, number>>", () => {
        const result = mapTypeToZodSchema({
            type: "genericType",
            name: "Record",
            typeArgs: [
                { type: "primitiveType", value: "string" },
                {
                    type: "genericType",
                    name: "Record",
                    typeArgs: [
                        { type: "primitiveType", value: "string" },
                        { type: "primitiveType", value: "number" },
                    ],
                },
            ],
        }, {});
        expect(result).toBe("z.record(z.string(), z.record(z.string(), z.number()))");
    });
    it("emits doubly-nested z.record for Record<string, Record<string, Record<string, boolean>>>", () => {
        const result = mapTypeToZodSchema({
            type: "genericType",
            name: "Record",
            typeArgs: [
                { type: "primitiveType", value: "string" },
                {
                    type: "genericType",
                    name: "Record",
                    typeArgs: [
                        { type: "primitiveType", value: "string" },
                        {
                            type: "genericType",
                            name: "Record",
                            typeArgs: [
                                { type: "primitiveType", value: "string" },
                                { type: "primitiveType", value: "boolean" },
                            ],
                        },
                    ],
                },
            ],
        }, {});
        expect(result).toBe("z.record(z.string(), z.record(z.string(), z.record(z.string(), z.boolean())))");
    });
    it("throws on unresolved generic types other than Record", () => {
        expect(() => mapTypeToZodSchema({
            type: "genericType",
            name: "Container",
            typeArgs: [{ type: "primitiveType", value: "string" }],
        }, {})).toThrow(/Unresolved generic type at codegen: Container/);
    });
});
describe("appendMeta", () => {
    const obj = (entries) => ({
        type: "agencyObject",
        entries: Object.entries(entries).map(([key, value]) => ({
            key,
            value: value,
        })),
    });
    const stringLit = (s) => ({
        type: "string",
        segments: [{ type: "text", value: s }],
    });
    const tag = (name, args) => ({ type: "tag", name, arguments: args });
    it("returns the schema unchanged when no @jsonSchema tag is present", () => {
        expect(appendMeta("z.string()", undefined)).toBe("z.string()");
        expect(appendMeta("z.string()", [])).toBe("z.string()");
        expect(appendMeta("z.string()", [tag("validate", [])])).toBe("z.string()");
    });
    it("appends .meta(...) when a single @jsonSchema tag is present", () => {
        const tags = [tag("jsonSchema", [obj({ format: stringLit("email") })])];
        expect(appendMeta("z.string()", tags)).toContain(".meta(");
        expect(appendMeta("z.string()", tags)).toMatch(/format:\s*"email"/);
    });
    it("merges stacked @jsonSchema tags into a single .meta(...) call", () => {
        const tags = [
            tag("jsonSchema", [obj({ format: stringLit("email") })]),
            tag("jsonSchema", [obj({ description: stringLit("user email") })]),
        ];
        const out = appendMeta("z.string()", tags);
        expect(out).toMatch(/format:\s*"email"/);
        expect(out).toMatch(/description:\s*"user email"/);
    });
    it("merges multiple object-literal arguments inside a single @jsonSchema tag", () => {
        const tags = [
            tag("jsonSchema", [
                obj({ format: stringLit("email") }),
                obj({ description: stringLit("user email") }),
            ]),
        ];
        const out = appendMeta("z.string()", tags);
        expect(out).toMatch(/format:\s*"email"/);
        expect(out).toMatch(/description:\s*"user email"/);
    });
});
