package layoutauthoringv2

import (
	"errors"
	"fmt"
	common "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/commonv1"
	historysemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/queryv1"
	"testing"
)

func TestHistoryProjectionIsAClosedResultBinding(t *testing.T) {
	valid := func() HistoryRelatedListProjection {
		return HistoryRelatedListProjection{ObjectID: "ChangeLog", Columns: map[string]historysemantic.HistoryColumn{
			"Key": historysemantic.HistoryColumnID, "Parent": historysemantic.HistoryColumnParentID, "Attribute": historysemantic.HistoryColumnField,
			"Before": historysemantic.HistoryColumnOldValue, "After": historysemantic.HistoryColumnNewValue, "Type": historysemantic.HistoryColumnDataType,
			"Time": historysemantic.HistoryColumnCreatedDate, "Actor": historysemantic.HistoryColumnCreatedByID,
		}}
	}
	if err := ValidateHistoryRelatedListProjections("Issue", []HistoryRelatedListProjection{valid()}); err != nil {
		t.Fatal(err)
	}
	for _, count := range []int{0, MaxHistoryRelatedListProjections - 1, MaxHistoryRelatedListProjections} {
		projections := make([]HistoryRelatedListProjection, count)
		for i := range projections {
			projections[i] = valid()
			projections[i].ObjectID = fmt.Sprintf("Changes%d", i)
		}
		if err := ValidateHistoryRelatedListProjections("Issue", projections); err != nil {
			t.Fatalf("count %d: %v", count, err)
		}
	}
	for _, test := range []struct {
		name string
		edit func(*HistoryRelatedListProjection)
	}{
		{"arbitrary schema field", func(p *HistoryRelatedListProjection) { p.Columns["Before"] = "secret" }},
		{"duplicate member", func(p *HistoryRelatedListProjection) { p.Columns["Before"] = historysemantic.HistoryColumnNewValue }},
		{"case-folded alias", func(p *HistoryRelatedListProjection) {
			delete(p.Columns, "Before")
			p.Columns["AFTER"] = historysemantic.HistoryColumnOldValue
		}},
		{"missing member", func(p *HistoryRelatedListProjection) { delete(p.Columns, "Before") }},
		{"root shadow", func(p *HistoryRelatedListProjection) { p.ObjectID = "Issue" }},
		{"relationship expression", func(p *HistoryRelatedListProjection) {
			delete(p.Columns, "Before")
			p.Columns["Owner.Secret"] = historysemantic.HistoryColumnOldValue
		}},
	} {
		t.Run(test.name, func(t *testing.T) {
			p := valid()
			test.edit(&p)
			if err := ValidateHistoryRelatedListProjections("Issue", []HistoryRelatedListProjection{p}); err == nil {
				t.Fatal("accepted invalid projection")
			}
		})
	}
	if err := ValidateHistoryRelatedListProjections("Issue", []HistoryRelatedListProjection{valid(), valid()}); err == nil {
		t.Fatal("accepted ambiguous source")
	}
}

func TestHistoryProjectionResourceLimits(t *testing.T) {
	for _, tc := range []struct {
		name           string
		count, columns int
		kind           common.LimitKind
		maximum        uint64
	}{
		{"projections", MaxHistoryRelatedListProjections + 1, 8, common.LimitOperationCount, MaxHistoryRelatedListProjections},
		{"columns", 1, historysemantic.HistoryResultColumnCount + 1, common.LimitFieldCount, historysemantic.HistoryResultColumnCount},
	} {
		t.Run(tc.name, func(t *testing.T) {
			projections := make([]HistoryRelatedListProjection, tc.count)
			for i := range projections {
				projections[i] = HistoryRelatedListProjection{ObjectID: fmt.Sprintf("Changes%d", i), Columns: map[string]historysemantic.HistoryColumn{}}
				for j := 0; j < tc.columns; j++ {
					projections[i].Columns[fmt.Sprintf("Field%d", j)] = historysemantic.HistoryColumnID
				}
			}
			err := ValidateHistoryRelatedListProjections("Issue", projections)
			var failure *common.Failure
			if !errors.As(err, &failure) || failure.Category != common.FailureResourceExhausted || failure.LimitKind != tc.kind || failure.Maximum != tc.maximum {
				t.Fatalf("limit = %#v", err)
			}
		})
	}
}
