package dialectsfcrm

import (
	"context"
	"errors"
	"reflect"
	"testing"

	"github.com/nexuscrm/shared/pkg/contract"
	"github.com/nexuscrm/shared/pkg/models"
	"github.com/nexuscrm/shared/pkg/queryspec"
)

func TestFieldHistoryReportSourceRegistrationsOwnPinnedSourceNames(t *testing.T) {
	registrations, err := FieldHistoryReportSourceRegistrations(&fieldHistoryReportQuery{})
	if err != nil {
		t.Fatalf("FieldHistoryReportSourceRegistrations: %v", err)
	}
	want := []string{"AccountHistory", "OpportunityFieldHistory", "OpportunityHistory"}
	wantMetadataColumns := [][]string{{"Id", "AccountId"}, {"Id", "OpportunityId"}, {"Id", "OpportunityId"}}
	if len(registrations) != len(want) {
		t.Fatalf("registration count = %d, want %d", len(registrations), len(want))
	}
	for i, registration := range registrations {
		if registration.SourceRef != want[i] {
			t.Fatalf("registration[%d].SourceRef = %q, want %q", i, registration.SourceRef, want[i])
		}
		if !registration.Capabilities.Filters || !registration.Capabilities.Grouping || !registration.Capabilities.Pagination {
			t.Fatalf("registration[%d] capabilities = %#v", i, registration.Capabilities)
		}
		if i < 2 && !registration.RowFieldSecurityRequired {
			t.Fatalf("registration[%d] does not require dynamic row field security", i)
		}
		metadataColumns := make([]string, 0, 2)
		for _, column := range registration.Columns {
			if column.MetadataOnly {
				metadataColumns = append(metadataColumns, column.Name)
			}
		}
		if !reflect.DeepEqual(metadataColumns, wantMetadataColumns[i]) {
			t.Fatalf("registration[%d] metadata columns = %#v, want %#v", i, metadataColumns, wantMetadataColumns[i])
		}
	}
	if _, err := FieldHistoryReportSourceRegistrations(nil); err == nil {
		t.Fatal("FieldHistoryReportSourceRegistrations(nil) error = nil")
	}
}

func TestFieldHistoryReportSourceRegistrationsAcceptValidatedPackColumns(t *testing.T) {
	registrations, err := FieldHistoryReportSourceRegistrationsWithOptions(&fieldHistoryReportQuery{}, FieldHistoryReportSourceOptions{
		OpportunityHistoryColumns: []OpportunityHistoryReportColumn{
			{Name: "Territory", FieldAPIName: "Territory__c"},
			{Name: "BookedValue", FieldAPIName: "BookedValue__c", HistoricalSnapshot: true},
		},
	})
	if err != nil {
		t.Fatalf("FieldHistoryReportSourceRegistrationsWithOptions: %v", err)
	}
	opportunityHistory := registrations[2]
	want := map[string]struct {
		field   string
		derived bool
	}{
		"Territory":   {field: "Territory__c"},
		"BookedValue": {field: "BookedValue__c", derived: true},
	}
	for _, column := range opportunityHistory.Columns {
		expected, ok := want[column.Name]
		if !ok {
			continue
		}
		if len(column.FieldReferences) != 1 || column.FieldReferences[0].ObjectAPIName != "Opportunity" || column.FieldReferences[0].FieldAPIName != expected.field || column.Derived != expected.derived {
			t.Fatalf("column %s = %#v, want field %s derived=%t", column.Name, column, expected.field, expected.derived)
		}
		delete(want, column.Name)
	}
	if len(want) != 0 {
		t.Fatalf("missing extension columns: %#v", want)
	}
}

func TestFieldHistoryReportSourceExecutesNeutralHistoryQuery(t *testing.T) {
	oldValue, newValue := "Prospecting", "Qualification"
	query := &fieldHistoryReportQuery{
		historyResult: &contract.HistoryQueryResult{
			TotalSize: 12,
			Rows: []contract.HistoryRecord{{
				ID:          "017-history",
				ParentID:    "006-opportunity",
				Field:       "StageName",
				OldValue:    &oldValue,
				NewValue:    &newValue,
				DataType:    "string",
				CreatedDate: "2026-07-18T10:00:00.000Z",
				CreatedByID: "005-writer",
			}},
		},
		queryResult: &contract.QueryResult{Records: []contract.Record{{
			"Id": "006-opportunity", "Name": "Renewal",
		}}},
	}
	registrations, err := FieldHistoryReportSourceRegistrations(query)
	if err != nil {
		t.Fatalf("FieldHistoryReportSourceRegistrations: %v", err)
	}
	registration := registrations[1]
	result, err := registration.Source.Execute(context.Background(), contract.VirtualReportSourceRequest{
		Definition: models.ReportDefinition{
			SourceObject: "Opportunity",
			Scope:        "all",
			Filters: []models.ReportFilter{{
				Column: "Field", Operator: "equals", Value: "StageName",
			}},
			DateFilter: &models.ReportDateFilter{
				Column: "CreatedDate", StartDate: "2026-07-01T00:00:00.000Z",
			},
		},
		Columns: registration.Columns,
		Limit:   25,
		Offset:  5,
	}, &models.UserSession{ID: "005-reader"})
	if err != nil {
		t.Fatalf("Execute: %v", err)
	}
	if query.historyRequest.ParentObjectAPIName != "Opportunity" || query.historyRequest.PageSize != 25 || query.historyRequest.CursorOffset != 5 {
		t.Fatalf("history request = %#v", query.historyRequest)
	}
	wantFilter := queryspec.And(
		queryspec.FieldPredicate("Field", "=", "StageName"),
		queryspec.FieldPredicate("CreatedDate", ">=", "2026-07-01T00:00:00.000Z"),
	)
	if !reflect.DeepEqual(query.historyRequest.Filter, wantFilter) {
		t.Fatalf("history filter = %#v, want %#v", query.historyRequest.Filter, wantFilter)
	}
	if query.parentRequest.ObjectAPIName != "Opportunity" || query.parentRequest.Limit != 1 {
		t.Fatalf("parent request = %#v", query.parentRequest)
	}
	if result.TotalCount != 12 || len(result.Rows) != 1 {
		t.Fatalf("result = %#v", result)
	}
	row := result.Rows[0]
	if row.Values["OpportunityName"].AccessObjectAPIName != "Opportunity" || row.Values["OpportunityName"].RecordFieldName != "Name" {
		t.Fatalf("row values = %#v", row.Values)
	}
	if row.Values["OpportunityId"].AccessObjectAPIName != "Opportunity" || row.Values["OpportunityId"].RecordFieldName != "Id" {
		t.Fatalf("parent-id projection = %#v", row.Values["OpportunityId"])
	}
	wantHistoryFields := map[string]contract.VirtualReportHistoryField{
		"Id":          contract.VirtualReportHistoryID,
		"Field":       contract.VirtualReportHistoryFieldName,
		"OldValue":    contract.VirtualReportHistoryOldValue,
		"NewValue":    contract.VirtualReportHistoryNewValue,
		"DataType":    contract.VirtualReportHistoryDataType,
		"CreatedDate": contract.VirtualReportHistoryCreatedDate,
		"CreatedById": contract.VirtualReportHistoryCreatedByID,
	}
	for column, field := range wantHistoryFields {
		projection := row.Values[column].History
		if projection == nil || projection.RecordID != "017-history" || projection.Field != field {
			t.Fatalf("%s projection = %#v, want history field %q", column, projection, field)
		}
	}
	if len(row.AccessRecords) != 1 || row.AccessRecords[0].ObjectAPIName != "Opportunity" || row.AccessRecords[0].RecordID != "006-opportunity" {
		t.Fatalf("access evidence = %#v", row.AccessRecords)
	}
	if row.HistoryRecordID != "017-history" {
		t.Fatalf("row history gate = %q", row.HistoryRecordID)
	}
}

func TestFieldHistoryReportSourceFailsClosedOnUnsupportedFilterShape(t *testing.T) {
	registrations, err := FieldHistoryReportSourceRegistrations(&fieldHistoryReportQuery{})
	if err != nil {
		t.Fatalf("FieldHistoryReportSourceRegistrations: %v", err)
	}
	for _, definition := range []models.ReportDefinition{
		{Scope: "mine"},
		{Filters: []models.ReportFilter{{Column: "OldValue", Operator: "equals", Value: "secret"}}},
		{FilterLogic: "1 OR 2"},
	} {
		if _, err := registrations[0].Source.Execute(context.Background(), contract.VirtualReportSourceRequest{
			Definition: definition,
			Columns:    registrations[0].Columns,
			Limit:      10,
		}, &models.UserSession{ID: "005-reader"}); err == nil {
			t.Fatalf("Execute(%#v) error = nil, want fail-closed rejection", definition)
		}
	}
}

type fieldHistoryReportQuery struct {
	historyRequest contract.HistoryQueryRequest
	parentRequest  queryspec.QuerySpec
	historyResult  *contract.HistoryQueryResult
	queryResult    *contract.QueryResult
}

func (q *fieldHistoryReportQuery) Query(_ context.Context, req queryspec.QuerySpec, _ *models.UserSession) (*contract.QueryResult, error) {
	q.parentRequest = req
	if q.queryResult == nil {
		return nil, errors.New("unexpected parent query")
	}
	return q.queryResult, nil
}

func (*fieldHistoryReportQuery) SearchWithSpec(context.Context, queryspec.SearchSpec, *models.UserSession) ([]contract.Record, error) {
	return nil, errors.New("unexpected search")
}

func (*fieldHistoryReportQuery) GlobalSearchWithSpec(context.Context, queryspec.GlobalSearchSpec, *models.UserSession) ([]contract.SearchResult, error) {
	return nil, errors.New("unexpected global search")
}

func (q *fieldHistoryReportQuery) QueryHistory(_ context.Context, req contract.HistoryQueryRequest, _ *models.UserSession) (*contract.HistoryQueryResult, error) {
	q.historyRequest = req
	if q.historyResult == nil {
		return nil, errors.New("unexpected history query")
	}
	return q.historyResult, nil
}
