package runtimeapi

import (
	"context"
	"errors"
	"sort"
	"strings"

	"github.com/nexuscrm/shared/pkg/models"
	"github.com/pingcap-inc/nexuscrm-api-host/runtimegrpc"
	approvalnotificationsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/approvalnotificationflowv1"
	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"
)

// ApprovalNotificationFlowReadEngine is the repository-backed neutral source
// that has already applied active, protected, and notification-safe filters.
type ApprovalNotificationFlowReadEngine interface {
	ListApprovalNotificationFlows(context.Context) ([]*models.FlowDefinition, error)
}

type ApprovalNotificationFlowReadServer struct {
	runtimev1.UnimplementedApprovalNotificationFlowReadServiceServer
	engine ApprovalNotificationFlowReadEngine
}

func NewApprovalNotificationFlowReadServer(
	engine ApprovalNotificationFlowReadEngine,
) (*ApprovalNotificationFlowReadServer, error) {
	if engine == nil {
		return nil, errors.New("approval notification Flow read requires an eligibility engine")
	}
	return &ApprovalNotificationFlowReadServer{engine: engine}, nil
}

func (server *ApprovalNotificationFlowReadServer) ReadApprovalNotificationFlows(
	ctx context.Context,
	wireRequest *runtimev1.ReadApprovalNotificationFlowsRequest,
) (*runtimev1.ReadApprovalNotificationFlowsResponse, error) {
	request, err := mapping.FromWireReadApprovalNotificationFlowsRequest(ctx, wireRequest)
	if err != nil {
		return nil, approvalNotificationFlowReadStatus(err)
	}
	receipts, err := approvalNotificationFlowReadAuthority(ctx, request.MinCatalogRevision)
	if err != nil {
		return nil, approvalNotificationFlowReadStatus(err)
	}
	definitions, err := server.engine.ListApprovalNotificationFlows(ctx)
	if err != nil {
		return nil, approvalNotificationFlowReadStatus(
			classifyPlatformEngineError(ctx, err, commonsemantic.InvalidComponentOperation),
		)
	}
	flows := make([]approvalnotificationsemantic.FlowDescriptor, 0, len(definitions))
	for _, definition := range definitions {
		if definition == nil {
			return nil, approvalNotificationFlowReadStatus(&approvalnotificationsemantic.Failure{
				Category: approvalnotificationsemantic.FailureInternal,
			})
		}
		name := strings.TrimSpace(definition.DeveloperName)
		label := strings.TrimSpace(definition.MasterLabel)
		if label == "" {
			label = name
		}
		flows = append(flows, approvalnotificationsemantic.FlowDescriptor{
			Name: name, Label: label, Description: definition.Description,
		})
	}
	sort.Slice(flows, func(left, right int) bool {
		leftKey, rightKey := strings.ToLower(flows[left].Name), strings.ToLower(flows[right].Name)
		if leftKey != rightKey {
			return leftKey < rightKey
		}
		return flows[left].Name < flows[right].Name
	})
	response, err := mapping.ToWireReadApprovalNotificationFlowsResponse(
		request,
		approvalnotificationsemantic.Response{Flows: flows, Receipts: receipts},
	)
	if err != nil {
		return nil, approvalNotificationFlowReadStatus(err)
	}
	return response, nil
}

func approvalNotificationFlowReadAuthority(
	ctx context.Context,
	minimum *uint64,
) (approvalnotificationsemantic.Receipts, error) {
	currentCapability, current := runtimegrpc.Capability(ctx)
	trusted, ok := trustedCallFromContext(ctx)
	if !current || currentCapability != approvalnotificationsemantic.CapabilityName ||
		!ok || !containsCapability(trusted.capabilities, approvalnotificationsemantic.CapabilityName) {
		return approvalnotificationsemantic.Receipts{}, &approvalnotificationsemantic.Failure{
			Category: approvalnotificationsemantic.FailureUnavailable,
		}
	}
	// This port reads current repository metadata and therefore cannot claim a
	// Catalog publication revision. A minimum revision must fail closed.
	if minimum != nil {
		return approvalnotificationsemantic.Receipts{}, &approvalnotificationsemantic.Failure{
			Category:         approvalnotificationsemantic.FailureStaleState,
			StaleAxis:        approvalnotificationsemantic.StaleAxisCatalog,
			RequiredRevision: *minimum,
			ServedRevision:   0,
		}
	}
	return approvalnotificationsemantic.Receipts{
		ProtocolVersion:   "1.0",
		SecurityEpoch:     trusted.securityEpoch,
		CapabilityVersion: approvalnotificationsemantic.CapabilityName,
	}, nil
}

func approvalNotificationFlowReadStatus(err error) error {
	return mapping.ToPlatformStatusError(err, approvalnotificationsemantic.CapabilityName)
}
