package compatibility_test

import (
	"context"
	"errors"
	"sync/atomic"
	"testing"
	"time"

	semantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/recyclebinv1"
	commonv1 "github.com/pingcap-inc/nexuscrm-contract/wire-go/gen/common/v1"
	runtimev1 "github.com/pingcap-inc/nexuscrm-contract/wire-go/gen/runtime/v1"
	"github.com/pingcap-inc/nexuscrm-contract/wire-go/mapping"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

func TestGeneratedRecycleBinClientUsesOneCoarseRPC(t *testing.T) {
	client, service, cleanup := startRecycleBinTestServer(t)
	defer cleanup()
	ctx, cancel := context.WithTimeout(withCapability(context.Background(), semantic.CapabilityName), time.Second)
	defer cancel()

	response, err := client.ListRecycleBin(ctx, &runtimev1.ListRecycleBinRequest{Scope: runtimev1.RecycleBinScope_RECYCLE_BIN_SCOPE_ALL})
	if err != nil || service.calls.Load() != 1 || len(response.GetItems()) != 1 || len(response.GetFailedObjects()) != 1 {
		t.Fatalf("response=%#v err=%v calls=%d", response, err, service.calls.Load())
	}
	if response.GetItems()[0].GetRecordId() != "row-1" || response.GetFailedObjects()[0].GetCode() != runtimev1.RecycleBinObjectFailureCode_RECYCLE_BIN_OBJECT_FAILURE_CODE_FORBIDDEN {
		t.Fatalf("projection=%#v", response)
	}
}

func TestGeneratedRecycleBinClientFailsClosedOnCapabilityMismatch(t *testing.T) {
	client, service, cleanup := startRecycleBinTestServer(t)
	defer cleanup()
	ctx, cancel := context.WithTimeout(withProtocol(context.Background()), time.Second)
	defer cancel()

	response, err := client.ListRecycleBin(ctx, &runtimev1.ListRecycleBinRequest{Scope: runtimev1.RecycleBinScope_RECYCLE_BIN_SCOPE_MINE})
	if response != nil || status.Code(err) != codes.FailedPrecondition || service.calls.Load() != 0 {
		t.Fatalf("response=%#v error=%v calls=%d", response, err, service.calls.Load())
	}
	var failure *semantic.Failure
	if decoded := mapping.FromRecycleBinStatusError(err); !errors.As(decoded, &failure) || failure.Category != semantic.FailureIncompatible {
		t.Fatalf("decoded=%#v", decoded)
	}
}

type testRecycleBinService struct {
	runtimev1.UnimplementedRecycleBinReadServiceServer
	calls atomic.Int64
}

func (s *testRecycleBinService) ListRecycleBin(context.Context, *runtimev1.ListRecycleBinRequest) (*runtimev1.ListRecycleBinResponse, error) {
	s.calls.Add(1)
	return &runtimev1.ListRecycleBinResponse{
		Items: []*runtimev1.RecycleBinItem{{
			RecordId: "row-1", ObjectId: "WorkItem", DeletedDate: "2026-07-22T04:00:00Z",
		}},
		FailedObjects: []*runtimev1.RecycleBinObjectFailure{{
			ObjectId: "RestrictedItem", Code: runtimev1.RecycleBinObjectFailureCode_RECYCLE_BIN_OBJECT_FAILURE_CODE_FORBIDDEN,
		}},
		Receipts: &commonv1.ConsistencyReceipts{
			ProtocolVersion:   &commonv1.ProtocolVersion{Major: semantic.ProtocolMajor, Minor: semantic.ProtocolMinor},
			CapabilityVersion: &commonv1.CapabilityVersion{Name: semantic.CapabilityName, Major: 1},
		},
	}, nil
}

func startRecycleBinTestServer(t *testing.T) (runtimev1.RecycleBinReadServiceClient, *testRecycleBinService, func()) {
	t.Helper()
	service := &testRecycleBinService{}
	connection, cleanup := startBufconnServer(t, func(server *grpc.Server) {
		runtimev1.RegisterRecycleBinReadServiceServer(server, service)
	})
	return runtimev1.NewRecycleBinReadServiceClient(connection), service, cleanup
}
