package runtimeapi

import (
	"context"
	"errors"

	"github.com/nexuscrm/persistence"
	applicationresultsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/applicationresultv1"
	commandsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/commandv1"
	commonsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/commonv1"
	runtimev1 "github.com/pingcap-inc/nexuscrm-contract/wire-go/gen/runtime/v1"
	"github.com/pingcap-inc/nexuscrm-contract/wire-go/mapping"
)

// ApplicationResultEngine is Runtime's one immutable application-result read
// port. The transport adapter neither owns nor reconstructs result state.
type ApplicationResultEngine interface {
	GetApplicationResult(
		context.Context,
		persistence.RuntimePlatformCommandTrustedScope,
		applicationresultsemantic.GetResultRequest,
	) (applicationresultsemantic.GetResultResponse, error)
}

type ApplicationResultServer struct {
	runtimev1.UnimplementedApplicationResultServiceServer
	engine ApplicationResultEngine
}

func NewApplicationResultServer(
	engine ApplicationResultEngine,
) (*ApplicationResultServer, error) {
	if engine == nil {
		return nil, errors.New("runtime application result: engine is required")
	}
	return &ApplicationResultServer{engine: engine}, nil
}

func (s *ApplicationResultServer) GetApplicationResult(
	ctx context.Context,
	wireRequest *runtimev1.GetApplicationResultRequest,
) (*runtimev1.GetApplicationResultResponse, error) {
	request, err := mapping.FromWireGetApplicationResultRequest(ctx, wireRequest)
	if err != nil {
		return nil, mapping.ToPlatformStatusError(
			err,
			applicationresultsemantic.CapabilityName,
		)
	}
	scope, ok := applicationResultScopeFromAdmission(ctx)
	if !ok {
		return nil, mapping.ToPlatformStatusError(
			&commonsemantic.Failure{Category: commonsemantic.FailureUnavailable},
			applicationresultsemantic.CapabilityName,
		)
	}
	// ApplicationResult has its own transport capability, but the durable row
	// remains in the exact command-acceptance partition that originally
	// admitted it. Keep this fixed here so no caller or adapter can query a
	// second result namespace accidentally.
	scope.RuntimeCapabilityID = commandsemantic.CapabilityName
	response, err := s.engine.GetApplicationResult(ctx, scope, request)
	if ctxErr := ctx.Err(); ctxErr != nil {
		return nil, mapping.ToPlatformStatusError(
			ctxErr,
			applicationresultsemantic.CapabilityName,
		)
	}
	if err != nil {
		return nil, mapping.ToPlatformStatusError(
			err,
			applicationresultsemantic.CapabilityName,
		)
	}
	wireResponse, err := mapping.ToWireGetApplicationResultResponse(
		request,
		response,
	)
	if err != nil {
		return nil, mapping.ToPlatformStatusError(
			err,
			applicationresultsemantic.CapabilityName,
		)
	}
	return wireResponse, nil
}
