import type { Kysely } from "kysely";
import { describe, it, expect, beforeEach, afterEach } from "vitest";

import {
	handleContentCreate,
	handleContentDuplicate,
	handleContentGet,
	handleContentList,
	handleContentUpdate,
} from "../../../src/api/index.js";
import { BylineRepository } from "../../../src/database/repositories/byline.js";
import type { Database } from "../../../src/database/types.js";
import { SchemaRegistry } from "../../../src/schema/registry.js";
import { setupTestDatabaseWithCollections, teardownTestDatabase } from "../../utils/test-db.js";

describe("Content Handlers — auto-slug generation", () => {
	let db: Kysely<Database>;

	beforeEach(async () => {
		db = await setupTestDatabaseWithCollections();
		// Add a "name" field to the page collection so we can test name-based slug generation
		const registry = new SchemaRegistry(db);
		await registry.createField("page", { slug: "name", label: "Name", type: "string" });
	});

	afterEach(async () => {
		await teardownTestDatabase(db);
	});

	describe("handleContentCreate", () => {
		it("should auto-generate slug from title when slug is omitted", async () => {
			const result = await handleContentCreate(db, "post", {
				data: { title: "Hello World" },
			});

			expect(result.success).toBe(true);
			expect(result.data?.item.slug).toBe("hello-world");
		});

		it("should auto-generate slug from name when title is absent", async () => {
			const result = await handleContentCreate(db, "page", {
				data: { name: "My Widget" },
			});

			expect(result.success).toBe(true);
			expect(result.data?.item.slug).toBe("my-widget");
		});

		it("should prefer title over name for slug generation", async () => {
			const result = await handleContentCreate(db, "page", {
				data: { title: "From Title", name: "From Name" },
			});

			expect(result.success).toBe(true);
			expect(result.data?.item.slug).toBe("from-title");
		});

		it("should respect explicit slug and not auto-generate", async () => {
			const result = await handleContentCreate(db, "post", {
				data: { title: "Hello World" },
				slug: "custom-slug",
			});

			expect(result.success).toBe(true);
			expect(result.data?.item.slug).toBe("custom-slug");
		});

		it("should handle slug collisions by appending numeric suffix", async () => {
			// Create first item with the slug
			await handleContentCreate(db, "post", {
				data: { title: "Hello World" },
			});

			// Create second item with same title — should get unique slug
			const result = await handleContentCreate(db, "post", {
				data: { title: "Hello World" },
			});

			expect(result.success).toBe(true);
			expect(result.data?.item.slug).toBe("hello-world-1");
		});

		it("should increment suffix on repeated collisions", async () => {
			await handleContentCreate(db, "post", {
				data: { title: "Hello World" },
			});
			await handleContentCreate(db, "post", {
				data: { title: "Hello World" },
			});

			const result = await handleContentCreate(db, "post", {
				data: { title: "Hello World" },
			});

			expect(result.success).toBe(true);
			expect(result.data?.item.slug).toBe("hello-world-2");
		});

		it("should leave slug null when no title or name is present", async () => {
			const result = await handleContentCreate(db, "post", {
				data: { content: [{ _type: "block", children: [{ _type: "span", text: "hi" }] }] },
			});

			expect(result.success).toBe(true);
			expect(result.data?.item.slug).toBeNull();
		});

		it("should leave slug null when title is not a string", async () => {
			const result = await handleContentCreate(db, "post", {
				data: { title: 42 },
			});

			expect(result.success).toBe(true);
			expect(result.data?.item.slug).toBeNull();
		});

		it("should leave slug null when title is empty string", async () => {
			const result = await handleContentCreate(db, "post", {
				data: { title: "" },
			});

			expect(result.success).toBe(true);
			expect(result.data?.item.slug).toBeNull();
		});

		it("should handle unicode titles", async () => {
			const result = await handleContentCreate(db, "post", {
				data: { title: "Café Naïve" },
			});

			expect(result.success).toBe(true);
			expect(result.data?.item.slug).toBe("cafe-naive");
		});

		it("should allow same auto-slug in different collections", async () => {
			const postResult = await handleContentCreate(db, "post", {
				data: { title: "About" },
			});
			const pageResult = await handleContentCreate(db, "page", {
				data: { title: "About" },
			});

			expect(postResult.success).toBe(true);
			expect(pageResult.success).toBe(true);
			expect(postResult.data?.item.slug).toBe("about");
			expect(pageResult.data?.item.slug).toBe("about");
		});
	});

	describe("handleContentDuplicate", () => {
		it("should generate slug from duplicated title", async () => {
			const original = await handleContentCreate(db, "post", {
				data: { title: "My Post" },
				slug: "my-post",
			});

			const result = await handleContentDuplicate(db, "post", original.data!.item.id);

			expect(result.success).toBe(true);
			// Title becomes "My Post (Copy)", slug should be generated from it
			expect(result.data?.item.slug).toBe("my-post-copy");
		});

		it("should handle duplicate slug collision from copy", async () => {
			const original = await handleContentCreate(db, "post", {
				data: { title: "My Post" },
				slug: "my-post",
			});

			// First duplicate
			const dup1 = await handleContentDuplicate(db, "post", original.data!.item.id);
			expect(dup1.data?.item.slug).toBe("my-post-copy");

			// Second duplicate — "My Post (Copy)" title slugifies to "my-post-copy"
			// which now collides with the first duplicate
			const dup2 = await handleContentDuplicate(db, "post", original.data!.item.id);
			expect(dup2.success).toBe(true);
			expect(dup2.data?.item.slug).toBe("my-post-copy-1");
		});
	});

	describe("byline hydration and assignment", () => {
		it("should assign and return bylines on create", async () => {
			const bylineRepo = new BylineRepository(db);
			const byline = await bylineRepo.create({
				slug: "author-one",
				displayName: "Author One",
			});

			const created = await handleContentCreate(db, "post", {
				data: { title: "Bylined" },
				bylines: [{ bylineId: byline.id, roleLabel: "Writer" }],
			});

			expect(created.success).toBe(true);
			expect(created.data?.item.primaryBylineId).toBe(byline.id);
			expect(created.data?.item.byline?.id).toBe(byline.id);
			expect(created.data?.item.bylines).toHaveLength(1);
			expect(created.data?.item.bylines?.[0]?.roleLabel).toBe("Writer");
		});

		it("should return bylines on get and list", async () => {
			const bylineRepo = new BylineRepository(db);
			const first = await bylineRepo.create({ slug: "first", displayName: "First" });
			const second = await bylineRepo.create({ slug: "second", displayName: "Second" });

			const created = await handleContentCreate(db, "post", {
				data: { title: "Order Test" },
				bylines: [{ bylineId: second.id }, { bylineId: first.id }],
			});
			expect(created.success).toBe(true);
			const contentId = created.data!.item.id;

			const fetched = await handleContentGet(db, "post", contentId);
			expect(fetched.success).toBe(true);
			expect(fetched.data?.item.bylines?.[0]?.byline.id).toBe(second.id);
			expect(fetched.data?.item.bylines?.[1]?.byline.id).toBe(first.id);
			expect(fetched.data?.item.byline?.id).toBe(second.id);

			const listed = await handleContentList(db, "post", {});
			expect(listed.success).toBe(true);
			const listedItem = listed.data?.items.find((item) => item.id === contentId);
			expect(listedItem?.byline?.id).toBe(second.id);
			expect(listedItem?.bylines?.[0]?.byline.id).toBe(second.id);
		});

		it("should update byline ordering on update", async () => {
			const bylineRepo = new BylineRepository(db);
			const first = await bylineRepo.create({ slug: "first-upd", displayName: "First" });
			const second = await bylineRepo.create({ slug: "second-upd", displayName: "Second" });

			const created = await handleContentCreate(db, "post", {
				data: { title: "Update Bylines" },
				bylines: [{ bylineId: first.id }, { bylineId: second.id }],
			});
			expect(created.success).toBe(true);

			const updated = await handleContentUpdate(db, "post", created.data!.item.id, {
				bylines: [{ bylineId: second.id }, { bylineId: first.id }],
			});

			expect(updated.success).toBe(true);
			expect(updated.data?.item.primaryBylineId).toBe(second.id);
			expect(updated.data?.item.bylines?.[0]?.byline.id).toBe(second.id);
			expect(updated.data?.item.bylines?.[1]?.byline.id).toBe(first.id);
		});

		it("should copy bylines when duplicating", async () => {
			const bylineRepo = new BylineRepository(db);
			const byline = await bylineRepo.create({
				slug: "dup-author",
				displayName: "Dup Author",
			});

			const original = await handleContentCreate(db, "post", {
				data: { title: "Duplicate With Bylines" },
				bylines: [{ bylineId: byline.id }],
			});
			expect(original.success).toBe(true);

			const duplicated = await handleContentDuplicate(db, "post", original.data!.item.id);
			expect(duplicated.success).toBe(true);
			expect(duplicated.data?.item.byline?.id).toBe(byline.id);
			expect(duplicated.data?.item.bylines).toHaveLength(1);
		});
	});
});
