package refund

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 RefundStatus int

const (
	REFUND_PENDING    RefundStatus = iota
	REFUND_APPROVED                // approuvé, en attente de traitement
	REFUND_REJECTED                // rejeté
	REFUND_PROCESSING              // en cours de virement/blockchain
	REFUND_COMPLETED               // remboursé
	REFUND_CANCELLED               // annulé avant approbation
)

func (s RefundStatus) String() string {
	return [...]string{"pending", "approved", "rejected", "processing", "completed", "cancelled"}[s]
}

func RefundStatusList() []RefundStatus {
	return []RefundStatus{REFUND_PENDING, REFUND_APPROVED, REFUND_REJECTED, REFUND_PROCESSING, REFUND_COMPLETED, REFUND_CANCELLED}
}

// Refund représente une demande de remboursement sur un paiement validé.
type Refund struct {
	utils.AbstractObject
	PaymentID     string             `json:"payment_id" bson:"payment_id" validate:"required"`
	BillID        string             `json:"bill_id,omitempty" bson:"bill_id,omitempty"`
	InvoiceID     string             `json:"invoice_id,omitempty" bson:"invoice_id,omitempty"`
	RefundType    pricing.RefundType `json:"refund_type" bson:"refund_type"`
	Amount        float64            `json:"amount" bson:"amount" validate:"required"`
	Currency      string             `json:"currency" bson:"currency" default:"EUR"`
	Reason        string             `json:"reason,omitempty" bson:"reason,omitempty"`
	Status        RefundStatus       `json:"status" bson:"status"`
	RequestedAt   time.Time          `json:"requested_at" bson:"requested_at"`
	ProcessedAt   *time.Time         `json:"processed_at,omitempty" bson:"processed_at,omitempty"`
	ProcessedByID string             `json:"processed_by_id,omitempty" bson:"processed_by_id,omitempty"`
	TransactionID string             `json:"transaction_id,omitempty" bson:"transaction_id,omitempty"`
	Notes         string             `json:"notes,omitempty" bson:"notes,omitempty"`
	// ratio appliqué sur le montant original (0-100). 0 = non renseigné (remboursement total).
	RefundRatio float64 `json:"refund_ratio,omitempty" bson:"refund_ratio,omitempty"`
}

// NewRefund crée une demande de remboursement total.
func NewRefund(paymentID, billID string, amount float64, currency string, refundType pricing.RefundType, reason string) *Refund {
	return &Refund{
		PaymentID:   paymentID,
		BillID:      billID,
		Amount:      amount,
		Currency:    currency,
		RefundType:  refundType,
		Reason:      reason,
		Status:      REFUND_PENDING,
		RequestedAt: time.Now().UTC(),
	}
}

// NewPartialRefund crée une demande de remboursement partiel selon un ratio pourcentage.
func NewPartialRefund(paymentID, billID string, originalAmount, ratioPercent float64, currency string, refundType pricing.RefundType, reason string) *Refund {
	amount := originalAmount * ratioPercent / 100
	r := NewRefund(paymentID, billID, amount, currency, refundType, reason)
	r.RefundRatio = ratioPercent
	return r
}

// Approve approuve la demande de remboursement.
func (r *Refund) Approve(processedByID string) {
	r.Status = REFUND_APPROVED
	r.ProcessedByID = processedByID
}

// Reject rejette la demande de remboursement.
func (r *Refund) Reject(processedByID, notes string) {
	now := time.Now().UTC()
	r.Status = REFUND_REJECTED
	r.ProcessedByID = processedByID
	r.ProcessedAt = &now
	r.Notes = notes
}

// Process passe le remboursement en cours de traitement.
func (r *Refund) Process() bool {
	if r.Status != REFUND_APPROVED {
		return false
	}
	r.Status = REFUND_PROCESSING
	return true
}

// Complete finalise le remboursement avec l'identifiant de transaction.
func (r *Refund) Complete(transactionID string) {
	now := time.Now().UTC()
	r.Status = REFUND_COMPLETED
	r.TransactionID = transactionID
	r.ProcessedAt = &now
}

// Cancel annule la demande si elle est encore en attente.
func (r *Refund) Cancel() bool {
	if r.Status != REFUND_PENDING {
		return false
	}
	r.Status = REFUND_CANCELLED
	return true
}

func (r *Refund) GetAccessor(request *tools.APIRequest) utils.Accessor {
	return NewAccessor(request)
}

func (r *Refund) StoreDraftDefault() {
	r.IsDraft = true
}

func (r *Refund) CanUpdate(set utils.DBObject) (bool, utils.DBObject) {
	incoming := set.(*Refund)
	if !r.IsDraft && r.Status != incoming.Status {
		return true, &Refund{
			Status:        incoming.Status,
			TransactionID: incoming.TransactionID,
			Notes:         incoming.Notes,
			ProcessedByID: incoming.ProcessedByID,
		}
	}
	return r.IsDraft, set
}

func (r *Refund) CanDelete() bool {
	return r.IsDraft || r.Status == REFUND_PENDING
}
