package queryv1

import (
	"context"
	"encoding/json"
	"errors"
	"reflect"
	"strings"
	"testing"
	"time"
)

func TestQueryRequestAndResponseSemanticMatrix(t *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	falseValue, zero, empty := false, int64(0), ""
	request := Request{
		ObjectID: "Issue", Selections: []Selection{{Kind: SelectionField, Field: "Active"}, {Kind: SelectionField, Field: "Amount"}, {Kind: SelectionField, Field: "Name"}}, Limit: 200,
		Filter: Predicate{Kind: PredicateAnd, Children: []Predicate{
			{Kind: PredicateFieldComparison, Field: "Active", Operator: ComparisonEqual, Value: &Scalar{Boolean: &falseValue}},
			{Kind: PredicateFieldComparison, Field: "Amount", Operator: ComparisonEqual, Value: &Scalar{Integer: &zero}},
		}},
	}
	if err := ValidateRequest(ctx, request); err != nil {
		t.Fatalf("ValidateRequest: %v", err)
	}
	values := []Value{{Kind: ValueBoolean, Boolean: &falseValue}, {Kind: ValueInteger, Integer: &zero}, {Kind: ValueString, String: &empty}, {Kind: ValueDecimal, Decimal: &Decimal{Coefficient: "0", Scale: 4}}, {Kind: ValueString, Null: true}}
	response := Response{TotalSize: 1, Done: true, Receipts: Receipts{ProtocolVersion: "1.0", CapabilityVersion: CapabilityName}, Records: []Record{{ObjectID: "Issue", Fields: []Field{{Name: "Active", Value: values[0]}, {Name: "Amount", Value: values[1]}, {Name: "Budget", Value: values[3]}, {Name: "Name", Value: values[2]}, {Name: "Nullable", Value: values[4]}}}}}
	if err := ValidateResponse(response); err != nil {
		t.Fatalf("ValidateResponse: %v", err)
	}
	// Declared-but-absent and FLS-hidden fields are both represented by no Field entry.
	if len(response.Records[0].Fields) != 5 {
		t.Fatalf("fields = %d", len(response.Records[0].Fields))
	}
}

func TestImmutableNeutralConformanceVectors(t *testing.T) {
	var document struct {
		Schema  string `json:"schema_version"`
		Vectors []struct {
			Name            string          `json:"name"`
			Operation       string          `json:"operation"`
			Request         json.RawMessage `json:"request"`
			Response        json.RawMessage `json:"response"`
			HistoryRequest  json.RawMessage `json:"history_request"`
			HistoryResponse json.RawMessage `json:"history_response"`
			Failure         *struct {
				Category FailureCategory  `json:"category"`
				Retry    RetryDisposition `json:"retry"`
			} `json:"failure"`
		} `json:"vectors"`
	}
	if err := json.Unmarshal(ConformanceVectorsJSON(), &document); err != nil {
		t.Fatal(err)
	}
	if document.Schema != "nexuscrm.runtime.record-query.conformance/v1" || len(document.Vectors) < 5 {
		t.Fatalf("schema=%q vectors=%d", document.Schema, len(document.Vectors))
	}
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	for _, vector := range document.Vectors {
		t.Run(vector.Name, func(t *testing.T) {
			switch vector.Operation {
			case "QUERY_RECORDS":
				var request Request
				if err := json.Unmarshal(vector.Request, &request); err != nil {
					t.Fatal(err)
				}
				if err := ValidateRequest(ctx, request); err != nil {
					t.Fatal(err)
				}
				if len(vector.Response) != 0 {
					var response Response
					if err := json.Unmarshal(vector.Response, &response); err != nil {
						t.Fatal(err)
					}
					if err := ValidateResponse(response); err != nil {
						t.Fatal(err)
					}
				}
			case "QUERY_HISTORY":
				var request HistoryRequest
				if err := json.Unmarshal(vector.HistoryRequest, &request); err != nil {
					t.Fatal(err)
				}
				if err := ValidateHistoryRequest(ctx, request); err != nil {
					t.Fatal(err)
				}
				var response HistoryResponse
				if err := json.Unmarshal(vector.HistoryResponse, &response); err != nil {
					t.Fatal(err)
				}
				if err := ValidateHistoryResponse(response); err != nil {
					t.Fatal(err)
				}
			default:
				t.Fatalf("unknown operation %q", vector.Operation)
			}
			if vector.Failure != nil {
				if !ValidTopLevelFailure(vector.Failure.Category) || RetryDispositionFor(vector.Failure.Category) != vector.Failure.Retry {
					t.Fatalf("failure=%#v", vector.Failure)
				}
			}
		})
	}
	copyData := ConformanceVectorsJSON()
	copyData[0] ^= 0xff
	if reflect.DeepEqual(copyData, ConformanceVectorsJSON()) {
		t.Fatal("ConformanceVectorsJSON returned shared mutable storage")
	}
}

func TestQueryBoundsAndCancellationFailBeforeExecution(t *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	var failure *Failure
	if err := ValidateRequest(ctx, Request{ObjectID: "Issue"}); !errors.As(err, &failure) ||
		failure.Category != FailureInvalidRequest || failure.InvalidComponent != InvalidComponentSelection {
		t.Fatalf("empty selection failure = %#v, want QUERY_SELECTION", err)
	}
	if InvalidComponentSelection == InvalidComponentField || InvalidComponentField != InvalidComponent("SELECTED_ROOT_FIELD") {
		t.Fatalf("query selection ownership or compatibility value changed: canonical=%q legacy=%q", InvalidComponentSelection, InvalidComponentField)
	}

	request := Request{ObjectID: "Issue", Selections: []Selection{{Kind: SelectionField, Field: "Id"}}}
	request.Selections = make([]Selection, MaxSelectedFields+1)
	for i := range request.Selections {
		request.Selections[i] = Selection{Kind: SelectionField, Field: "Id"}
	}
	if err := ValidateRequest(ctx, request); !errors.As(err, &failure) || failure.LimitKind != LimitSelectedFields {
		t.Fatalf("selected-field bound = %#v", err)
	}

	canceled, stop := context.WithCancel(context.Background())
	stop()
	if err := ValidateRequest(canceled, Request{}); !errors.As(err, &failure) || failure.Category != FailureCanceled {
		t.Fatalf("canceled = %#v", err)
	}

	response := Response{Done: true, Receipts: Receipts{ProtocolVersion: "1.0", CapabilityVersion: CapabilityName}, Records: make([]Record, MaxRows+1)}
	if err := ValidateResponse(response); !errors.As(err, &failure) || failure.LimitKind != LimitRecordCount {
		t.Fatalf("row bound = %#v", err)
	}

	includeDeleted := Request{ObjectID: "Issue", Selections: []Selection{{Kind: SelectionField, Field: "Id"}}, IncludeDeleted: true, Limit: MaxRows + 1}
	if err := ValidateRequest(ctx, includeDeleted); !errors.As(err, &failure) || failure.LimitKind != LimitRecordCount {
		t.Fatalf("include-deleted limit = %#v", err)
	}
}

func TestQueryResponseRequiresCanonicalFieldsAndExactCarrier(t *testing.T) {
	response := Response{Done: true, Receipts: Receipts{ProtocolVersion: "1.0", CapabilityVersion: CapabilityName}, Records: []Record{{Fields: []Field{{Name: "Zed", Value: Value{Kind: ValueString, Null: true}}, {Name: "Alpha", Value: Value{Kind: ValueString, Null: true}}}}}}
	if err := ValidateResponse(response); err == nil {
		t.Fatal("non-canonical field order accepted")
	}
	response.Records[0].Fields = []Field{{Name: "Amount", Value: Value{Kind: ValueDecimal, Decimal: &Decimal{Coefficient: "01", Scale: 2}}}}
	if err := ValidateResponse(response); err == nil {
		t.Fatal("non-canonical decimal accepted")
	}
	response.Records[0].Fields[0].Value.Decimal.Coefficient = strings.Repeat("9", 80)
	if err := ValidateResponse(response); err != nil {
		t.Fatalf("large exact coefficient: %v", err)
	}
	response.Records[0].Fields[0].Value.Decimal.Scale = MaxSemanticResponseBytes
	if err := ValidateResponse(response); err == nil {
		t.Fatal("decimal expansion beyond the response envelope was accepted")
	}
}

func TestQueryResponseDistinguishesNamedRecordsFromAnonymousValueObjects(t *testing.T) {
	empty := ""
	falseValue := false
	zero := int64(0)
	longKey := strings.Repeat("a", MaxFieldIdentifierBytes+1)
	anonymous := Record{Fields: []Field{
		{Name: "", Value: Value{Kind: ValueString, Null: true}},
		{Name: "a-b", Value: Value{Kind: ValueBoolean, Boolean: &falseValue}},
		{Name: longKey, Value: Value{Kind: ValueInteger, Integer: &zero}},
		{Name: "日本語", Value: Value{Kind: ValueString, String: &empty}},
	}}
	response := Response{
		Done:     true,
		Receipts: Receipts{ProtocolVersion: "1.0", CapabilityVersion: CapabilityName},
		Records: []Record{{Fields: []Field{{
			Name:  "Payload",
			Value: Value{Kind: ValueObject, Object: &anonymous},
		}}}},
	}
	if err := ValidateResponse(response); err != nil {
		t.Fatalf("anonymous value object rejected: %v", err)
	}
	encoded, err := json.Marshal(response)
	if err != nil {
		t.Fatal(err)
	}
	var roundTrip Response
	if err := json.Unmarshal(encoded, &roundTrip); err != nil {
		t.Fatal(err)
	}
	if !reflect.DeepEqual(roundTrip, response) {
		t.Fatalf("anonymous value object round trip\n got %#v\nwant %#v", roundTrip, response)
	}

	named := anonymous
	named.ObjectID = "Issue"
	response.Records[0].Fields[0].Value.Object = &named
	if err := ValidateResponse(response); err == nil {
		t.Fatal("named record accepted non-metadata field keys")
	}

	duplicate := anonymous
	duplicate.Fields = append([]Field{anonymous.Fields[0]}, anonymous.Fields...)
	response.Records[0].Fields[0].Value.Object = &duplicate
	if err := ValidateResponse(response); err == nil {
		t.Fatal("anonymous value object accepted duplicate keys")
	}

	invalidUTF8 := anonymous
	invalidUTF8.Fields = []Field{{Name: string([]byte{0xff}), Value: Value{Kind: ValueString, Null: true}}}
	response.Records[0].Fields[0].Value.Object = &invalidUTF8
	if err := ValidateResponse(response); err == nil {
		t.Fatal("anonymous value object accepted invalid UTF-8 key")
	}
}

func TestQueryPlanIsClosedAndTyped(t *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	base := Request{
		ObjectID: "Issue",
		Selections: []Selection{
			{Kind: SelectionField, Field: "Owner.Name"},
			{Kind: SelectionAggregate, Aggregate: AggregateCount, Field: "Id", Alias: "total"},
		},
		GroupBy: "Owner.Name",
		OrderBy: []Sort{{Field: "Owner.Name", Direction: SortAscending}},
		Filter: Predicate{Kind: PredicateComparison, Operator: ComparisonEqual,
			Left:  Operand{Kind: OperandFunction, Function: FunctionTemporalPart, TemporalBasis: TemporalBasisCivil, TemporalPart: TemporalPartDate, Args: []Operand{{Kind: OperandField, Field: "CreatedDate"}}},
			Right: Operand{Kind: OperandLiteral, Literal: &Scalar{String: stringPointer("2026-07-19")}}},
	}
	if err := ValidateRequest(ctx, base); err != nil {
		t.Fatalf("closed typed plan rejected: %v", err)
	}
	trusted := base
	trusted.Filter = Predicate{Kind: PredicateComparison, Operator: ComparisonEqual,
		Left:  Operand{Kind: OperandField, Field: "recipient_id"},
		Right: Operand{Kind: OperandTrustedContext, TrustedContext: TrustedContextPrincipalID}}
	if err := ValidateRequest(ctx, trusted); err != nil {
		t.Fatalf("trusted principal operand rejected: %v", err)
	}
	invalid := trusted
	invalid.Filter.Right.TrustedContext = TrustedContextValue("ORG_ID")
	if err := ValidateRequest(ctx, invalid); err == nil {
		t.Fatal("unknown trusted context value accepted")
	}
	invalid = trusted
	invalid.Filter.Right.Field = "caller_supplied"
	if err := ValidateRequest(ctx, invalid); err == nil {
		t.Fatal("trusted context operand accepted caller payload")
	}

	invalid = base
	invalid.Selections = []Selection{{Kind: SelectionField, Field: "COUNT(Id)"}}
	if err := ValidateRequest(ctx, invalid); err == nil {
		t.Fatal("opaque select expression accepted")
	}
	invalid = base
	invalid.Filter.Operator = ComparisonOperator("DROP_TABLE")
	if err := ValidateRequest(ctx, invalid); err == nil {
		t.Fatal("unknown comparison operator accepted")
	}
	invalid = base
	invalid.Filter.Operator = ComparisonEqual
	invalid.Filter.Left.Function = Function("PRODUCT_FUNCTION")
	if err := ValidateRequest(ctx, invalid); err == nil {
		t.Fatal("unknown function accepted")
	}
	invalid = base
	invalid.Filter.Left.TemporalPart = ""
	if err := ValidateRequest(ctx, invalid); err == nil {
		t.Fatal("temporal operation without a typed part accepted")
	}
	invalid = base
	invalid.Filter.Children = []Predicate{{Kind: PredicateExpression, Operand: Operand{Kind: OperandLiteral, Literal: &Scalar{Boolean: boolPointer(true)}}}}
	if err := ValidateRequest(ctx, invalid); err == nil {
		t.Fatal("ambiguous predicate arms accepted")
	}
}

func TestDecimalExpansionBudgetIsCompletePageBudget(t *testing.T) {
	decimal := Value{Kind: ValueDecimal, Decimal: &Decimal{Coefficient: "1", Scale: MaxSemanticResponseBytes / 2}}
	response := Response{
		Done:     true,
		Receipts: Receipts{ProtocolVersion: "1.0", CapabilityVersion: CapabilityName},
		Records:  []Record{{Fields: []Field{{Name: "First", Value: decimal}, {Name: "Second", Value: decimal}}}},
	}
	var failure *Failure
	if err := ValidateResponse(response); !errors.As(err, &failure) || failure.LimitKind != LimitSemanticResponse {
		t.Fatalf("aggregate decimal expansion error = %#v", err)
	}

	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	requestDecimal := Decimal{Coefficient: "1", Scale: MaxSemanticRequestBytes}
	request := Request{
		ObjectID:   "Issue",
		Selections: []Selection{{Kind: SelectionField, Field: "Id"}},
		Filter:     Predicate{Kind: PredicateFieldComparison, Field: "Amount", Operator: ComparisonEqual, Value: &Scalar{Decimal: &requestDecimal}},
	}
	if err := ValidateRequest(ctx, request); !errors.As(err, &failure) || failure.LimitKind != LimitSemanticRequest {
		t.Fatalf("request decimal expansion error = %#v", err)
	}
}

func stringPointer(value string) *string { return &value }
func boolPointer(value bool) *bool       { return &value }

func TestHistoryRequestAndResponseBounds(t *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	request := HistoryRequest{ParentObjectID: "Issue", SelectedFields: []string{"Field", "OldValue"}, PageSize: 200}
	if err := ValidateHistoryRequest(ctx, request); err != nil {
		t.Fatal(err)
	}
	tooMany := request
	tooMany.PageSize = MaxHistoryRows + 1
	var failure *Failure
	if err := ValidateHistoryRequest(ctx, tooMany); !errors.As(err, &failure) || failure.LimitKind != LimitRecordCount {
		t.Fatalf("history page bound error = %#v", err)
	}
	decimal := Decimal{Coefficient: "1", Scale: MaxSemanticRequestBytes}
	oversized := request
	oversized.Filter = Predicate{Kind: PredicateFieldComparison, Field: "Amount", Operator: ComparisonEqual, Value: &Scalar{Decimal: &decimal}}
	if err := ValidateHistoryRequest(ctx, oversized); !errors.As(err, &failure) || failure.LimitKind != LimitSemanticRequest {
		t.Fatalf("history decimal expansion error = %#v", err)
	}
	response := HistoryResponse{Done: true, Receipts: Receipts{ProtocolVersion: "1.0", CapabilityVersion: CapabilityName}, Rows: []HistoryRow{{ID: "H-1", ParentID: "ISS-1", Field: "Status", DataType: "string", CreatedDate: "2026-07-19T00:00:00Z", CreatedByID: "U-1"}}}
	if err := ValidateHistoryResponse(response); err != nil {
		t.Fatal(err)
	}
}
