package llm

import (
	"encoding/json"
	"sort"
	"strings"
	"testing"
)

func TestLookupResponseSchemaSucceedsForRegisteredSchemas(t *testing.T) {
	tests := []ResponseSchemaKey{
		TestArtifactSchemaKey,
		TestValidatorDecisionSchemaKey,
	}

	for _, key := range tests {
		t.Run(string(key), func(t *testing.T) {
			schema, ok := LookupResponseSchema(key)
			if !ok {
				t.Fatalf("expected schema for key %q", key)
			}
			if schema.Key != key {
				t.Fatalf("unexpected key: got %q want %q", schema.Key, key)
			}
			if schema.ID == "" || schema.Version == "" || schema.Name == "" {
				t.Fatalf("expected schema metadata, got %+v", schema)
			}
			if !strings.HasPrefix(schema.SHA256, "sha256:") {
				t.Fatalf("expected prefixed hash, got %q", schema.SHA256)
			}
		})
	}
}

func TestLookupResponseSchemaUnknownReturnsFalse(t *testing.T) {
	if schema, ok := LookupResponseSchema("unknown"); ok {
		t.Fatalf("expected unknown schema lookup to fail, got %+v", schema)
	}
}

func TestLookupResponseSchemaDNDSpellsLLMIsNotFrameworkRegistered(t *testing.T) {
	if schema, ok := LookupResponseSchema("dnd_spells_llm"); ok {
		t.Fatalf("expected D&D spells schema lookup to fail in framework registry, got %+v", schema)
	}
}

func TestMustLookupResponseSchemaPanicsForUnknownKey(t *testing.T) {
	defer func() {
		if recover() == nil {
			t.Fatalf("expected panic")
		}
	}()

	_ = MustLookupResponseSchema("unknown")
}

func TestRegisteredResponseSchemasSortedByKey(t *testing.T) {
	schemas := RegisteredResponseSchemas()
	keys := make([]string, len(schemas))
	seen := make(map[ResponseSchemaKey]bool, len(schemas))
	for i, schema := range schemas {
		keys[i] = string(schema.Key)
		seen[schema.Key] = true
	}
	if !sort.StringsAreSorted(keys) {
		t.Fatalf("expected sorted keys, got %v", keys)
	}
	if !seen[TestArtifactSchemaKey] || !seen[TestValidatorDecisionSchemaKey] {
		t.Fatalf("registered schemas = %v, want test schemas", keys)
	}
}

func TestResponseSchemaContentIsValidJSON(t *testing.T) {
	for _, schema := range RegisteredResponseSchemas() {
		if !json.Valid(schema.JSONSchema) {
			t.Fatalf("schema %q has invalid JSON: %s", schema.Key, schema.JSONSchema)
		}
	}
}

func TestResponseSchemaJSONIsMutationSafe(t *testing.T) {
	for _, key := range []ResponseSchemaKey{TestArtifactSchemaKey, TestValidatorDecisionSchemaKey} {
		t.Run(string(key), func(t *testing.T) {
			first := MustLookupResponseSchema(key)
			first.JSONSchema[0] = '['

			second := MustLookupResponseSchema(key)
			if !json.Valid(second.JSONSchema) {
				t.Fatalf("schema JSON was mutated: %s", second.JSONSchema)
			}
			if len(second.JSONSchema) > 0 && second.JSONSchema[0] == '[' {
				t.Fatalf("schema JSON did not use defensive copy")
			}

			registered := RegisteredResponseSchemas()
			for i := range registered {
				if registered[i].Key == key {
					registered[i].JSONSchema[0] = '['
				}
			}
			again := MustLookupResponseSchema(key)
			if !json.Valid(again.JSONSchema) || again.JSONSchema[0] == '[' {
				t.Fatalf("registered schema JSON did not use defensive copy")
			}
		})
	}
}

func TestResponseSchemaDiagnosticsMapOmitsRawSchemaContent(t *testing.T) {
	schema := MustLookupResponseSchema(TestValidatorDecisionSchemaKey)
	diagnostics := schema.DiagnosticsMap()

	for _, key := range []string{"id", "version", "name", "sha256"} {
		if diagnostics[key] == "" {
			t.Fatalf("expected diagnostics key %q, got %#v", key, diagnostics)
		}
	}
	if _, ok := diagnostics["json_schema"]; ok {
		t.Fatalf("diagnostics should omit raw schema content: %#v", diagnostics)
	}
	if _, ok := diagnostics["JSONSchema"]; ok {
		t.Fatalf("diagnostics should omit raw schema content: %#v", diagnostics)
	}
}
