package mapping

import (
	"context"
	"testing"
	"time"

	semantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/auditlogv1"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

func TestAuditLogMappingRoundTrip(t *testing.T) {
	oldValue, newValue := "old", "new"
	response := semantic.Response{
		Rows: []semantic.Row{{ID: "row-1", ObjectID: "Account", RecordID: "001", FieldID: "Name",
			OldValue: &oldValue, NewValue: &newValue, ChangedByID: "005", ChangedAt: "2026-08-14T12:00:00Z", Action: "UPDATE"}},
		Receipts: semantic.Receipts{ProtocolVersion: "1.0", CapabilityVersion: semantic.CapabilityName},
	}
	request := semantic.Request{ObjectID: "Account", RecordID: "001", Limit: 100}
	wire, err := ToWireReadAuditLogResponse(request, response)
	if err != nil {
		t.Fatal(err)
	}
	roundTrip, err := FromWireReadAuditLogResponse(request, wire)
	if err != nil {
		t.Fatal(err)
	}
	if len(roundTrip.Rows) != 1 || roundTrip.Rows[0].Action != "UPDATE" || *roundTrip.Rows[0].NewValue != "new" {
		t.Fatalf("round trip = %#v", roundTrip)
	}
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	wireRequest, err := ToWireReadAuditLogRequest(ctx, request)
	if err != nil {
		t.Fatal(err)
	}
	roundRequest, err := FromWireReadAuditLogRequest(ctx, wireRequest)
	if err != nil || roundRequest.ObjectID != "Account" {
		t.Fatalf("request = %#v err=%v", roundRequest, err)
	}
}

func TestAuditLogMappingRejectsResponseOutsideRequest(t *testing.T) {
	request := semantic.Request{ObjectID: "Account", RecordID: "001", Limit: 1}
	response := semantic.Response{
		Rows: []semantic.Row{{
			ID: "row-1", ObjectID: "Contact", RecordID: "003", FieldID: "Name",
			ChangedByID: "005", ChangedAt: "2026-08-14T12:00:00Z",
		}},
		Receipts: semantic.Receipts{ProtocolVersion: "1.0", CapabilityVersion: semantic.CapabilityName},
	}
	if _, err := ToWireReadAuditLogResponse(request, response); err == nil {
		t.Fatal("response outside requested record crossed semantic-to-wire mapping")
	}

	response.Rows[0].ObjectID, response.Rows[0].RecordID = request.ObjectID, request.RecordID
	wire, err := ToWireReadAuditLogResponse(request, response)
	if err != nil {
		t.Fatal(err)
	}
	wire.Rows[0].RecordId = "002"
	if _, err := FromWireReadAuditLogResponse(request, wire); err == nil {
		t.Fatal("response outside requested record crossed wire-to-semantic mapping")
	}
}

func TestAuditLogPlatformStatusPreservesClosedFailures(t *testing.T) {
	for _, category := range []semantic.FailureCategory{
		semantic.FailureForbidden, semantic.FailureUnavailable, semantic.FailureInternal,
	} {
		decoded := FromPlatformStatusError(
			ToPlatformStatusError(&semantic.Failure{Category: category}, semantic.CapabilityName),
			semantic.CapabilityName,
		)
		failure, ok := decoded.(*semantic.Failure)
		if !ok || failure.Category != category {
			t.Fatalf("category %q decoded as %#v", category, decoded)
		}
	}
	if got := status.Code(ToPlatformStatusError(
		&semantic.Failure{Category: semantic.FailureForbidden}, semantic.CapabilityName,
	)); got != codes.PermissionDenied {
		t.Fatalf("status = %v, want PermissionDenied", got)
	}
	if got := status.Code(ToPlatformStatusError(
		&semantic.Failure{Category: "MISSING"}, semantic.CapabilityName,
	)); got != codes.Internal {
		t.Fatalf("unsupported MISSING status = %v, want Internal", got)
	}
	decoded := FromPlatformStatusError(status.Error(codes.NotFound, "must not disclose"), semantic.CapabilityName)
	if failure, ok := decoded.(*semantic.Failure); !ok || failure.Category != semantic.FailureInternal {
		t.Fatalf("unsupported NotFound decoded as %#v", decoded)
	}
}
