/**
 * Validates the generated schemas/roomodes.json against sample configurations
 * using AJV. The schema itself is dynamically generated from the Zod types in
 * packages/types/src/mode.ts -- see packages/types/scripts/generate-roomodes-schema.ts.
 *
 * A separate drift-detection test (roomodes-schema-sync.spec.ts) ensures the
 * checked-in schema stays in sync with the Zod source of truth.
 */
import { describe, it, expect, beforeAll } from "vitest"
import Ajv, { type ValidateFunction } from "ajv"
import * as fs from "fs"
import * as path from "path"
import { fileURLToPath } from "url"

describe("roomodes JSON schema", () => {
	let schema: Record<string, unknown>
	let validate: ValidateFunction

	beforeAll(() => {
		const __dirname = path.dirname(fileURLToPath(import.meta.url))
		const schemaPath = path.resolve(__dirname, "../../../../schemas/roomodes.json")
		schema = JSON.parse(fs.readFileSync(schemaPath, "utf-8"))
		const ajv = new Ajv.default({ strict: false })
		validate = ajv.compile(schema)
	})

	it("should be a valid JSON Schema", () => {
		expect(validate).toBeDefined()
	})

	it("should accept a minimal valid .roomodes config", () => {
		const config = {
			customModes: [
				{
					slug: "my-mode",
					name: "My Mode",
					roleDefinition: "You are a helpful assistant.",
					groups: ["read"],
				},
			],
		}

		const valid = validate(config)
		expect(validate.errors).toBeNull()
		expect(valid).toBe(true)
	})

	it("should accept a mode with all optional properties", () => {
		const config = {
			customModes: [
				{
					slug: "full-mode",
					name: "Full Mode",
					roleDefinition: "A complete mode definition.",
					whenToUse: "Use when you need everything.",
					description: "A mode with all properties.",
					customInstructions: "Follow these additional rules.",
					groups: ["read", "edit", "command", "mcp"],
					source: "project",
				},
			],
		}

		const valid = validate(config)
		expect(validate.errors).toBeNull()
		expect(valid).toBe(true)
	})

	it("should accept the built-in architect mode with tuple-style edit group", () => {
		const config = {
			customModes: [
				{
					slug: "architect",
					name: "Architect",
					roleDefinition: "You are an experienced technical leader.",
					whenToUse: "Use this mode when you need to plan.",
					description: "Plan and design before implementation",
					groups: ["read", ["edit", { fileRegex: "\\.md$", description: "Markdown files only" }], "mcp"],
					source: "project",
				},
			],
		}

		const valid = validate(config)
		expect(validate.errors).toBeNull()
		expect(valid).toBe(true)
	})

	it("should accept a tuple group entry with only fileRegex", () => {
		const config = {
			customModes: [
				{
					slug: "restricted",
					name: "Restricted",
					roleDefinition: "Limited editor.",
					groups: [["edit", { fileRegex: "\\.ts$" }]],
				},
			],
		}

		const valid = validate(config)
		expect(validate.errors).toBeNull()
		expect(valid).toBe(true)
	})

	it("should accept a tuple group entry with empty options", () => {
		const config = {
			customModes: [
				{
					slug: "empty-opts",
					name: "Empty Options",
					roleDefinition: "Mode with empty group options.",
					groups: [["edit", {}]],
				},
			],
		}

		const valid = validate(config)
		expect(validate.errors).toBeNull()
		expect(valid).toBe(true)
	})

	it("should accept the modes tool group", () => {
		const config = {
			customModes: [
				{
					slug: "orchestrator",
					name: "Orchestrator",
					roleDefinition: "You orchestrate other modes.",
					groups: ["read", "modes"],
				},
			],
		}

		const valid = validate(config)
		expect(validate.errors).toBeNull()
		expect(valid).toBe(true)
	})

	it("should reject a config missing customModes", () => {
		const config = {}

		const valid = validate(config)
		expect(valid).toBe(false)
	})

	it("should reject a mode missing required slug", () => {
		const config = {
			customModes: [
				{
					name: "No Slug",
					roleDefinition: "Missing slug.",
					groups: ["read"],
				},
			],
		}

		const valid = validate(config)
		expect(valid).toBe(false)
	})

	it("should reject a mode missing required groups", () => {
		const config = {
			customModes: [
				{
					slug: "no-groups",
					name: "No Groups",
					roleDefinition: "Missing groups.",
				},
			],
		}

		const valid = validate(config)
		expect(valid).toBe(false)
	})

	it("should reject a slug with invalid characters", () => {
		const config = {
			customModes: [
				{
					slug: "invalid slug!",
					name: "Bad Slug",
					roleDefinition: "Invalid slug characters.",
					groups: ["read"],
				},
			],
		}

		const valid = validate(config)
		expect(valid).toBe(false)
	})

	it("should reject an invalid tool group name", () => {
		const config = {
			customModes: [
				{
					slug: "bad-group",
					name: "Bad Group",
					roleDefinition: "Invalid group name.",
					groups: ["nonexistent"],
				},
			],
		}

		const valid = validate(config)
		expect(valid).toBe(false)
	})

	it("should reject additional properties on CustomMode", () => {
		const config = {
			customModes: [
				{
					slug: "extra-props",
					name: "Extra Props",
					roleDefinition: "Has extra properties.",
					groups: ["read"],
					unknownField: true,
				},
			],
		}

		const valid = validate(config)
		expect(valid).toBe(false)
	})

	it("should reject additional properties on GroupOptions", () => {
		const config = {
			customModes: [
				{
					slug: "bad-opts",
					name: "Bad Options",
					roleDefinition: "Extra options properties.",
					groups: [["edit", { fileRegex: "\\.md$", unknownOpt: true }]],
				},
			],
		}

		const valid = validate(config)
		expect(valid).toBe(false)
	})

	it("should reject a tuple with more than 2 elements", () => {
		const config = {
			customModes: [
				{
					slug: "big-tuple",
					name: "Big Tuple",
					roleDefinition: "Too many tuple elements.",
					groups: [["edit", { fileRegex: "\\.md$" }, "extra"]],
				},
			],
		}

		const valid = validate(config)
		expect(valid).toBe(false)
	})

	it("should reject an invalid source value", () => {
		const config = {
			customModes: [
				{
					slug: "bad-source",
					name: "Bad Source",
					roleDefinition: "Invalid source.",
					groups: ["read"],
					source: "unknown",
				},
			],
		}

		const valid = validate(config)
		expect(valid).toBe(false)
	})

	it("should accept an empty customModes array", () => {
		const config = {
			customModes: [],
		}

		const valid = validate(config)
		expect(validate.errors).toBeNull()
		expect(valid).toBe(true)
	})

	it("should accept the browser tool group (deprecated but valid)", () => {
		const config = {
			customModes: [
				{
					slug: "browser-mode",
					name: "Browser Mode",
					roleDefinition: "A mode that uses the browser tool group.",
					groups: ["read", "browser", "command"],
				},
			],
		}

		const valid = validate(config)
		expect(validate.errors).toBeNull()
		expect(valid).toBe(true)
	})

	it("should accept a browser tuple group entry", () => {
		const config = {
			customModes: [
				{
					slug: "browser-tuple",
					name: "Browser Tuple",
					roleDefinition: "A mode with browser tuple.",
					groups: [["browser", { fileRegex: "\\.html$", description: "HTML files only" }]],
				},
			],
		}

		const valid = validate(config)
		expect(validate.errors).toBeNull()
		expect(valid).toBe(true)
	})

	it("should accept a mode with rulesFiles", () => {
		const config = {
			customModes: [
				{
					slug: "rules-mode",
					name: "Rules Mode",
					roleDefinition: "A mode with rules files.",
					groups: ["read"],
					rulesFiles: [
						{
							relativePath: "rule1.md",
							content: "# Rule 1\nFollow this rule.",
						},
						{
							relativePath: "subfolder/rule2.md",
							content: "# Rule 2\nFollow this other rule.",
						},
					],
				},
			],
		}

		const valid = validate(config)
		expect(validate.errors).toBeNull()
		expect(valid).toBe(true)
	})

	it("should accept a mode with empty rulesFiles array", () => {
		const config = {
			customModes: [
				{
					slug: "empty-rules",
					name: "Empty Rules",
					roleDefinition: "A mode with empty rules files.",
					groups: ["read"],
					rulesFiles: [],
				},
			],
		}

		const valid = validate(config)
		expect(validate.errors).toBeNull()
		expect(valid).toBe(true)
	})

	it("should accept rulesFiles entries with only relativePath (content is optional)", () => {
		const config = {
			customModes: [
				{
					slug: "path-only-rules",
					name: "Path Only Rules",
					roleDefinition: "A mode with rules files that only have relativePath.",
					groups: ["read"],
					rulesFiles: [{ relativePath: "rule1.md" }],
				},
			],
		}

		const valid = validate(config)
		expect(validate.errors).toBeNull()
		expect(valid).toBe(true)
	})

	it("should reject rulesFiles entries missing required relativePath", () => {
		const config = {
			customModes: [
				{
					slug: "bad-rules",
					name: "Bad Rules",
					roleDefinition: "A mode with invalid rules files.",
					groups: ["read"],
					rulesFiles: [{ content: "some content" }],
				},
			],
		}

		const valid = validate(config)
		expect(valid).toBe(false)
	})

	it("should reject rulesFiles entries with extra properties", () => {
		const config = {
			customModes: [
				{
					slug: "extra-rules",
					name: "Extra Rules",
					roleDefinition: "A mode with extra rule properties.",
					groups: ["read"],
					rulesFiles: [{ relativePath: "rule1.md", content: "content", extra: true }],
				},
			],
		}

		const valid = validate(config)
		expect(valid).toBe(false)
	})

	it("should accept multiple modes", () => {
		const config = {
			customModes: [
				{
					slug: "mode-a",
					name: "Mode A",
					roleDefinition: "First mode.",
					groups: ["read"],
				},
				{
					slug: "mode-b",
					name: "Mode B",
					roleDefinition: "Second mode.",
					groups: ["read", "edit"],
				},
			],
		}

		const valid = validate(config)
		expect(validate.errors).toBeNull()
		expect(valid).toBe(true)
	})
})
