package erecon

import (
	"time"

	"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/agg"

	"github.com/tech/sendico/pkg/mlogger"
	paymenttypes "github.com/tech/sendico/pkg/payments/types"
	"go.uber.org/zap"
)

// Reconciler applies external async events to payment runtime state.
type Reconciler interface {
	Reconcile(in Input) (*Output, error)
}

// Input is the reconciliation payload.
type Input struct {
	Payment *agg.Payment
	Event   Event
}

// Output is the reconciliation result.
type Output struct {
	Payment          *agg.Payment
	MatchedStepRef   string
	StepChanged      bool
	AggregateChanged bool
}

// Event is one transport-agnostic external event envelope.
// Exactly one payload must be set.
type Event struct {
	Gateway *GatewayEvent
	Ledger  *LedgerEvent
	Card    *CardEvent
}

// GatewayStatus is gateway operation lifecycle status.
type GatewayStatus string

const (
	GatewayStatusUnspecified GatewayStatus = "unspecified"
	GatewayStatusCreated     GatewayStatus = "created"
	GatewayStatusProcessing  GatewayStatus = "processing"
	GatewayStatusWaiting     GatewayStatus = "waiting"
	GatewayStatusSuccess     GatewayStatus = "success"
	GatewayStatusFailed      GatewayStatus = "failed"
	GatewayStatusCancelled   GatewayStatus = "cancelled"
)

// LedgerStatus is ledger operation lifecycle status.
type LedgerStatus string

const (
	LedgerStatusUnspecified LedgerStatus = "unspecified"
	LedgerStatusPending     LedgerStatus = "pending"
	LedgerStatusProcessing  LedgerStatus = "processing"
	LedgerStatusPosted      LedgerStatus = "posted"
	LedgerStatusFailed      LedgerStatus = "failed"
	LedgerStatusCancelled   LedgerStatus = "cancelled"
)

// CardStatus is card payout lifecycle status.
type CardStatus string

const (
	CardStatusUnspecified CardStatus = "unspecified"
	CardStatusCreated     CardStatus = "created"
	CardStatusProcessing  CardStatus = "processing"
	CardStatusWaiting     CardStatus = "waiting"
	CardStatusSuccess     CardStatus = "success"
	CardStatusFailed      CardStatus = "failed"
	CardStatusCancelled   CardStatus = "cancelled"
)

// GatewayEvent is one async event from gateway execution flow.
type GatewayEvent struct {
	StepRef           string
	OperationRef      string
	TransferRef       string
	GatewayInstanceID string
	Status            GatewayStatus
	ExecutedMoney     *paymenttypes.Money
	FailureCode       string
	FailureMsg        string
	Retryable         *bool
	TerminalFailure   bool
	OccurredAt        *time.Time
}

// LedgerEvent is one async event from ledger flow.
type LedgerEvent struct {
	StepRef         string
	EntryRef        string
	Status          LedgerStatus
	FailureCode     string
	FailureMsg      string
	Retryable       *bool
	TerminalFailure bool
	OccurredAt      *time.Time
}

// CardEvent is one async event from card payout flow.
type CardEvent struct {
	StepRef           string
	PayoutRef         string
	GatewayInstanceID string
	Status            CardStatus
	FailureCode       string
	FailureMsg        string
	Retryable         *bool
	TerminalFailure   bool
	OccurredAt        *time.Time
}

const (
	ExternalRefKindOperation  = "operation_ref"
	ExternalRefKindTransfer   = "transfer_ref"
	ExternalRefKindLedger     = "ledger_entry_ref"
	ExternalRefKindCardPayout = "card_payout_ref"
)

// Dependencies configures reconciliation service integrations.
type Dependencies struct {
	Logger mlogger.Logger
	Now    func() time.Time
}

func New(deps ...Dependencies) Reconciler {
	var dep Dependencies
	if len(deps) > 0 {
		dep = deps[0]
	}
	logger := dep.Logger
	if logger == nil {
		logger = zap.NewNop()
	}
	now := dep.Now
	if now == nil {
		now = defaultNow
	}
	return &svc{
		logger: logger.Named("reconciler"),
		now:    now,
	}
}
