package metadata

import (
	"strings"
	"testing"
	"time"

	"github.com/google/uuid"

	"git.warky.dev/wdevs/amcs/internal/config"
	thoughttypes "git.warky.dev/wdevs/amcs/internal/types"
)

func testCaptureConfig() config.CaptureConfig {
	return config.CaptureConfig{
		Source: "mcp",
		MetadataDefaults: config.CaptureMetadataDefault{
			Type:          "observation",
			TopicFallback: "uncategorized",
		},
	}
}

func TestFallbackUsesConfiguredDefaults(t *testing.T) {
	got := Fallback(testCaptureConfig())
	if got.Type != "observation" {
		t.Fatalf("Fallback type = %q, want observation", got.Type)
	}
	if len(got.Topics) != 1 || got.Topics[0] != "uncategorized" {
		t.Fatalf("Fallback topics = %#v, want [uncategorized]", got.Topics)
	}
	if got.Source != "mcp" {
		t.Fatalf("Fallback source = %q, want mcp", got.Source)
	}
	if got.MetadataStatus != MetadataStatusComplete {
		t.Fatalf("Fallback metadata status = %q, want complete", got.MetadataStatus)
	}
}

func TestNormalizeTrimsDedupesAndCapsTopics(t *testing.T) {
	topics := []string{}
	for i := 0; i < 12; i++ {
		topics = append(topics, strings.TrimSpace(" topic "))
		topics = append(topics, string(rune('a'+i)))
	}

	got := Normalize(thoughttypes.ThoughtMetadata{
		People: []string{" Alice ", "alice", "", "Bob"},
		Topics: topics,
		Type:   "INVALID",
	}, testCaptureConfig())

	if len(got.People) != 2 {
		t.Fatalf("People len = %d, want 2", len(got.People))
	}
	if got.Type != "observation" {
		t.Fatalf("Type = %q, want observation", got.Type)
	}
	if len(got.Topics) != maxTopics {
		t.Fatalf("Topics len = %d, want %d", len(got.Topics), maxTopics)
	}
}

func TestMergeAddsPatchAndNormalizes(t *testing.T) {
	base := thoughttypes.ThoughtMetadata{
		People: []string{"Alice"},
		Topics: []string{"go"},
		Type:   "idea",
		Source: "mcp",
	}
	patch := thoughttypes.ThoughtMetadata{
		People: []string{" Bob ", "alice"},
		Topics: []string{"testing"},
		Type:   "task",
	}

	got := Merge(base, patch, testCaptureConfig())
	if got.Type != "task" {
		t.Fatalf("Type = %q, want task", got.Type)
	}
	if len(got.People) != 2 {
		t.Fatalf("People len = %d, want 2", len(got.People))
	}
	if len(got.Topics) != 2 {
		t.Fatalf("Topics len = %d, want 2", len(got.Topics))
	}
}

func TestNormalizeDedupesAttachmentsByFileID(t *testing.T) {
	id := uuid.New()

	got := Normalize(thoughttypes.ThoughtMetadata{
		Attachments: []thoughttypes.ThoughtAttachment{
			{FileID: id, Name: " one.png ", MediaType: " image/png ", Kind: " image ", SizeBytes: 12, SHA256: " abc "},
			{FileID: id, Name: "two.png", MediaType: "image/png", Kind: "image", SizeBytes: 99, SHA256: "def"},
		},
	}, testCaptureConfig())

	if len(got.Attachments) != 1 {
		t.Fatalf("Attachments len = %d, want 1", len(got.Attachments))
	}
	if got.Attachments[0].Name != "one.png" {
		t.Fatalf("Attachment name = %q, want one.png", got.Attachments[0].Name)
	}
	if got.Attachments[0].Kind != "image" {
		t.Fatalf("Attachment kind = %q, want image", got.Attachments[0].Kind)
	}
}

func TestSanitizeExtractedDropsAttachmentsAndMetadataControlFields(t *testing.T) {
	id := uuid.New()

	got := SanitizeExtracted(thoughttypes.ThoughtMetadata{
		Type:                    "idea",
		Attachments:             []thoughttypes.ThoughtAttachment{{FileID: id, Name: "secret.pdf"}},
		MetadataStatus:          MetadataStatusFailed,
		MetadataUpdatedAt:       "2026-03-30T10:00:00Z",
		MetadataLastAttemptedAt: "2026-03-30T10:01:00Z",
		MetadataError:           "boom",
	})

	if len(got.Attachments) != 0 {
		t.Fatalf("Attachments len = %d, want 0", len(got.Attachments))
	}
	if got.MetadataStatus != "" {
		t.Fatalf("MetadataStatus = %q, want empty", got.MetadataStatus)
	}
	if got.MetadataUpdatedAt != "" {
		t.Fatalf("MetadataUpdatedAt = %q, want empty", got.MetadataUpdatedAt)
	}
	if got.MetadataLastAttemptedAt != "" {
		t.Fatalf("MetadataLastAttemptedAt = %q, want empty", got.MetadataLastAttemptedAt)
	}
	if got.MetadataError != "" {
		t.Fatalf("MetadataError = %q, want empty", got.MetadataError)
	}
	if got.Type != "idea" {
		t.Fatalf("Type = %q, want idea", got.Type)
	}
}

func TestMarkMetadataPendingTracksAttemptWithoutClearingPreviousSuccess(t *testing.T) {
	attempt := time.Date(2026, 3, 30, 10, 0, 0, 0, time.UTC)
	base := thoughttypes.ThoughtMetadata{
		Topics:            []string{"go"},
		MetadataUpdatedAt: "2026-03-29T10:00:00Z",
	}

	got := MarkMetadataPending(base, testCaptureConfig(), attempt, errTestMetadataFailure)
	if got.MetadataStatus != MetadataStatusPending {
		t.Fatalf("MetadataStatus = %q, want pending", got.MetadataStatus)
	}
	if got.MetadataUpdatedAt != "2026-03-29T10:00:00Z" {
		t.Fatalf("MetadataUpdatedAt = %q, want previous success timestamp", got.MetadataUpdatedAt)
	}
	if got.MetadataLastAttemptedAt != "2026-03-30T10:00:00Z" {
		t.Fatalf("MetadataLastAttemptedAt = %q, want attempt timestamp", got.MetadataLastAttemptedAt)
	}
	if got.MetadataError == "" {
		t.Fatal("MetadataError is empty, want failure message")
	}
}

func TestMarkMetadataCompleteClearsErrorAndSetsTimestamps(t *testing.T) {
	attempt := time.Date(2026, 3, 30, 10, 0, 0, 0, time.UTC)

	got := MarkMetadataComplete(thoughttypes.ThoughtMetadata{
		Topics:         []string{"go"},
		MetadataStatus: MetadataStatusFailed,
		MetadataError:  "timeout",
	}, testCaptureConfig(), attempt)

	if got.MetadataStatus != MetadataStatusComplete {
		t.Fatalf("MetadataStatus = %q, want complete", got.MetadataStatus)
	}
	if got.MetadataUpdatedAt != "2026-03-30T10:00:00Z" {
		t.Fatalf("MetadataUpdatedAt = %q, want completion timestamp", got.MetadataUpdatedAt)
	}
	if got.MetadataLastAttemptedAt != "2026-03-30T10:00:00Z" {
		t.Fatalf("MetadataLastAttemptedAt = %q, want completion timestamp", got.MetadataLastAttemptedAt)
	}
	if got.MetadataError != "" {
		t.Fatalf("MetadataError = %q, want empty", got.MetadataError)
	}
}

var errTestMetadataFailure = testError("timeout")

type testError string

func (e testError) Error() string {
	return string(e)
}
