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

import {
	AttachmentType,
	AttachmentPayload,
	ContextSummaryPayload,
	ConversationEntryMetadata,
	GenericObjectPayload,
	UserMessagePayload,
} from "./conversation"

describe("conversation entry schemas", () => {
	test("parses valid user message payloads", () => {
		const payload = UserMessagePayload.assert({
			role: "user",
			parts: [{ type: "text", text: "hello" }],
		})

		expect(payload).toEqual({
			role: "user",
			parts: [{ type: "text", text: "hello" }],
		})
	})

	test("rejects user message payloads with the wrong role", () => {
		expect(() =>
			UserMessagePayload.assert({
				role: "assistant",
				parts: [{ type: "text", text: "hello" }],
			}),
		).toThrow()
	})

	test("rejects user message payloads with no parts", () => {
		expect(() =>
			UserMessagePayload.assert({
				role: "user",
				parts: [],
			}),
		).toThrow()
	})

	test("parses valid attachment payloads", () => {
		const payload = AttachmentPayload.assert({
			role: "user",
			name: "whiteboard.png",
			mimeType: "image/png",
			attachmentType: AttachmentType.Image,
			caption: "whiteboard sketch",
		})

		expect(payload).toEqual({
			role: "user",
			name: "whiteboard.png",
			mimeType: "image/png",
			attachmentType: AttachmentType.Image,
			caption: "whiteboard sketch",
		})
	})

	test("rejects extra fields on structured payloads", () => {
		expect(() =>
			AttachmentPayload.assert({
				role: "user",
				name: "whiteboard.png",
				mimeType: "image/png",
				attachmentType: AttachmentType.Image,
				fileId: "file-1",
			}),
		).toThrow()
	})

	test("parses context summary payloads", () => {
		const payload = ContextSummaryPayload.assert({
			covers: {
				startSequence: 1,
				endSequence: 12,
			},
			summary: {
				userIntent: "Design message storage.",
				durableFacts: [],
				preferences: ["Keep the schema simple."],
				decisions: ["Use conversation_entries as the timeline."],
				openTasks: [],
				importantDetails: [],
			},
			promptVersion: "conversation-summary-v1",
			sourceEntryIds: ["entry-1", "entry-2"],
		})

		expect(payload).toMatchObject({
			covers: {
				startSequence: 1,
				endSequence: 12,
			},
			promptVersion: "conversation-summary-v1",
		})
	})

	test("allows generic object payloads for tool entries", () => {
		const payload = GenericObjectPayload.assert({
			toolCallId: "call-1",
			toolName: "calendar.search",
			input: { date: "2026-06-15" },
		})

		expect(payload).toEqual({
			toolCallId: "call-1",
			toolName: "calendar.search",
			input: { date: "2026-06-15" },
		})
	})

	test("rejects non-object generic payloads", () => {
		expect(() => GenericObjectPayload.assert("done")).toThrow()
	})

	test("parses model run metadata and allows extra top-level metadata", () => {
		const metadata = ConversationEntryMetadata.assert({
			modelRun: {
				route: "default-chat",
				provider: "pi",
				model: "pi-model",
				inputTokens: 120,
				outputTokens: 24,
			},
			traceId: "trace-1",
		})

		expect(metadata.modelRun?.model).toBe("pi-model")
		expect(metadata.traceId).toBe("trace-1")
	})

	test("rejects invalid model run metadata", () => {
		expect(() =>
			ConversationEntryMetadata.assert({
				modelRun: {
					route: "default-chat",
					provider: "pi",
					model: "pi-model",
					inputTokens: -1,
				},
			}),
		).toThrow()
	})
})
