package mapping

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

	semantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/queryv1"
	commonv1 "github.com/pingcap-inc/nexuscrm-contract/wire-go/gen/common/v1"
	runtimev1 "github.com/pingcap-inc/nexuscrm-contract/wire-go/gen/runtime/v1"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"google.golang.org/protobuf/encoding/protowire"
	"google.golang.org/protobuf/proto"
)

func TestQueryWireRoundTripPreservesTypedValuesAndPlan(t *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	zero, empty, falseValue := int64(0), "", false
	request := semantic.Request{ObjectID: "Issue", Selections: []semantic.Selection{{Kind: semantic.SelectionField, Field: "Active"}, {Kind: semantic.SelectionField, Field: "Amount"}, {Kind: semantic.SelectionField, Field: "Name"}}, Limit: 200, Filter: semantic.Predicate{Kind: semantic.PredicateAnd, Children: []semantic.Predicate{{Kind: semantic.PredicateFieldComparison, Field: "Active", Operator: semantic.ComparisonEqual, Value: &semantic.Scalar{Boolean: &falseValue}}, {Kind: semantic.PredicateFieldComparison, Field: "Amount", Operator: semantic.ComparisonEqual, Value: &semantic.Scalar{Integer: &zero}}, {Kind: semantic.PredicateComparison, Operator: semantic.ComparisonEqual, Left: semantic.Operand{Kind: semantic.OperandFunction, Function: semantic.FunctionTemporalPart, TemporalBasis: semantic.TemporalBasisBusiness, TemporalPart: semantic.TemporalPartQuarter, Args: []semantic.Operand{{Kind: semantic.OperandField, Field: "CreatedDate"}}}, Right: semantic.Operand{Kind: semantic.OperandLiteral, Literal: &semantic.Scalar{Integer: &zero}}}, {Kind: semantic.PredicateComparison, Operator: semantic.ComparisonEqual, Left: semantic.Operand{Kind: semantic.OperandField, Field: "OwnerId"}, Right: semantic.Operand{Kind: semantic.OperandTrustedContext, TrustedContext: semantic.TrustedContextPrincipalID}}}}}
	wireRequest, err := ToWireQueryRecordsRequest(ctx, request)
	if err != nil {
		t.Fatal(err)
	}
	roundRequest, err := FromWireQueryRecordsRequest(ctx, wireRequest)
	if err != nil {
		t.Fatal(err)
	}
	if !reflect.DeepEqual(roundRequest, request) {
		t.Fatalf("request round trip\n got %#v\nwant %#v", roundRequest, request)
	}
	response := semantic.Response{TotalSize: 1, Done: true, Receipts: semantic.Receipts{ProtocolVersion: "1.0", CapabilityVersion: semantic.CapabilityName}, Records: []semantic.Record{{ObjectID: "Issue", Fields: []semantic.Field{{Name: "Active", Value: semantic.Value{Kind: semantic.ValueBoolean, Boolean: &falseValue}}, {Name: "Amount", Value: semantic.Value{Kind: semantic.ValueInteger, Integer: &zero}}, {Name: "Budget", Value: semantic.Value{Kind: semantic.ValueDecimal, Decimal: &semantic.Decimal{Coefficient: "0", Scale: 4}}}, {Name: "Name", Value: semantic.Value{Kind: semantic.ValueString, String: &empty}}, {Name: "Nullable", Value: semantic.Value{Kind: semantic.ValueString, Null: true}}}}}}
	wireResponse, err := ToWireQueryRecordsResponse(response)
	if err != nil {
		t.Fatal(err)
	}
	roundResponse, err := FromWireQueryRecordsResponse(wireResponse)
	if err != nil {
		t.Fatal(err)
	}
	if !reflect.DeepEqual(roundResponse, response) {
		t.Fatalf("response round trip\n got %#v\nwant %#v", roundResponse, response)
	}
}

func TestQueryWireIgnoresUnknownOptionalFieldsButRejectsMalformedKnownPlan(t *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	request := semantic.Request{ObjectID: "Issue", Selections: []semantic.Selection{{Kind: semantic.SelectionField, Field: "Id"}}, Limit: 20}
	wireRequest, err := ToWireQueryRecordsRequest(ctx, request)
	if err != nil {
		t.Fatal(err)
	}
	encoded, err := proto.Marshal(wireRequest)
	if err != nil {
		t.Fatal(err)
	}
	encoded = protowire.AppendTag(encoded, 100, protowire.BytesType)
	encoded = protowire.AppendString(encoded, "future-optional")
	decoded := &runtimev1.QueryRecordsRequest{}
	if err := proto.Unmarshal(encoded, decoded); err != nil {
		t.Fatal(err)
	}
	roundTrip, err := FromWireQueryRecordsRequest(ctx, decoded)
	if err != nil || !reflect.DeepEqual(roundTrip, request) {
		t.Fatalf("unknown optional request field: %#v, %v", roundTrip, err)
	}
	response := semantic.Response{Done: true, Receipts: semantic.Receipts{ProtocolVersion: "1.0", CapabilityVersion: semantic.CapabilityName}}
	wireResponse, err := ToWireQueryRecordsResponse(response)
	if err != nil {
		t.Fatal(err)
	}
	responseBytes, err := proto.Marshal(wireResponse)
	if err != nil {
		t.Fatal(err)
	}
	responseBytes = protowire.AppendTag(responseBytes, 100, protowire.BytesType)
	responseBytes = protowire.AppendString(responseBytes, "future-optional")
	decodedResponse := &runtimev1.QueryRecordsResponse{}
	if err := proto.Unmarshal(responseBytes, decodedResponse); err != nil {
		t.Fatal(err)
	}
	semanticResponse, err := FromWireQueryRecordsResponse(decodedResponse)
	if err != nil || !reflect.DeepEqual(semanticResponse, response) {
		t.Fatalf("unknown optional response field: %#v, %v", semanticResponse, err)
	}

	malformed := proto.Clone(wireRequest).(*runtimev1.QueryRecordsRequest)
	malformed.Filter = &runtimev1.QueryPredicate{
		Kind: runtimev1.QueryPredicateKind(999),
		Value: &runtimev1.QueryPredicate_FieldComparison{FieldComparison: &runtimev1.QueryFieldComparison{
			Field: "Id", Operator: runtimev1.QueryComparisonOperator_QUERY_COMPARISON_OPERATOR_EQUAL, Value: &runtimev1.QueryScalar{Value: &runtimev1.QueryScalar_StringValue{StringValue: "I-1"}},
		}},
	}
	if _, err := FromWireQueryRecordsRequest(ctx, malformed); err == nil {
		t.Fatal("unknown predicate enum silently weakened the filter")
	}
	malformed = proto.Clone(wireRequest).(*runtimev1.QueryRecordsRequest)
	malformed.OrderBy = []*runtimev1.QuerySort{nil}
	if _, err := FromWireQueryRecordsRequest(ctx, malformed); err == nil {
		t.Fatal("nil order-by entry was silently omitted")
	}
	malformed = proto.Clone(wireRequest).(*runtimev1.QueryRecordsRequest)
	malformed.Filter = &runtimev1.QueryPredicate{
		Kind: runtimev1.QueryPredicateKind_QUERY_PREDICATE_KIND_COMPARISON,
		Value: &runtimev1.QueryPredicate_Comparison{Comparison: &runtimev1.QueryComparison{
			Left:     &runtimev1.QueryOperand{Kind: runtimev1.QueryOperandKind_QUERY_OPERAND_KIND_FIELD, Value: &runtimev1.QueryOperand_Field{Field: "OwnerId"}},
			Operator: runtimev1.QueryComparisonOperator_QUERY_COMPARISON_OPERATOR_EQUAL,
			Right:    &runtimev1.QueryOperand{Kind: runtimev1.QueryOperandKind_QUERY_OPERAND_KIND_TRUSTED_CONTEXT, Value: &runtimev1.QueryOperand_Field{Field: "attacker"}},
		}},
	}
	if _, err := FromWireQueryRecordsRequest(ctx, malformed); err == nil {
		t.Fatal("trusted-context kind accepted a caller-controlled field arm")
	}
}

func TestQueryHistoryWireRoundTripPreservesNullAndCursorFacts(t *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	field := "Status"
	request := semantic.HistoryRequest{
		ParentObjectID: "WorkItem", SelectedFields: []string{"Field", "OldValue", "NewValue"},
		ParentRecordID: "W-1", Filter: semantic.Predicate{Kind: semantic.PredicateFieldComparison, Field: "Field", Operator: semantic.ComparisonEqual, Value: &semantic.Scalar{String: &field}},
		Limit: 100, Offset: 2, SortDescending: true, PageSize: 50, CursorOffset: 3,
	}
	wireRequest, err := ToWireQueryHistoryRequest(ctx, request)
	if err != nil {
		t.Fatal(err)
	}
	roundRequest, err := FromWireQueryHistoryRequest(ctx, wireRequest)
	if err != nil {
		t.Fatal(err)
	}
	if !reflect.DeepEqual(roundRequest, request) {
		t.Fatalf("history request round trip\n got %#v\nwant %#v", roundRequest, request)
	}
	newValue := "Closed"
	response := semantic.HistoryResponse{TotalSize: 1, Done: true, Receipts: semantic.Receipts{ProtocolVersion: "1.0", CapabilityVersion: semantic.CapabilityName}, Rows: []semantic.HistoryRow{{ID: "H-1", ParentID: "W-1", Field: "Status", OldValue: nil, NewValue: &newValue, DataType: "string", CreatedDate: "2026-07-19T00:00:00Z", CreatedByID: "U-1"}}}
	wireResponse, err := ToWireQueryHistoryResponse(response)
	if err != nil {
		t.Fatal(err)
	}
	roundResponse, err := FromWireQueryHistoryResponse(wireResponse)
	if err != nil {
		t.Fatal(err)
	}
	if !reflect.DeepEqual(roundResponse, response) {
		t.Fatalf("history response round trip\n got %#v\nwant %#v", roundResponse, response)
	}
}

func TestQueryWireDecodeEnforcesTreeBudgetBeforeSemanticConversion(t *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()

	t.Run("exact depth is accepted", func(t *testing.T) {
		request := minimalWireQueryRequest()
		request.Filter = wireNotChain(semantic.MaxNestingDepth - 1)
		if _, err := FromWireQueryRecordsRequest(ctx, request); err != nil {
			t.Fatalf("exact nesting depth rejected: %v", err)
		}
	})

	t.Run("deep predicate is rejected before recursive descent", func(t *testing.T) {
		request := minimalWireQueryRequest()
		request.Filter = wireNotChain(4096)
		_, err := FromWireQueryRecordsRequest(ctx, request)
		assertQueryFailureLimit(t, err, semantic.LimitQueryDepth, semantic.MaxNestingDepth)
	})

	t.Run("node budget is shared by filter and having", func(t *testing.T) {
		request := minimalWireQueryRequest()
		request.Filter = wireListExpression(127)
		request.Having = wireListExpression(127)
		_, err := FromWireQueryRecordsRequest(ctx, request)
		assertQueryFailureLimit(t, err, semantic.LimitQueryPredicate, semantic.MaxPredicateNodes)
	})

	t.Run("recursive response value is bounded before conversion", func(t *testing.T) {
		value := &runtimev1.QueryValue{
			DeclaredKind: runtimev1.QueryValueKind_QUERY_VALUE_KIND_STRING,
			Value:        &runtimev1.QueryValue_StringValue{StringValue: "leaf"},
		}
		for range 4096 {
			value = &runtimev1.QueryValue{
				DeclaredKind: runtimev1.QueryValueKind_QUERY_VALUE_KIND_LIST,
				Value:        &runtimev1.QueryValue_ListValue{ListValue: &runtimev1.QueryValues{Values: []*runtimev1.QueryValue{value}}},
			}
		}
		response := &runtimev1.QueryRecordsResponse{
			Done:     true,
			Records:  []*runtimev1.QueryRecord{{Fields: []*runtimev1.QueryField{{Name: "Nested", Value: value}}}},
			Receipts: queryReceiptsToWire(semantic.Receipts{ProtocolVersion: "1.0", CapabilityVersion: semantic.CapabilityName}),
		}
		_, err := FromWireQueryRecordsResponse(response)
		assertQueryFailureLimit(t, err, semantic.LimitQueryDepth, semantic.MaxNestingDepth)
	})
}

func TestQueryStatusOwnsSelectionComponent(t *testing.T) {
	wireErr := ToQueryStatusError(&semantic.Failure{
		Category:         semantic.FailureInvalidRequest,
		InvalidComponent: semantic.InvalidComponentSelection,
		CanonicalField:   "Owner",
	})
	if status.Code(wireErr) != codes.InvalidArgument {
		t.Fatalf("status = %s, want INVALID_ARGUMENT", status.Code(wireErr))
	}
	details := status.Convert(wireErr).Details()
	if len(details) != 1 {
		t.Fatalf("details = %#v", details)
	}
	detail, ok := details[0].(*commonv1.RuntimeErrorDetail)
	if !ok || detail.GetInvalidRequest().GetComponent() != commonv1.InvalidComponent_INVALID_COMPONENT_QUERY_SELECTION {
		t.Fatalf("query invalid component = %#v, want QUERY_SELECTION", detail)
	}
	if detail.GetInvalidRequest().GetComponent() == commonv1.InvalidComponent_INVALID_COMPONENT_SELECTED_ROOT_FIELD {
		t.Fatal("query selection failure leaked selected-root vocabulary")
	}
	var round *semantic.Failure
	if err := FromQueryStatusError(wireErr); !errors.As(err, &round) ||
		round.InvalidComponent != semantic.InvalidComponentSelection || round.CanonicalField != "Owner" {
		t.Fatalf("round status = %#v", err)
	}

	foreign := ToQueryStatusError(&semantic.Failure{
		Category:         semantic.FailureInvalidRequest,
		InvalidComponent: semantic.InvalidComponent("SELECTED_ROOT_FIELD"),
	})
	if status.Code(foreign) != codes.Internal {
		t.Fatalf("selected-root component encoded by query mapper as %s, want INTERNAL", status.Code(foreign))
	}
}

func minimalWireQueryRequest() *runtimev1.QueryRecordsRequest {
	return &runtimev1.QueryRecordsRequest{
		ObjectId: "Issue",
		Selections: []*runtimev1.QuerySelection{{
			Kind:  runtimev1.QuerySelectionKind_QUERY_SELECTION_KIND_FIELD,
			Field: "Id",
		}},
	}
}

func wireNotChain(notCount int) *runtimev1.QueryPredicate {
	predicate := &runtimev1.QueryPredicate{
		Kind: runtimev1.QueryPredicateKind_QUERY_PREDICATE_KIND_FIELD_COMPARISON,
		Value: &runtimev1.QueryPredicate_FieldComparison{FieldComparison: &runtimev1.QueryFieldComparison{
			Field: "Id", Operator: runtimev1.QueryComparisonOperator_QUERY_COMPARISON_OPERATOR_EQUAL,
			Value: &runtimev1.QueryScalar{Value: &runtimev1.QueryScalar_StringValue{StringValue: "I-1"}},
		}},
	}
	for range notCount {
		predicate = &runtimev1.QueryPredicate{
			Kind: runtimev1.QueryPredicateKind_QUERY_PREDICATE_KIND_NOT,
			Value: &runtimev1.QueryPredicate_Children{Children: &runtimev1.QueryPredicates{
				Values: []*runtimev1.QueryPredicate{predicate},
			}},
		}
	}
	return predicate
}

func wireListExpression(elements int) *runtimev1.QueryPredicate {
	values := make([]*runtimev1.QueryOperand, 0, elements)
	for range elements {
		values = append(values, &runtimev1.QueryOperand{
			Kind: runtimev1.QueryOperandKind_QUERY_OPERAND_KIND_LITERAL,
			Value: &runtimev1.QueryOperand_Literal{Literal: &runtimev1.QueryScalar{
				Value: &runtimev1.QueryScalar_StringValue{StringValue: "x"},
			}},
		})
	}
	return &runtimev1.QueryPredicate{
		Kind: runtimev1.QueryPredicateKind_QUERY_PREDICATE_KIND_EXPRESSION,
		Value: &runtimev1.QueryPredicate_Expression{Expression: &runtimev1.QueryOperand{
			Kind:  runtimev1.QueryOperandKind_QUERY_OPERAND_KIND_LIST,
			Value: &runtimev1.QueryOperand_List{List: &runtimev1.QueryOperands{Values: values}},
		}},
	}
}

func assertQueryFailureLimit(t *testing.T, err error, kind semantic.LimitKind, maximum int) {
	t.Helper()
	var failure *semantic.Failure
	if !errors.As(err, &failure) || failure.Category != semantic.FailureResourceExhausted ||
		failure.LimitKind != kind || failure.Maximum != uint64(maximum) {
		t.Fatalf("failure = %#v, want RESOURCE_EXHAUSTED %s maximum %d", err, kind, maximum)
	}
}
