package payment

import (
	"time"

	"cloud.o-forge.io/core/oc-lib/models/common/pricing"
	"cloud.o-forge.io/core/oc-lib/models/utils"
	"cloud.o-forge.io/core/oc-lib/tools"
)

type PaymentStatus int

const (
	PAYMENT_PENDING    PaymentStatus = iota
	PAYMENT_PROCESSING               // en cours de traitement blockchain/réseau
	PAYMENT_COMPLETED                // confirmé
	PAYMENT_FAILED                   // échoué
	PAYMENT_CANCELLED                // annulé avant exécution
	PAYMENT_REFUNDED                 // remboursé
)

func (s PaymentStatus) String() string {
	return [...]string{"pending", "processing", "completed", "failed", "cancelled", "refunded"}[s]
}

func PaymentStatusList() []PaymentStatus {
	return []PaymentStatus{PAYMENT_PENDING, PAYMENT_PROCESSING, PAYMENT_COMPLETED, PAYMENT_FAILED, PAYMENT_CANCELLED, PAYMENT_REFUNDED}
}

type PaymentMethod int

const (
	METHOD_BLOCKCHAIN     PaymentMethod = iota
	METHOD_CREDIT_CARD
	METHOD_BANK_TRANSFER
	METHOD_CRYPTO
	METHOD_INTERNAL_CREDIT // crédit interne à la plateforme
)

func (m PaymentMethod) String() string {
	return [...]string{"blockchain", "credit_card", "bank_transfer", "crypto", "internal_credit"}[m]
}

func PaymentMethodList() []PaymentMethod {
	return []PaymentMethod{METHOD_BLOCKCHAIN, METHOD_CREDIT_CARD, METHOD_BANK_TRANSFER, METHOD_CRYPTO, METHOD_INTERNAL_CREDIT}
}

// Payment représente une transaction de paiement — instantanée, mensuelle ou annuelle.
type Payment struct {
	utils.AbstractObject
	BillID          string              `json:"bill_id,omitempty" bson:"bill_id,omitempty"`
	InvoiceID       string              `json:"invoice_id,omitempty" bson:"invoice_id,omitempty"`
	SubscriptionID  string              `json:"subscription_id,omitempty" bson:"subscription_id,omitempty"`
	PayerPeerID     string              `json:"payer_peer_id,omitempty" bson:"payer_peer_id,omitempty"`
	RecipientPeerID string              `json:"recipient_peer_id,omitempty" bson:"recipient_peer_id,omitempty"`
	Amount          float64             `json:"amount" bson:"amount"`
	Currency        string              `json:"currency" bson:"currency" default:"EUR"`
	Status          PaymentStatus       `json:"status" bson:"status"`
	Method          PaymentMethod       `json:"method" bson:"method"`
	PaymentType     pricing.PaymentType `json:"payment_type" bson:"payment_type"` // PAY_ONCE, PAY_EVERY_MONTH, PAY_EVERY_YEAR
	TransactionID   string              `json:"transaction_id,omitempty" bson:"transaction_id,omitempty"`
	WalletFrom      string              `json:"wallet_from,omitempty" bson:"wallet_from,omitempty"`
	WalletTo        string              `json:"wallet_to,omitempty" bson:"wallet_to,omitempty"`
	ScheduledAt     *time.Time          `json:"scheduled_at,omitempty" bson:"scheduled_at,omitempty"`
	ProcessedAt     *time.Time          `json:"processed_at,omitempty" bson:"processed_at,omitempty"`
	FailureReason   string              `json:"failure_reason,omitempty" bson:"failure_reason,omitempty"`
	Metadata        map[string]string   `json:"metadata,omitempty" bson:"metadata,omitempty"`
}

// NewInstantPayment crée un paiement immédiat (PAY_ONCE).
func NewInstantPayment(billID, payerPeerID, recipientPeerID string, amount float64, currency string, method PaymentMethod) *Payment {
	return &Payment{
		BillID:          billID,
		PayerPeerID:     payerPeerID,
		RecipientPeerID: recipientPeerID,
		Amount:          amount,
		Currency:        currency,
		Status:          PAYMENT_PENDING,
		Method:          method,
		PaymentType:     pricing.PAY_ONCE,
	}
}

// NewScheduledPayment crée un paiement programmé (mensuel ou annuel).
func NewScheduledPayment(subscriptionID, payerPeerID, recipientPeerID string, amount float64, currency string, method PaymentMethod, paymentType pricing.PaymentType, scheduledAt time.Time) *Payment {
	return &Payment{
		SubscriptionID:  subscriptionID,
		PayerPeerID:     payerPeerID,
		RecipientPeerID: recipientPeerID,
		Amount:          amount,
		Currency:        currency,
		Status:          PAYMENT_PENDING,
		Method:          method,
		PaymentType:     paymentType,
		ScheduledAt:     &scheduledAt,
	}
}

// Complete marque le paiement comme confirmé.
func (p *Payment) Complete(transactionID string) {
	now := time.Now().UTC()
	p.Status = PAYMENT_COMPLETED
	p.TransactionID = transactionID
	p.ProcessedAt = &now
}

// Fail marque le paiement comme échoué.
func (p *Payment) Fail(reason string) {
	now := time.Now().UTC()
	p.Status = PAYMENT_FAILED
	p.FailureReason = reason
	p.ProcessedAt = &now
}

// Cancel annule le paiement s'il est encore en attente.
func (p *Payment) Cancel() bool {
	if p.Status != PAYMENT_PENDING {
		return false
	}
	p.Status = PAYMENT_CANCELLED
	return true
}

// IsRefundable indique si le paiement peut faire l'objet d'un remboursement.
func (p *Payment) IsRefundable() bool {
	return p.Status == PAYMENT_COMPLETED
}

func (p *Payment) GetAccessor(request *tools.APIRequest) utils.Accessor {
	return NewAccessor(request)
}

func (p *Payment) StoreDraftDefault() {
	p.IsDraft = true
}

func (p *Payment) CanUpdate(set utils.DBObject) (bool, utils.DBObject) {
	incoming := set.(*Payment)
	if !p.IsDraft && p.Status != incoming.Status {
		return true, &Payment{
			Status:        incoming.Status,
			TransactionID: incoming.TransactionID,
			FailureReason: incoming.FailureReason,
		}
	}
	return p.IsDraft, set
}

func (p *Payment) CanDelete() bool {
	return p.IsDraft || p.Status == PAYMENT_PENDING
}
