package oobs

import (
	"context"
	"time"

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

// Observer records orchestration-v2 telemetry and exposes audit timelines.
type Observer interface {
	RecordPayment(ctx context.Context, in RecordPaymentInput) error
	RecordStep(ctx context.Context, in RecordStepInput) error
	RecordExternal(ctx context.Context, in RecordExternalInput) error

	PaymentTimeline(ctx context.Context, in PaymentTimelineInput) (*TimelineOutput, error)
	StepTimeline(ctx context.Context, in StepTimelineInput) (*TimelineOutput, error)
}

// PaymentEvent classifies aggregate-level observability events.
type PaymentEvent string

const (
	PaymentEventCreated        PaymentEvent = "created"
	PaymentEventStateChanged   PaymentEvent = "state_changed"
	PaymentEventNeedsAttention PaymentEvent = "needs_attention"
	PaymentEventSettled        PaymentEvent = "settled"
	PaymentEventFailed         PaymentEvent = "failed"
)

// StepEvent classifies step-attempt-level observability events.
type StepEvent string

const (
	StepEventScheduled StepEvent = "scheduled"
	StepEventStarted   StepEvent = "started"
	StepEventCompleted StepEvent = "completed"
	StepEventFailed    StepEvent = "failed"
	StepEventSkipped   StepEvent = "skipped"
	StepEventBlocked   StepEvent = "blocked"
)

// ExternalSource identifies asynchronous event origin.
type ExternalSource string

const (
	ExternalSourceGateway ExternalSource = "gateway"
	ExternalSourceLedger  ExternalSource = "ledger"
	ExternalSourceCard    ExternalSource = "card"
)

// TimelineScope classifies timeline item scope.
type TimelineScope string

const (
	ScopePayment TimelineScope = "payment"
	ScopeStep    TimelineScope = "step"
)

// TimelineEntry is one immutable audit timeline item.
type TimelineEntry struct {
	OccurredAt time.Time
	Scope      TimelineScope
	PaymentRef string
	StepRef    string
	StepCode   string
	Attempt    uint32
	Event      string
	State      string
	Message    string
	Fields     map[string]string
}

// TimelineOutput is one timeline query result page.
type TimelineOutput struct {
	Items []TimelineEntry
}

// RecordPaymentInput is aggregate-level telemetry payload.
type RecordPaymentInput struct {
	Payment *agg.Payment
	Event   PaymentEvent
	Message string
	Fields  map[string]string
}

// RecordStepInput is step-attempt-level telemetry payload.
type RecordStepInput struct {
	PaymentRef string
	Step       agg.StepExecution
	Event      StepEvent
	Message    string
	Duration   time.Duration
	Fields     map[string]string
}

// RecordExternalInput is external-event telemetry payload.
type RecordExternalInput struct {
	PaymentRef string
	StepRef    string
	Attempt    uint32
	Source     ExternalSource
	Status     string
	RefKind    string
	Ref        string
	Message    string
	Fields     map[string]string
}

// PaymentTimelineInput scopes payment-level timeline lookup.
type PaymentTimelineInput struct {
	PaymentRef string
	Limit      int32
	Desc       bool
}

// StepTimelineInput scopes step-attempt timeline lookup.
type StepTimelineInput struct {
	PaymentRef string
	StepRef    string
	Attempt    uint32
	Limit      int32
	Desc       bool
}

// Metrics captures counters and durations for orchestration telemetry.
type Metrics interface {
	IncPaymentEvent(event PaymentEvent, state agg.State)
	IncStepEvent(event StepEvent, stepCode string, state agg.StepState)
	IncExternalEvent(source ExternalSource, status string)
	ObserveStepDuration(stepCode string, state agg.StepState, duration time.Duration)
}

// AuditStore persists timeline events and serves timeline lookups.
type AuditStore interface {
	Append(entry TimelineEntry) error
	ListByPayment(paymentRef string, limit int32, desc bool) ([]TimelineEntry, error)
	ListByStepAttempt(paymentRef string, stepRef string, attempt uint32, limit int32, desc bool) ([]TimelineEntry, error)
}

// Dependencies configures observer integrations.
type Dependencies struct {
	Logger  mlogger.Logger
	Metrics Metrics
	Store   AuditStore
	Now     func() time.Time
}

func New(deps Dependencies) (Observer, error) {
	return newService(deps)
}
