package runtimeapi

import (
	"context"
	"errors"

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

// SessionLifecycleEngine owns the existing durable identity/session authority.
// Sensitive values cross the gRPC adapter only through api-host's private
// transport context and are erased before any repository call.
type SessionLifecycleEngine interface {
	CreateSession(context.Context, RuntimeWorkloadCall, runtimegrpc.SessionCreateCredentials, semantic.CreateRequest) (semantic.CreateResponse, runtimegrpc.SessionIssuedCredentials, error)
	ReadSession(context.Context, RuntimeWorkloadCall, runtimegrpc.SessionPresentedCredentials, semantic.ReadRequest) (semantic.ReadResponse, runtimegrpc.SessionIssuedCredentials, error)
	RevokeSession(context.Context, RuntimeWorkloadCall, runtimegrpc.SessionPresentedCredentials, semantic.RevokeRequest) error
}

type SessionLifecycleServer struct {
	runtimev1.UnimplementedSessionLifecycleServiceServer
	engine SessionLifecycleEngine
}

func NewSessionLifecycleServer(engine SessionLifecycleEngine) (*SessionLifecycleServer, error) {
	if engine == nil {
		return nil, errors.New("runtime-api session lifecycle engine is required")
	}
	return &SessionLifecycleServer{engine: engine}, nil
}

func (s *SessionLifecycleServer) CreateSession(ctx context.Context, request *runtimev1.CreateSessionRequest) (*runtimev1.CreateSessionResponse, error) {
	if s == nil || s.engine == nil {
		return nil, sessionStatus(&semantic.Failure{Category: semantic.FailureUnavailable})
	}
	semanticRequest, err := mapping.FromWireCreateSessionRequest(ctx, request)
	if err != nil {
		return nil, sessionStatus(err)
	}
	credentials, ok := runtimegrpc.RuntimeSessionCreateCredentials(ctx)
	if !ok {
		return nil, sessionStatus(&semantic.Failure{Category: semantic.FailureUnauthenticated})
	}
	authority, ok := RuntimeWorkloadCallFromContext(ctx)
	if !ok {
		return nil, sessionStatus(&semantic.Failure{Category: semantic.FailureUnauthenticated})
	}
	response, issued, err := s.engine.CreateSession(
		runtimegrpc.WithoutRuntimeSessionCredentials(ctx), authority, credentials, semanticRequest,
	)
	if err != nil {
		return nil, sessionStatus(err)
	}
	if !response.AccessTokenIssued || issued.AccessToken == "" ||
		response.ControlProofIssued != (issued.ControlProof != "") {
		return nil, sessionStatus(&semantic.Failure{Category: semantic.FailureInternal})
	}
	wireResponse, err := mapping.ToWireCreateSessionResponse(semanticRequest, response)
	if err != nil {
		return nil, sessionStatus(err)
	}
	// Publish the raw credentials only after the complete semantic response has
	// mapped successfully. A mapping failure must never turn into a credential
	// header paired with an RPC error.
	if err := runtimegrpc.SetIssuedSessionCredentials(ctx, issued); err != nil {
		return nil, sessionStatus(err)
	}
	return wireResponse, nil
}

func (s *SessionLifecycleServer) ReadSession(ctx context.Context, request *runtimev1.ReadSessionRequest) (*runtimev1.ReadSessionResponse, error) {
	if s == nil || s.engine == nil {
		return nil, sessionStatus(&semantic.Failure{Category: semantic.FailureUnavailable})
	}
	semanticRequest, err := mapping.FromWireReadSessionRequest(ctx, request)
	if err != nil {
		return nil, sessionStatus(err)
	}
	credentials, ok := runtimegrpc.RuntimeSessionPresentedCredentials(ctx)
	if !ok {
		return nil, sessionStatus(&semantic.Failure{Category: semantic.FailureUnauthenticated})
	}
	authority, ok := RuntimeWorkloadCallFromContext(ctx)
	if !ok {
		return nil, sessionStatus(&semantic.Failure{Category: semantic.FailureUnauthenticated})
	}
	response, issued, err := s.engine.ReadSession(
		runtimegrpc.WithoutRuntimeSessionCredentials(ctx), authority, credentials, semanticRequest,
	)
	if err != nil {
		return nil, sessionStatus(err)
	}
	if response.ReplacementAccessTokenIssued != (issued.AccessToken != "") || issued.ControlProof != "" {
		return nil, sessionStatus(&semantic.Failure{Category: semantic.FailureInternal})
	}
	wireResponse, err := mapping.ToWireReadSessionResponse(semanticRequest, response)
	if err != nil {
		return nil, sessionStatus(err)
	}
	if issued.AccessToken != "" {
		if err := runtimegrpc.SetIssuedSessionCredentials(ctx, issued); err != nil {
			return nil, sessionStatus(err)
		}
	}
	return wireResponse, nil
}

func (s *SessionLifecycleServer) RevokeSession(ctx context.Context, request *runtimev1.RevokeSessionRequest) (*runtimev1.RevokeSessionResponse, error) {
	if s == nil || s.engine == nil {
		return nil, sessionStatus(&semantic.Failure{Category: semantic.FailureUnavailable})
	}
	semanticRequest, err := mapping.FromWireRevokeSessionRequest(ctx, request)
	if err != nil {
		return nil, sessionStatus(err)
	}
	credentials, ok := runtimegrpc.RuntimeSessionPresentedCredentials(ctx)
	if !ok {
		return nil, sessionStatus(&semantic.Failure{Category: semantic.FailureUnauthenticated})
	}
	authority, ok := RuntimeWorkloadCallFromContext(ctx)
	if !ok {
		return nil, sessionStatus(&semantic.Failure{Category: semantic.FailureUnauthenticated})
	}
	if err := s.engine.RevokeSession(
		runtimegrpc.WithoutRuntimeSessionCredentials(ctx), authority, credentials, semanticRequest,
	); err != nil {
		return nil, sessionStatus(err)
	}
	wireResponse, err := mapping.ToWireRevokeSessionResponse(semantic.RevokeResponse{})
	if err != nil {
		return nil, sessionStatus(err)
	}
	return wireResponse, nil
}

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