package sfcrmappservice

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

	"github.com/pingcap-inc/nexuscrm-api-host/runtimegrpc"
	catalogsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/catalog/consumerv1"
	permissionsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/effectivepermissionsv1"
	identitysemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/identityv1"
	catalogv1 "github.com/pingcap-inc/nexuscrm-contract/wire-go/gen/catalog/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"
)

type uiAppsGRPCServer struct {
	runtimev1.UnimplementedIdentityContextServiceServer
	runtimev1.UnimplementedEffectivePermissionServiceServer
	catalogv1.UnimplementedConsumerViewServiceServer
	catalogv1.UnimplementedAppMetadataViewV3ServiceServer

	view        catalogsemantic.AppMetadataViewV3
	permissions permissionsemantic.Response

	mu              sync.Mutex
	identityCalls   int
	catalogCalls    int
	permissionCalls int
}

func (server *uiAppsGRPCServer) 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] != permissionsemantic.CapabilityName &&
			semanticRequest.RequestedCapabilities[0] != catalogsemantic.AppMetadataV3CapabilityName) {
		return nil, errors.New("unexpected delegated UI Apps 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 *uiAppsGRPCServer) GetAppMetadataViewV3(
	ctx context.Context,
	request *catalogv1.GetAppMetadataViewV3Request,
) (*catalogv1.GetAppMetadataViewV3Response, error) {
	if err := requireRecordFilesMetadata(ctx, false, false); err != nil {
		return nil, err
	}
	if _, err := mapping.FromWireCatalogGetV3Request(ctx, request); err != nil {
		return nil, mapping.ToPlatformStatusError(err, catalogsemantic.AppMetadataV3CapabilityName)
	}
	server.mu.Lock()
	server.catalogCalls++
	server.mu.Unlock()
	return mapping.ToWireGetAppMetadataViewV3Response(server.view)
}

func (server *uiAppsGRPCServer) ReadEffectivePermissions(
	ctx context.Context,
	request *runtimev1.ReadEffectivePermissionsRequest,
) (*runtimev1.ReadEffectivePermissionsResponse, error) {
	if err := requireRecordFilesMetadata(ctx, false, true); err != nil {
		return nil, err
	}
	semanticRequest, err := mapping.FromWireReadEffectivePermissionsRequest(ctx, request)
	if err != nil {
		return nil, mapping.ToPlatformStatusError(err, permissionsemantic.CapabilityName)
	}
	if semanticRequest.MinCatalogRevision == nil ||
		*semanticRequest.MinCatalogRevision != server.view.Receipts.CatalogRevision {
		return nil, errors.New("UI Apps permission read lost exact Catalog revision")
	}
	server.mu.Lock()
	server.permissionCalls++
	server.mu.Unlock()
	return mapping.ToWireReadEffectivePermissionsResponseForRequest(
		semanticRequest, selectedPermissionTestResponse(server.permissions, semanticRequest),
	)
}

func TestUIAppsUsesProductionTLSPlatformGRPCBoundary(t *testing.T) {
	view := uiAppsTestView(t, uiAppsTestNavigation)
	permissions := uiAppsTestPermissions(view)
	target, caFile, server, cleanup := startUIAppsGRPCServer(t, view, permissions)
	defer cleanup()

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

	service := uiAppsTestService(t, view, permissions)
	service.uiApps, err = NewRuntimeUIAppsClient(
		platform.GetAppMetadataViewV3,
		platform.ReadEffectivePermissions,
		uiAppsTestCurrentNavigation(view, permissions),
	)
	if err != nil {
		t.Fatal(err)
	}
	recorder := httptest.NewRecorder()
	service.ServeHTTP(recorder, uiAppsRequest(http.MethodGet, "/services/data/v63.0/ui-api/apps"))
	if recorder.Code != http.StatusOK {
		t.Fatalf("UI Apps status=%d body=%s", recorder.Code, recorder.Body.String())
	}

	server.mu.Lock()
	defer server.mu.Unlock()
	if server.catalogCalls != 1 || server.permissionCalls != 1 || server.identityCalls != 1 {
		t.Fatalf("identity/catalog/permission=%d/%d/%d, want 1/1/1",
			server.identityCalls, server.catalogCalls, server.permissionCalls)
	}
}

func startUIAppsGRPCServer(
	t *testing.T,
	view catalogsemantic.AppMetadataViewV3,
	permissions permissionsemantic.Response,
) (string, string, *uiAppsGRPCServer, 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 := &uiAppsGRPCServer{view: view, permissions: permissions}
	runtimev1.RegisterIdentityContextServiceServer(grpcServer, implementation)
	catalogv1.RegisterConsumerViewServiceServer(grpcServer, implementation)
	catalogv1.RegisterAppMetadataViewV3ServiceServer(grpcServer, implementation)
	runtimev1.RegisterEffectivePermissionServiceServer(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()
	}
}
