package xplan

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

// Compiler builds execution runtime step graph from resolved quote snapshots.
type Compiler interface {
	Compile(in Input) (*Graph, error)
}

// StepKind classifies graph step intent.
type StepKind string

const (
	StepKindUnspecified    StepKind = "unspecified"
	StepKindLiquidityCheck StepKind = "liquidity_check"
	StepKindPrefunding     StepKind = "prefunding"
	StepKindRailSend       StepKind = "rail_send"
	StepKindRailObserve    StepKind = "rail_observe"
	StepKindFundsCredit    StepKind = "funds_credit"
	StepKindFundsDebit     StepKind = "funds_debit"
	StepKindFundsMove      StepKind = "funds_move"
	StepKindFundsBlock     StepKind = "funds_block"
	StepKindFundsRelease   StepKind = "funds_release"
)

// Input is the compiler payload.
type Input struct {
	IntentSnapshot model.PaymentIntent
	QuoteSnapshot  *model.PaymentQuoteSnapshot
	Policies       []Policy
}

// Graph is a compiled runtime execution graph.
type Graph struct {
	RouteRef  string
	Readiness paymenttypes.QuoteExecutionReadiness
	Steps     []Step
}

// Step is one compiled graph node.
type Step struct {
	StepRef      string
	StepCode     string
	Kind         StepKind
	Action       model.RailOperation
	DependsOn    []string
	Rail         model.Rail
	Gateway      string
	InstanceID   string
	HopIndex     uint32
	HopRole      paymenttypes.QuoteRouteHopRole
	Visibility   model.ReportVisibility
	UserLabel    string
	Comment      string
	CommitPolicy model.CommitPolicy
	CommitAfter  []string
	Metadata     map[string]string
}

// Custody classifies whether a rail is internal to orchestrator or external.
type Custody string

const (
	CustodyUnspecified Custody = "unspecified"
	CustodyInternal    Custody = "internal"
	CustodyExternal    Custody = "external"
)

// EndpointMatch defines optional selectors for one boundary endpoint.
type EndpointMatch struct {
	Rail    *model.Rail `json:"rail,omitempty" bson:"rail,omitempty"`
	Custody *Custody    `json:"custody,omitempty" bson:"custody,omitempty"`
	Gateway string      `json:"gateway,omitempty" bson:"gateway,omitempty"`
	Network string      `json:"network,omitempty" bson:"network,omitempty"`
	Method  string      `json:"method,omitempty" bson:"method,omitempty"`
}

// EdgeMatch defines source/target selectors for a boundary policy.
type EdgeMatch struct {
	Source EndpointMatch `json:"source" bson:"source"`
	Target EndpointMatch `json:"target" bson:"target"`
}

// PolicyStep defines one operation step emitted by a matching policy.
type PolicyStep struct {
	Code       string                 `json:"code" bson:"code"`
	Action     model.RailOperation    `json:"action" bson:"action"`
	Rail       *model.Rail            `json:"rail,omitempty" bson:"rail,omitempty"`
	DependsOn  []string               `json:"depends_on,omitempty" bson:"depends_on,omitempty"`
	Visibility model.ReportVisibility `json:"visibility,omitempty" bson:"visibility,omitempty"`
	UserLabel  string                 `json:"user_label,omitempty" bson:"user_label,omitempty"`
	Metadata   map[string]string      `json:"metadata,omitempty" bson:"metadata,omitempty"`
}

// Policy defines optional edge-specific expansion override.
type Policy struct {
	ID       string       `json:"id" bson:"id"`
	Enabled  *bool        `json:"enabled,omitempty" bson:"enabled,omitempty"`
	Priority int          `json:"priority,omitempty" bson:"priority,omitempty"`
	Match    EdgeMatch    `json:"match" bson:"match"`
	Steps    []PolicyStep `json:"steps" bson:"steps"`
	Success  []PolicyStep `json:"success,omitempty" bson:"success,omitempty"`
	Failure  []PolicyStep `json:"failure,omitempty" bson:"failure,omitempty"`
}

// Dependencies configures execution graph compiler integrations.
type Dependencies struct {
	Logger mlogger.Logger
}

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