package shared

import (
	"reflect"
	"testing"

	"gitea.maximumdirect.net/eric/notarius/internal/core/source"
)

func TestCitationResolver(t *testing.T) {
	doc := citationDocument()
	resolver, err := NewCitationResolver(doc)
	if err != nil {
		t.Fatalf("NewCitationResolver() error = %v", err)
	}
	tests := []struct {
		name    string
		refs    []source.SourceRef
		want    string
		wantErr bool
	}{
		{name: "empty references", refs: nil, want: ""},
		{name: "invalid source id", refs: []source.SourceRef{{SourceID: "other", StartUnitID: 10, EndUnitID: 10}}, wantErr: true},
		{name: "unknown start unit", refs: []source.SourceRef{{SourceID: "session", StartUnitID: 99, EndUnitID: 10}}, wantErr: true},
		{name: "unknown end unit", refs: []source.SourceRef{{SourceID: "session", StartUnitID: 10, EndUnitID: 99}}, wantErr: true},
		{name: "reversed document range", refs: []source.SourceRef{{SourceID: "session", StartUnitID: 30, EndUnitID: 10}}, wantErr: true},
		{
			name: "disjoint ranges supplied out of order",
			refs: []source.SourceRef{
				{SourceID: "session", StartUnitID: 30, EndUnitID: 30},
				{SourceID: "session", StartUnitID: 10, EndUnitID: 10},
			},
			want: "alpha\ngamma",
		},
		{
			name: "adjacent ranges",
			refs: []source.SourceRef{
				{SourceID: "session", StartUnitID: 10, EndUnitID: 20},
				{SourceID: "session", StartUnitID: 30, EndUnitID: 40},
			},
			want: "alpha\nbeta\ngamma\ndelta",
		},
		{
			name: "overlapping and duplicate ranges",
			refs: []source.SourceRef{
				{SourceID: "session", StartUnitID: 10, EndUnitID: 30},
				{SourceID: "session", StartUnitID: 20, EndUnitID: 40},
				{SourceID: "session", StartUnitID: 10, EndUnitID: 30},
			},
			want: "alpha\nbeta\ngamma\ndelta",
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			beforeUnits := append([]source.SourceUnit(nil), doc.Units...)
			refs := append([]source.SourceRef(nil), test.refs...)
			got, err := resolver.CitedText(refs)
			if (err != nil) != test.wantErr {
				t.Fatalf("CitedText() error = %v, want error = %t", err, test.wantErr)
			}
			if err == nil && got != test.want {
				t.Fatalf("CitedText() = %q, want %q", got, test.want)
			}
			if !reflect.DeepEqual(doc.Units, beforeUnits) || !reflect.DeepEqual(refs, test.refs) {
				t.Fatalf("CitedText() mutated document or references")
			}
		})
	}
}

func TestNewCitationResolverRejectsNilDocument(t *testing.T) {
	if _, err := NewCitationResolver(nil); err == nil {
		t.Fatal("NewCitationResolver() error = nil, want nil-document error")
	}
}

func citationDocument() *source.SourceDocument {
	return &source.SourceDocument{
		ID:     "session",
		Kind:   "transcript",
		Format: "application/json",
		Digest: "sha256:session",
		Units: []source.SourceUnit{
			{ID: 10, Kind: "message", Text: "alpha"},
			{ID: 20, Kind: "message", Text: "beta"},
			{ID: 30, Kind: "message", Text: "gamma"},
			{ID: 40, Kind: "message", Text: "delta"},
		},
	}
}
