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"
	catalogsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/catalog/consumerv1"
	approvalnotificationsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/approvalnotificationflowv1"
	permissionsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/effectivepermissionsv1"
	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 approvalNotificationFlowGRPCServer struct {
	runtimev1.UnimplementedIdentityContextServiceServer
	runtimev1.UnimplementedEffectivePermissionServiceServer
	runtimev1.UnimplementedApprovalNotificationFlowReadServiceServer

	mu              sync.Mutex
	identityCalls   int
	permissionCalls int
	flowCalls       int
}

func (server *approvalNotificationFlowGRPCServer) Exchange(
	ctx context.Context,
	wireRequest *runtimev1.ExchangeRequest,
) (*runtimev1.ExchangeResponse, error) {
	if err := requireRecordFilesMetadata(ctx, true, false); err != nil {
		return nil, err
	}
	request, err := mapping.FromWireIdentityExchangeRequest(ctx, wireRequest)
	if err != nil {
		return nil, mapping.ToIdentityStatusError(err)
	}
	if len(request.RequestedCapabilities) != 1 ||
		(request.RequestedCapabilities[0] != permissionsemantic.CapabilityName &&
			request.RequestedCapabilities[0] != approvalnotificationsemantic.CapabilityName) {
		return nil, errors.New("unexpected delegated approval notification Flow 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 *approvalNotificationFlowGRPCServer) ReadEffectivePermissions(
	ctx context.Context,
	wireRequest *runtimev1.ReadEffectivePermissionsRequest,
) (*runtimev1.ReadEffectivePermissionsResponse, error) {
	if err := requireRecordFilesMetadata(ctx, false, true); err != nil {
		return nil, err
	}
	request, err := mapping.FromWireReadEffectivePermissionsRequest(ctx, wireRequest)
	if err != nil {
		return nil, mapping.ToPlatformStatusError(err, permissionsemantic.CapabilityName)
	}
	server.mu.Lock()
	server.permissionCalls++
	server.mu.Unlock()
	return mapping.ToWireReadEffectivePermissionsResponseForRequest(
		request,
		selectedPermissionTestResponse(
			approvalNotificationFlowPermissions(true, 3), request,
		),
	)
}

func (server *approvalNotificationFlowGRPCServer) ReadApprovalNotificationFlows(
	ctx context.Context,
	wireRequest *runtimev1.ReadApprovalNotificationFlowsRequest,
) (*runtimev1.ReadApprovalNotificationFlowsResponse, error) {
	if err := requireRecordFilesMetadata(ctx, false, true); err != nil {
		return nil, err
	}
	request, err := mapping.FromWireReadApprovalNotificationFlowsRequest(ctx, wireRequest)
	if err != nil {
		return nil, mapping.ToPlatformStatusError(
			err, approvalnotificationsemantic.CapabilityName,
		)
	}
	server.mu.Lock()
	server.flowCalls++
	server.mu.Unlock()
	return mapping.ToWireReadApprovalNotificationFlowsResponse(
		request,
		approvalnotificationsemantic.Response{
			Flows: []approvalnotificationsemantic.FlowDescriptor{{
				Name: "Notify_Approver", Label: "Notify Approver",
				Description: "approval email",
			}},
			Receipts: approvalNotificationFlowReceipts(3),
		},
	)
}

func TestApprovalNotificationFlowsUsesProductionPlatformGRPCBoundary(t *testing.T) {
	target, caFile, server, cleanup := startApprovalNotificationFlowGRPCServer(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()
	config := permissionMetadataServiceConfig(
		platform.ReadEffectivePermissions,
		func(context.Context, catalogsemantic.GetRequest) (catalogsemantic.AppMetadataView, error) {
			return catalogsemantic.AppMetadataView{}, nil
		},
	)
	config.RuntimeReadNotificationFlows = platform.ReadApprovalNotificationFlows
	service, err := NewService(config)
	if err != nil {
		t.Fatal(err)
	}
	response := httptest.NewRecorder()
	service.ServeHTTP(response, approvalHTTPRequest(
		http.MethodGet,
		"/services/data/v63.0/process/approvals/notification-flows",
		"",
	))
	if response.Code != http.StatusOK ||
		!strings.Contains(response.Body.String(), `"name":"Notify_Approver"`) {
		t.Fatalf("status=%d body=%s", response.Code, response.Body.String())
	}
	server.mu.Lock()
	defer server.mu.Unlock()
	if server.identityCalls != 2 || server.permissionCalls != 1 || server.flowCalls != 1 {
		t.Fatalf(
			"identity/permission/flows=%d/%d/%d, want 2/1/1",
			server.identityCalls, server.permissionCalls, server.flowCalls,
		)
	}
}

func startApprovalNotificationFlowGRPCServer(
	t *testing.T,
) (string, string, *approvalNotificationFlowGRPCServer, 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 := &approvalNotificationFlowGRPCServer{}
	runtimev1.RegisterIdentityContextServiceServer(grpcServer, implementation)
	runtimev1.RegisterEffectivePermissionServiceServer(grpcServer, implementation)
	runtimev1.RegisterApprovalNotificationFlowReadServiceServer(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()
	}
}
