package sfcrmappservice

import (
	"context"
	"errors"
	"net"
	"net/http"
	"net/http/httptest"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/pingcap-inc/nexuscrm-api-host/runtimegrpc"
	approvalsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/approvalv1"
	commonsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/commonv1"
	identitysemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/identityv1"
	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"
)

type approvalGRPCServer struct {
	runtimev1.UnimplementedIdentityContextServiceServer
	runtimev1.UnimplementedApprovalServiceServer

	mu               sync.Mutex
	identityCalls    int
	executeCalls     int
	readCalls        int
	executeRequests  []approvalsemantic.ExecuteRequest
	executeFailure   *commonsemantic.Failure
	executeRejection *approvalsemantic.SubmitRejection
}

func (server *approvalGRPCServer) Exchange(
	ctx context.Context,
	request *runtimev1.ExchangeRequest,
) (*runtimev1.ExchangeResponse, error) {
	if err := requireRecordFilesMetadata(ctx, true, false); err != nil {
		return nil, err
	}
	semanticRequest, err := mapping.FromWireIdentityExchangeRequest(ctx, request)
	if err != nil {
		return nil, mapping.ToIdentityStatusError(err)
	}
	if len(semanticRequest.RequestedCapabilities) != 1 ||
		(semanticRequest.RequestedCapabilities[0] != approvalsemantic.ExecuteCapabilityName &&
			semanticRequest.RequestedCapabilities[0] != approvalsemantic.ReadCapabilityName) {
		return nil, errors.New("unexpected delegated Approval capability")
	}
	server.mu.Lock()
	server.identityCalls++
	server.mu.Unlock()
	return mapping.ToWireIdentityExchangeResponse(identitysemantic.Response{
		OpaqueDelegation:    "header.payload.signature",
		ExpiresAtUnixSecond: time.Now().Add(time.Minute).Unix(),
	})
}

func (server *approvalGRPCServer) ExecuteApproval(
	ctx context.Context,
	request *runtimev1.ExecuteApprovalRequest,
) (*runtimev1.ExecuteApprovalResponse, error) {
	if err := requireRecordFilesMetadata(ctx, false, true); err != nil {
		return nil, err
	}
	semanticRequest, err := mapping.FromWireExecuteApprovalRequest(ctx, request)
	if err != nil {
		return nil, mapping.ToPlatformStatusError(err, approvalsemantic.ExecuteCapabilityName)
	}
	server.mu.Lock()
	server.executeCalls++
	server.executeRequests = append(server.executeRequests, semanticRequest)
	executeFailure := server.executeFailure
	executeRejection := server.executeRejection
	server.mu.Unlock()
	if executeFailure != nil {
		return nil, mapping.ToPlatformStatusError(
			executeFailure, approvalsemantic.ExecuteCapabilityName,
		)
	}
	if executeRejection != nil {
		rejection := *executeRejection
		rejection.ValidationIssues = append(
			[]approvalsemantic.ValidationIssue(nil),
			executeRejection.ValidationIssues...,
		)
		return mapping.ToWireExecuteApprovalResponse(
			semanticRequest,
			approvalsemantic.ExecuteResponse{
				TargetRecordID:  semanticRequest.TargetRecordID,
				SubmitRejection: &rejection,
				Receipts:        approvalReceipts(approvalsemantic.ExecuteCapabilityName),
			},
		)
	}
	return mapping.ToWireExecuteApprovalResponse(
		semanticRequest,
		approvalExecuteResponse(semanticRequest),
	)
}

func (server *approvalGRPCServer) ReadApproval(
	ctx context.Context,
	request *runtimev1.ReadApprovalRequest,
) (*runtimev1.ReadApprovalResponse, error) {
	if err := requireRecordFilesMetadata(ctx, false, true); err != nil {
		return nil, err
	}
	semanticRequest, err := mapping.FromWireReadApprovalRequest(ctx, request)
	if err != nil {
		return nil, mapping.ToPlatformStatusError(err, approvalsemantic.ReadCapabilityName)
	}
	server.mu.Lock()
	server.readCalls++
	server.mu.Unlock()
	return mapping.ToWireReadApprovalResponse(
		semanticRequest,
		approvalsemantic.ReadResponse{
			Pending: []approvalsemantic.PendingItem{{
				WorkItemID: "work-grpc", InstanceID: "instance-grpc",
				DefinitionID: "definition-grpc", TargetRecordID: "record-grpc",
				ObjectID: "Account", SubmittedByID: "submitter-grpc",
				SubmittedAt: "2026-08-03T00:00:00Z", ActorID: "actor-grpc",
			}},
			Receipts: approvalReceipts(approvalsemantic.ReadCapabilityName),
		},
	)
}

func TestApprovalRoutesUseProductionPlatformGRPCBoundary(t *testing.T) {
	target, caFile, server, cleanup := startApprovalGRPCServer(t)
	defer cleanup()
	startupCtx, cancelStartup := context.WithTimeout(context.Background(), 2*time.Second)
	defer cancelStartup()
	platform, err := runtimegrpc.DialPlatformRuntime(startupCtx, runtimegrpc.RuntimeClientConfig{
		Target: target, AppInstallationID: "01900000-0000-7000-8000-000000000001",
		WorkloadToken: "workload-secret", CAFile: caFile, ServerName: "runtime.test",
		AttemptTimeout: time.Second,
	})
	if err != nil {
		t.Fatal(err)
	}
	defer platform.Close()
	service := approvalTestService(t, platform.ExecuteApproval, platform.ReadApproval)

	executeRequest := approvalHTTPRequest(
		http.MethodPost, "/services/data/v63.0/process/approvals",
		`{"ObjectApiName":"Account","RecordId":"record-1"}`,
	)
	executeRequest.Header.Set(crmMutationIdempotencyHeader, "grpc-approval")
	executeRecorder := httptest.NewRecorder()
	service.ServeHTTP(executeRecorder, executeRequest)
	if executeRecorder.Code != http.StatusOK {
		t.Fatalf("execute status=%d body=%s", executeRecorder.Code, executeRecorder.Body.String())
	}

	readRecorder := httptest.NewRecorder()
	service.ServeHTTP(readRecorder, approvalHTTPRequest(
		http.MethodGet, "/services/data/v63.0/process/approvals/pending", "",
	))
	if readRecorder.Code != http.StatusOK {
		t.Fatalf("read status=%d body=%s", readRecorder.Code, readRecorder.Body.String())
	}

	server.mu.Lock()
	defer server.mu.Unlock()
	if server.identityCalls != 2 || server.executeCalls != 1 || server.readCalls != 1 ||
		len(server.executeRequests) != 1 ||
		server.executeRequests[0].ObjectID != "Account" ||
		server.executeRequests[0].TargetRecordID != "record-1" {
		t.Fatalf(
			"identity/execute/read=%d/%d/%d requests=%#v",
			server.identityCalls, server.executeCalls, server.readCalls,
			server.executeRequests,
		)
	}
}

func TestApprovalSubmitConflictSurvivesProductionPlatformGRPCBoundary(t *testing.T) {
	target, caFile, server, cleanup := startApprovalGRPCServer(t)
	defer cleanup()
	server.mu.Lock()
	server.executeFailure = &commonsemantic.Failure{
		Category:         commonsemantic.FailureInvalidRequest,
		InvalidComponent: commonsemantic.InvalidComponentOperation,
	}
	server.mu.Unlock()

	startupCtx, cancelStartup := context.WithTimeout(context.Background(), 2*time.Second)
	defer cancelStartup()
	platform, err := runtimegrpc.DialPlatformRuntime(startupCtx, runtimegrpc.RuntimeClientConfig{
		Target: target, AppInstallationID: "01900000-0000-7000-8000-000000000001",
		WorkloadToken: "workload-secret", CAFile: caFile, ServerName: "runtime.test",
		AttemptTimeout: time.Second,
	})
	if err != nil {
		t.Fatal(err)
	}
	defer platform.Close()
	service := approvalTestService(t, platform.ExecuteApproval, platform.ReadApproval)

	request := approvalHTTPRequest(
		http.MethodPost, "/services/data/v63.0/process/approvals",
		`{"ObjectApiName":"Account","RecordId":"record-1"}`,
	)
	request.Header.Set(crmMutationIdempotencyHeader, "grpc-approval-conflict")
	recorder := httptest.NewRecorder()
	service.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusConflict ||
		!strings.Contains(recorder.Body.String(), `"errorCode":"CONFLICT"`) ||
		!strings.Contains(recorder.Body.String(), approvalAlreadyPendingMessage) {
		t.Fatalf("status=%d body=%s", recorder.Code, recorder.Body.String())
	}

	server.mu.Lock()
	defer server.mu.Unlock()
	if server.identityCalls != 1 || server.executeCalls != 1 || server.readCalls != 0 {
		t.Fatalf(
			"identity/execute/read=%d/%d/%d",
			server.identityCalls, server.executeCalls, server.readCalls,
		)
	}
}

func TestApprovalValidationRejectionSurvivesProductionPlatformGRPCBoundary(t *testing.T) {
	target, caFile, server, cleanup := startApprovalGRPCServer(t)
	defer cleanup()
	server.mu.Lock()
	server.executeRejection = &approvalsemantic.SubmitRejection{
		Reason: approvalsemantic.SubmitRejectionRecordValidationFailed,
		ValidationIssues: []approvalsemantic.ValidationIssue{
			{
				RuleName: "Quote_Submit_Requires_Complete_Lines",
				Message:  "Cannot submit: the quote needs at least one complete line item.",
			},
			{
				RuleName: "Quote_Submit_Requires_Service_Dates",
				Field:    "EndDate",
				Message:  "Cannot submit: complete the quote service dates.",
			},
		},
	}
	server.mu.Unlock()

	startupCtx, cancelStartup := context.WithTimeout(context.Background(), 2*time.Second)
	defer cancelStartup()
	platform, err := runtimegrpc.DialPlatformRuntime(startupCtx, runtimegrpc.RuntimeClientConfig{
		Target: target, AppInstallationID: "01900000-0000-7000-8000-000000000001",
		WorkloadToken: "workload-secret", CAFile: caFile, ServerName: "runtime.test",
		AttemptTimeout: time.Second,
	})
	if err != nil {
		t.Fatal(err)
	}
	defer platform.Close()
	service := approvalTestService(t, platform.ExecuteApproval, platform.ReadApproval)

	request := approvalHTTPRequest(
		http.MethodPost, "/services/data/v63.0/process/approvals",
		`{"ObjectApiName":"Account","RecordId":"record-1"}`,
	)
	request.Header.Set(crmMutationIdempotencyHeader, "grpc-approval-rejection")
	recorder := httptest.NewRecorder()
	service.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK ||
		!strings.Contains(recorder.Body.String(), `"entityId":"record-1"`) ||
		!strings.Contains(recorder.Body.String(), `"errors":["Cannot submit: the quote needs at least one complete line item.","Cannot submit: complete the quote service dates."]`) ||
		!strings.Contains(recorder.Body.String(), `"instanceStatus":"Error"`) ||
		!strings.Contains(recorder.Body.String(), `"success":false`) {
		t.Fatalf("status=%d body=%s", recorder.Code, recorder.Body.String())
	}

	server.mu.Lock()
	defer server.mu.Unlock()
	if server.identityCalls != 1 || server.executeCalls != 1 || server.readCalls != 0 {
		t.Fatalf(
			"identity/execute/read=%d/%d/%d",
			server.identityCalls, server.executeCalls, server.readCalls,
		)
	}
}

func startApprovalGRPCServer(
	t *testing.T,
) (string, string, *approvalGRPCServer, func()) {
	t.Helper()
	serverCredentials, caPEM := recordFilesTestCredentials(t)
	listener, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	grpcServer := grpc.NewServer(grpc.Creds(serverCredentials))
	implementation := &approvalGRPCServer{}
	runtimev1.RegisterIdentityContextServiceServer(grpcServer, implementation)
	runtimev1.RegisterApprovalServiceServer(grpcServer, implementation)
	go func() { _ = grpcServer.Serve(listener) }()
	caFile := filepath.Join(t.TempDir(), "runtime-ca.pem")
	if err := os.WriteFile(caFile, caPEM, 0o600); err != nil {
		t.Fatal(err)
	}
	return "passthrough:///" + listener.Addr().String(), caFile, implementation, func() {
		grpcServer.GracefulStop()
		_ = listener.Close()
	}
}
