package agg

import (
	"time"

	"github.com/tech/sendico/payments/storage/model"
	"github.com/tech/sendico/pkg/db/storable"
	"github.com/tech/sendico/pkg/mlogger"
	pm "github.com/tech/sendico/pkg/model"
	paymenttypes "github.com/tech/sendico/pkg/payments/types"
	"go.mongodb.org/mongo-driver/v2/bson"
	"go.uber.org/zap"
)

// Factory builds initial orchestration-v2 payment aggregates.
type Factory interface {
	Create(in Input) (*Payment, error)
}

// State is orchestration runtime state.
type State string

const (
	StateUnspecified    State = "unspecified"
	StateCreated        State = "created"
	StateExecuting      State = "executing"
	StateNeedsAttention State = "needs_attention"
	StateSettled        State = "settled"
	StateFailed         State = "failed"
)

// StepState is step-level execution state.
type StepState string

const (
	StepStateUnspecified    StepState = "unspecified"
	StepStatePending        StepState = "pending"
	StepStateRunning        StepState = "running"
	StepStateCompleted      StepState = "completed"
	StepStateFailed         StepState = "failed"
	StepStateNeedsAttention StepState = "needs_attention"
	StepStateSkipped        StepState = "skipped"
)

// StepShell defines one initial step telemetry item.
type StepShell struct {
	StepRef               string                 `bson:"stepRef" json:"stepRef"`
	StepCode              string                 `bson:"stepCode" json:"stepCode"`
	Rail                  model.Rail             `bson:"rail,omitempty" json:"rail,omitempty"`
	Gateway               string                 `bson:"gateway,omitempty" json:"gateway,omitempty"`
	InstanceID            string                 `bson:"instanceId,omitempty" json:"instanceId,omitempty"`
	ReportVisibility      model.ReportVisibility `bson:"reportVisibility,omitempty" json:"reportVisibility,omitempty"`
	UserLabel             string                 `bson:"userLabel,omitempty" json:"userLabel,omitempty"`
	Comment               string                 `bson:"comment,omitempty" json:"comment,omitempty"`
	PlannedMoney          *paymenttypes.Money    `bson:"plannedMoney,omitempty" json:"plannedMoney,omitempty"`
	PlannedConvertedMoney *paymenttypes.Money    `bson:"plannedConvertedMoney,omitempty" json:"plannedConvertedMoney,omitempty"`
}

// StepExecution is runtime telemetry for one step.
type StepExecution struct {
	StepRef               string                 `bson:"stepRef" json:"stepRef"`
	StepCode              string                 `bson:"stepCode" json:"stepCode"`
	Rail                  model.Rail             `bson:"rail,omitempty" json:"rail,omitempty"`
	Gateway               string                 `bson:"gateway,omitempty" json:"gateway,omitempty"`
	InstanceID            string                 `bson:"instanceId,omitempty" json:"instanceId,omitempty"`
	ReportVisibility      model.ReportVisibility `bson:"reportVisibility,omitempty" json:"reportVisibility,omitempty"`
	UserLabel             string                 `bson:"userLabel,omitempty" json:"userLabel,omitempty"`
	Comment               string                 `bson:"comment,omitempty" json:"comment,omitempty"`
	State                 StepState              `bson:"state" json:"state"`
	Attempt               uint32                 `bson:"attempt" json:"attempt"`
	StartedAt             *time.Time             `bson:"startedAt,omitempty" json:"startedAt,omitempty"`
	CompletedAt           *time.Time             `bson:"completedAt,omitempty" json:"completedAt,omitempty"`
	FailureCode           string                 `bson:"failureCode,omitempty" json:"failureCode,omitempty"`
	FailureMsg            string                 `bson:"failureMsg,omitempty" json:"failureMsg,omitempty"`
	ExternalRefs          []ExternalRef          `bson:"externalRefs,omitempty" json:"externalRefs,omitempty"`
	ExecutedMoney         *paymenttypes.Money    `bson:"executedMoney,omitempty" json:"executedMoney,omitempty"`
	ConvertedMoney        *paymenttypes.Money    `bson:"convertedMoney,omitempty" json:"convertedMoney,omitempty"`
	PlannedMoney          *paymenttypes.Money    `bson:"plannedMoney,omitempty" json:"plannedMoney,omitempty"`
	PlannedConvertedMoney *paymenttypes.Money    `bson:"plannedConvertedMoney,omitempty" json:"plannedConvertedMoney,omitempty"`
}

// ExternalRef links step execution to an external operation.
type ExternalRef struct {
	GatewayInstanceID string `bson:"gatewayInstanceId,omitempty" json:"gatewayInstanceId,omitempty"`
	Kind              string `bson:"kind" json:"kind"`
	Ref               string `bson:"ref" json:"ref"`
}

// Input defines payload for creating an initial payment aggregate.
type Input struct {
	OrganizationRef  bson.ObjectID               `bson:"organizationRef" json:"organizationRef"`
	IdempotencyKey   string                      `bson:"idempotencyKey" json:"idempotencyKey"`
	QuotationRef     string                      `bson:"quotationRef" json:"quotationRef"`
	ClientPaymentRef string                      `bson:"clientPaymentRef,omitempty" json:"clientPaymentRef,omitempty"`
	IntentSnapshot   model.PaymentIntent         `bson:"intentSnapshot" json:"intentSnapshot"`
	QuoteSnapshot    *model.PaymentQuoteSnapshot `bson:"quoteSnapshot" json:"quoteSnapshot"`
	Steps            []StepShell                 `bson:"steps,omitempty" json:"steps,omitempty"`
}

// Payment is orchestration-v2 runtime aggregate.
type Payment struct {
	storable.Base            `bson:",inline" json:",inline"`
	pm.OrganizationBoundBase `bson:",inline" json:",inline"`
	PaymentRef               string                      `bson:"paymentRef" json:"paymentRef"`
	IdempotencyKey           string                      `bson:"idempotencyKey" json:"idempotencyKey"`
	QuotationRef             string                      `bson:"quotationRef" json:"quotationRef"`
	ClientPaymentRef         string                      `bson:"clientPaymentRef,omitempty" json:"clientPaymentRef,omitempty"`
	IntentSnapshot           model.PaymentIntent         `bson:"intentSnapshot" json:"intentSnapshot"`
	QuoteSnapshot            *model.PaymentQuoteSnapshot `bson:"quoteSnapshot" json:"quoteSnapshot"`
	State                    State                       `bson:"state" json:"state"`
	Version                  uint64                      `bson:"version" json:"version"`
	StepExecutions           []StepExecution             `bson:"stepExecutions,omitempty" json:"stepExecutions,omitempty"`
}

// Dependencies configures aggregate factory integrations.
type Dependencies struct {
	Logger mlogger.Logger
}

func New(deps ...Dependencies) Factory {
	var dep Dependencies
	if len(deps) > 0 {
		dep = deps[0]
	}
	logger := dep.Logger
	if logger == nil {
		logger = zap.NewNop()
	}
	return &svc{
		logger: logger.Named("aggregator"),
		now:    func() time.Time { return time.Now().UTC() },
		newID: func() bson.ObjectID {
			return bson.NewObjectID()
		},
	}
}
