package runtimeapi

import (
	"context"
	"errors"

	"github.com/pingcap-inc/nexuscrm-api-host/runtimegrpc"
	semantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/authorizationv1"
	runtimev1 "github.com/pingcap-inc/nexuscrm-contract/wire-go/gen/runtime/v1"
	"github.com/pingcap-inc/nexuscrm-contract/wire-go/mapping"
)

type AuthorizationLifecycleEngine interface {
	BeginAuthorization(context.Context, RuntimeWorkloadCall, semantic.BeginRequest) (semantic.BeginResponse, error)
	CompleteAuthorization(context.Context, RuntimeWorkloadCall, runtimegrpc.SessionPresentedCredentials, semantic.CompleteRequest) (semantic.CompleteResponse, runtimegrpc.IssuedAuthorizationCredentials, error)
	ExchangeAuthorizationToken(context.Context, RuntimeWorkloadCall, runtimegrpc.AuthorizationExchangeCredentials, semantic.ExchangeRequest) (semantic.ExchangeResponse, runtimegrpc.IssuedAuthorizationCredentials, error)
}

type AuthorizationLifecycleServer struct {
	runtimev1.UnimplementedAuthorizationLifecycleServiceServer
	engine AuthorizationLifecycleEngine
}

func NewAuthorizationLifecycleServer(engine AuthorizationLifecycleEngine) (*AuthorizationLifecycleServer, error) {
	if engine == nil {
		return nil, errors.New("runtime-api authorization lifecycle engine is required")
	}
	return &AuthorizationLifecycleServer{engine: engine}, nil
}

func (s *AuthorizationLifecycleServer) BeginAuthorization(ctx context.Context, request *runtimev1.BeginAuthorizationRequest) (*runtimev1.BeginAuthorizationResponse, error) {
	semanticRequest, err := mapping.FromWireBeginAuthorizationRequest(ctx, request)
	if err != nil {
		return nil, authorizationStatus(err)
	}
	authority, ok := RuntimeWorkloadCallFromContext(ctx)
	if !ok {
		return nil, authorizationStatus(&semantic.Failure{Category: semantic.FailureUnauthenticated})
	}
	response, err := s.engine.BeginAuthorization(runtimegrpc.WithoutRuntimeSessionCredentials(ctx), authority, semanticRequest)
	if err != nil {
		return nil, authorizationStatus(err)
	}
	wire, err := mapping.ToWireBeginAuthorizationResponse(response)
	if err != nil {
		return nil, authorizationStatus(err)
	}
	return wire, nil
}

func (s *AuthorizationLifecycleServer) CompleteAuthorization(ctx context.Context, request *runtimev1.CompleteAuthorizationRequest) (*runtimev1.CompleteAuthorizationResponse, error) {
	semanticRequest, err := mapping.FromWireCompleteAuthorizationRequest(ctx, request)
	if err != nil {
		return nil, authorizationStatus(err)
	}
	credentials, ok := runtimegrpc.RuntimeSessionPresentedCredentials(ctx)
	if !ok {
		return nil, authorizationStatus(&semantic.Failure{Category: semantic.FailureUnauthenticated})
	}
	authority, ok := RuntimeWorkloadCallFromContext(ctx)
	if !ok {
		return nil, authorizationStatus(&semantic.Failure{Category: semantic.FailureUnauthenticated})
	}
	response, issued, err := s.engine.CompleteAuthorization(runtimegrpc.WithoutRuntimeSessionCredentials(ctx), authority, credentials, semanticRequest)
	if err != nil {
		return nil, authorizationStatus(err)
	}
	wire, err := mapping.ToWireCompleteAuthorizationResponse(response)
	if err != nil {
		return nil, authorizationStatus(err)
	}
	if response.AuthorizationCodeIssued {
		if err := runtimegrpc.SetIssuedAuthorizationCredentials(ctx, issued); err != nil {
			return nil, authorizationStatus(err)
		}
	}
	return wire, nil
}

func (s *AuthorizationLifecycleServer) ExchangeAuthorizationToken(ctx context.Context, request *runtimev1.ExchangeAuthorizationTokenRequest) (*runtimev1.ExchangeAuthorizationTokenResponse, error) {
	semanticRequest, err := mapping.FromWireExchangeAuthorizationTokenRequest(ctx, request)
	if err != nil {
		return nil, authorizationStatus(err)
	}
	credentials, ok := runtimegrpc.RuntimeAuthorizationExchangeCredentials(ctx)
	if !ok {
		return nil, authorizationStatus(&semantic.Failure{Category: semantic.FailureUnauthenticated})
	}
	authority, ok := RuntimeWorkloadCallFromContext(ctx)
	if !ok {
		return nil, authorizationStatus(&semantic.Failure{Category: semantic.FailureUnauthenticated})
	}
	response, issued, err := s.engine.ExchangeAuthorizationToken(runtimegrpc.WithoutRuntimeSessionCredentials(ctx), authority, credentials, semanticRequest)
	if err != nil {
		return nil, authorizationStatus(err)
	}
	if err := semantic.ValidateExchangeResponseForRequest(semanticRequest, response); err != nil {
		return nil, authorizationStatus(err)
	}
	wire, err := mapping.ToWireExchangeAuthorizationTokenResponse(response)
	if err != nil {
		return nil, authorizationStatus(err)
	}
	if response.Token != nil {
		if err := runtimegrpc.SetIssuedAuthorizationCredentials(ctx, issued); err != nil {
			return nil, authorizationStatus(err)
		}
	}
	return wire, nil
}

func authorizationStatus(err error) error {
	return mapping.ToPlatformStatusError(err, semantic.CapabilityName)
}
