import { z } from "astro/zod";
import { describe, it, expect } from "vitest";

import {
	text,
	textarea,
	number,
	boolean as booleanField,
	select,
	multiSelect,
	datetime,
	slug,
	image,
	file,
	reference,
	json,
	richText,
	portableText,
} from "../../../src/fields/index.js";

// Test regex patterns
const UPPERCASE_PATTERN_REGEX = /^[A-Z]+$/;
const SLUG_UPPERCASE_PATTERN_REGEX = /^[A-Z_]+$/;

describe("Field Types", () => {
	describe("text", () => {
		it("should create basic text field", () => {
			const field = text();
			expect(field.type).toBe("text");
			expect(field.schema).toBeDefined();
			expect(field.ui?.widget).toBe("text");
		});

		it("should validate required text", () => {
			const field = text({ required: true });
			expect(() => field.schema.parse("hello")).not.toThrow();
			expect(() => field.schema.parse(undefined)).toThrow();
		});

		it("should validate optional text", () => {
			const field = text({ required: false });
			expect(() => field.schema.parse("hello")).not.toThrow();
			expect(() => field.schema.parse(undefined)).not.toThrow();
		});

		it("should enforce minLength", () => {
			const field = text({ minLength: 5 });
			expect(() => field.schema.parse("hello")).not.toThrow();
			expect(() => field.schema.parse("hi")).toThrow();
		});

		it("should enforce maxLength", () => {
			const field = text({ maxLength: 10 });
			expect(() => field.schema.parse("hello")).not.toThrow();
			expect(() => field.schema.parse("hello world!")).toThrow();
		});

		it("should enforce pattern", () => {
			const field = text({ pattern: UPPERCASE_PATTERN_REGEX });
			expect(() => field.schema.parse("HELLO")).not.toThrow();
			expect(() => field.schema.parse("hello")).toThrow();
		});
	});

	describe("textarea", () => {
		it("should create textarea field", () => {
			const field = textarea();
			expect(field.type).toBe("textarea");
			expect(field.ui?.widget).toBe("textarea");
			expect(field.ui?.rows).toBe(6);
		});

		it("should accept custom rows", () => {
			const field = textarea({ rows: 10 });
			expect(field.ui?.rows).toBe(10);
		});

		it("should enforce length constraints", () => {
			const field = textarea({ minLength: 10, maxLength: 100 });
			expect(() => field.schema.parse("a".repeat(50))).not.toThrow();
			expect(() => field.schema.parse("short")).toThrow();
			expect(() => field.schema.parse("a".repeat(200))).toThrow();
		});
	});

	describe("number", () => {
		it("should create number field", () => {
			const field = number();
			expect(field.type).toBe("number");
			expect(field.ui?.widget).toBe("number");
		});

		it("should validate numbers", () => {
			const field = number({ required: true });
			expect(() => field.schema.parse(42)).not.toThrow();
			expect(() => field.schema.parse(3.14)).not.toThrow();
			expect(() => field.schema.parse("42")).toThrow();
		});

		it("should enforce integer constraint", () => {
			const field = number({ integer: true });
			expect(() => field.schema.parse(42)).not.toThrow();
			expect(() => field.schema.parse(3.14)).toThrow();
		});

		it("should enforce min/max", () => {
			const field = number({ min: 0, max: 100 });
			expect(() => field.schema.parse(50)).not.toThrow();
			expect(() => field.schema.parse(-1)).toThrow();
			expect(() => field.schema.parse(101)).toThrow();
		});
	});

	describe("boolean", () => {
		it("should create boolean field", () => {
			const field = booleanField();
			expect(field.type).toBe("boolean");
			expect(field.ui?.widget).toBe("boolean");
		});

		it("should validate booleans", () => {
			const field = booleanField();
			expect(() => field.schema.parse(true)).not.toThrow();
			expect(() => field.schema.parse(false)).not.toThrow();
			expect(() => field.schema.parse("true")).toThrow();
		});

		it("should apply default value", () => {
			const field = booleanField({ default: true });
			const result = field.schema.parse(undefined);
			expect(result).toBe(true);
		});
	});

	describe("select", () => {
		it("should create select field", () => {
			const field = select({ options: ["one", "two", "three"] as const });
			expect(field.type).toBe("select");
			expect(field.ui?.widget).toBe("select");
		});

		it("should validate enum values", () => {
			const field = select({
				options: ["red", "green", "blue"] as const,
				required: true,
			});
			expect(() => field.schema.parse("red")).not.toThrow();
			expect(() => field.schema.parse("yellow")).toThrow();
		});

		it("should apply default value", () => {
			const field = select({
				options: ["small", "medium", "large"] as const,
				default: "medium",
			});
			const result = field.schema.parse(undefined);
			expect(result).toBe("medium");
		});
	});

	describe("multiSelect", () => {
		it("should create multiSelect field", () => {
			const field = multiSelect({ options: ["a", "b", "c"] as const });
			expect(field.type).toBe("multiSelect");
			expect(field.ui?.widget).toBe("multiSelect");
		});

		it("should validate array of enum values", () => {
			const field = multiSelect({
				options: ["tag1", "tag2", "tag3"] as const,
				required: true,
			});
			expect(() => field.schema.parse(["tag1", "tag2"])).not.toThrow();
			expect(() => field.schema.parse(["tag1", "invalid"])).toThrow();
		});

		it("should enforce min/max selections", () => {
			const field = multiSelect({
				options: ["a", "b", "c", "d"] as const,
				min: 1,
				max: 3,
			});
			expect(() => field.schema.parse(["a", "b"])).not.toThrow();
			expect(() => field.schema.parse([])).toThrow();
			expect(() => field.schema.parse(["a", "b", "c", "d"])).toThrow();
		});
	});

	describe("datetime", () => {
		it("should create datetime field", () => {
			const field = datetime();
			expect(field.type).toBe("datetime");
			expect(field.ui?.widget).toBe("datetime");
		});

		it("should validate dates", () => {
			const field = datetime({ required: true });
			expect(() => field.schema.parse(new Date())).not.toThrow();
			expect(() => field.schema.parse("2024-01-01")).toThrow();
		});

		it("should enforce min/max dates", () => {
			const min = new Date("2024-01-01");
			const max = new Date("2024-12-31");
			const field = datetime({ min, max });

			expect(() => field.schema.parse(new Date("2024-06-15"))).not.toThrow();
			expect(() => field.schema.parse(new Date("2023-12-31"))).toThrow();
			expect(() => field.schema.parse(new Date("2025-01-01"))).toThrow();
		});
	});

	describe("slug", () => {
		it("should create slug field", () => {
			const field = slug();
			expect(field.type).toBe("slug");
			expect(field.ui?.widget).toBe("slug");
		});

		it("should validate slug format", () => {
			const field = slug({ required: true });
			expect(() => field.schema.parse("hello-world")).not.toThrow();
			expect(() => field.schema.parse("hello-world-123")).not.toThrow();
			expect(() => field.schema.parse("Hello World")).toThrow();
			expect(() => field.schema.parse("hello_world")).toThrow();
		});

		it("should accept custom pattern", () => {
			const field = slug({ pattern: SLUG_UPPERCASE_PATTERN_REGEX });
			expect(() => field.schema.parse("HELLO_WORLD")).not.toThrow();
			expect(() => field.schema.parse("hello-world")).toThrow();
		});
	});

	describe("image", () => {
		it("should create image field", () => {
			const field = image();
			expect(field.type).toBe("image");
			expect(field.ui?.widget).toBe("image");
		});

		it("should validate image value structure", () => {
			const field = image({ required: true });
			const validImage = {
				id: "img-123",
				src: "https://example.com/photo.jpg",
				alt: "A photo",
				width: 1920,
				height: 1080,
			};
			expect(() => field.schema.parse(validImage)).not.toThrow();
		});
	});

	describe("file", () => {
		it("should create file field", () => {
			const field = file();
			expect(field.type).toBe("file");
			expect(field.ui?.widget).toBe("file");
		});

		it("should validate file value structure", () => {
			const field = file({ required: true });
			const validFile = {
				id: "file-123",
				url: "https://example.com/doc.pdf",
				filename: "doc.pdf",
				mimeType: "application/pdf",
				size: 1024000,
			};
			expect(() => field.schema.parse(validFile)).not.toThrow();
		});
	});

	describe("reference", () => {
		it("should create reference field", () => {
			const field = reference({ to: "posts" });
			expect(field.type).toBe("reference");
			expect(field.ui?.widget).toBe("reference");
		});

		it("should validate string ID", () => {
			const field = reference({ to: "posts", required: true });
			expect(() => field.schema.parse("post-123")).not.toThrow();
			expect(() => field.schema.parse(123)).toThrow();
		});
	});

	describe("json", () => {
		it("should create json field", () => {
			const field = json();
			expect(field.type).toBe("json");
			expect(field.ui?.widget).toBe("json");
		});

		it("should accept any JSON data", () => {
			const field = json();
			expect(() => field.schema.parse({ foo: "bar" })).not.toThrow();
			expect(() => field.schema.parse([1, 2, 3])).not.toThrow();
			expect(() => field.schema.parse("string")).not.toThrow();
		});

		it("should validate with custom schema", () => {
			const customSchema = z.object({
				name: z.string(),
				age: z.number(),
			});

			const field = json({ schema: customSchema });
			expect(() => field.schema.parse({ name: "John", age: 30 })).not.toThrow();
			expect(() => field.schema.parse({ name: "John" })).toThrow();
		});
	});

	describe("richText", () => {
		it("should create richText field", () => {
			const field = richText();
			expect(field.type).toBe("richText");
			expect(field.ui?.widget).toBe("richText");
		});

		it("should validate string content", () => {
			const field = richText({ required: true });
			expect(() => field.schema.parse("# Heading\n\nParagraph")).not.toThrow();
			expect(() => field.schema.parse(123)).toThrow();
		});
	});

	describe("portableText", () => {
		it("should create portableText field", () => {
			const field = portableText();
			expect(field.type).toBe("portableText");
			expect(field.ui?.widget).toBe("portableText");
		});

		it("should validate array of blocks", () => {
			const field = portableText({ required: true });
			const blocks = [
				{
					_type: "block",
					_key: "key1",
					children: [{ _type: "span", text: "Hello" }],
				},
			];
			expect(() => field.schema.parse(blocks)).not.toThrow();
		});
	});
});
