package order

import (
	"encoding/json"
	"math"
	"strconv"
	"time"

	"hfb_sys/backend/internal/model"
	"hfb_sys/backend/pkg/money"

	"gorm.io/datatypes"
)

type orderPricing struct {
	RentAmountCent      int64
	OwnerRentAmountCent int64
	PlatformFeeCent     int64
}

type checkoutSettlement struct {
	OwnerRentIncomeCent     int64
	DepositCompensationCent int64
	OwnerIncomeCent         int64
	RentRefundCent          int64
	DepositRefundCent       int64
	RenterRefundCent        int64
	PlatformFeeCent         int64
	ActualRentAmountCent    int64
}

func orderDurationHours(order model.RentalOrder) int {
	if order.EstimatedDurationHours > 0 {
		return order.EstimatedDurationHours
	}
	return internalOrderHours
}

func orderEstimatedEndAt(order model.RentalOrder) *time.Time {
	if order.RentedAt == nil {
		return nil
	}
	durationHours := orderDurationHours(order)
	if durationHours <= 0 {
		return nil
	}
	endAt := order.RentedAt.Add(time.Duration(durationHours) * time.Hour)
	return &endAt
}

func estimateOrderDurationHours(snapshot datatypes.JSON) int {
	hafCoinM := readOrderSnapshotCoinM(snapshot)
	if hafCoinM <= 0 {
		return internalOrderHours
	}
	dailyLossM := readOrderSnapshotAssetNumber(snapshot, "daily_loss_m")
	if dailyLossM <= 0 {
		return internalOrderHours
	}
	days := hafCoinM / dailyLossM
	if days <= 0 {
		return internalOrderHours
	}
	hours := int(math.Round(days * 24))
	if hours <= 0 {
		return internalOrderHours
	}
	return hours
}

func buildOrderPricing(listing model.RentalListing, account model.GameAccount) orderPricing {
	rentAmountCent := listing.PriceCent
	ownerRentAmountCent := int64(math.Round(readSnapshotPrice(account.AssetSummary, "seller_total_price") * 100))
	if ownerRentAmountCent <= 0 || ownerRentAmountCent > rentAmountCent {
		ownerRentAmountCent = rentAmountCent
	}
	platformFeeCent := int64(math.Round(readSnapshotPrice(account.AssetSummary, "platform_markup_amount") * 100))
	if platformFeeCent <= 0 || ownerRentAmountCent+platformFeeCent != rentAmountCent {
		platformFeeCent = rentAmountCent - ownerRentAmountCent
	}
	if platformFeeCent < 0 {
		platformFeeCent = 0
	}
	return orderPricing{
		RentAmountCent:      rentAmountCent,
		OwnerRentAmountCent: ownerRentAmountCent,
		PlatformFeeCent:     platformFeeCent,
	}
}

func readSnapshotPrice(raw datatypes.JSON, key string) float64 {
	return readOrderSnapshotPrice(raw, key)
}

func readOrderSnapshotPrice(raw datatypes.JSON, key string) float64 {
	if len(raw) == 0 {
		return 0
	}
	var snapshot map[string]any
	if err := json.Unmarshal(raw, &snapshot); err != nil {
		return 0
	}
	if assetSummary, ok := snapshot["asset_summary"].(map[string]any); ok {
		snapshot = assetSummary
	}
	breakdown, ok := snapshot["price_breakdown"].(map[string]any)
	if !ok {
		return 0
	}
	return readJSONNumber(breakdown[key])
}

func readJSONNumber(value any) float64 {
	switch typed := value.(type) {
	case float64:
		return typed
	case float32:
		return float64(typed)
	case int:
		return float64(typed)
	case int64:
		return float64(typed)
	case json.Number:
		number, err := typed.Float64()
		if err != nil {
			return 0
		}
		return number
	case string:
		number, err := strconv.ParseFloat(typed, 64)
		if err != nil {
			return 0
		}
		return number
	default:
		return 0
	}
}

func buildCheckout(order model.RentalOrder, initiatedBy uint64, status string, content string, evidenceURLS []string, consumableAmountCent int64, coinConsumedM float64, otherAmountCent int64, explicitDeductCent int64, useExplicitDeduct bool) (model.OrderCheckout, error) {
	if consumableAmountCent < 0 || coinConsumedM < 0 || otherAmountCent < 0 || explicitDeductCent < 0 {
		return model.OrderCheckout{}, ErrInvalidCheckoutAmount
	}
	deductAmountCent := otherAmountCent
	if useExplicitDeduct {
		deductAmountCent = explicitDeductCent
	}
	if deductAmountCent > order.DepositAmountCent {
		return model.OrderCheckout{}, ErrInvalidCheckoutAmount
	}
	settlement := calculateCheckoutSettlement(order, consumableAmountCent, roundQuantity(coinConsumedM), deductAmountCent)
	evidence, err := marshalStringList(evidenceURLS)
	if err != nil {
		return model.OrderCheckout{}, err
	}
	return model.OrderCheckout{
		OrderID:                 order.ID,
		InitiatedBy:             initiatedBy,
		Status:                  status,
		RentAmountCent:          settlement.ActualRentAmountCent,
		OwnerRentAmountCent:     settlement.OwnerRentIncomeCent,
		PlatformFeeCent:         settlement.PlatformFeeCent,
		DepositAmountCent:       order.DepositAmountCent,
		ConsumableAmountCent:    consumableAmountCent,
		CoinConsumedM:           roundQuantity(coinConsumedM),
		OtherAmountCent:         otherAmountCent,
		DepositDeductAmountCent: settlement.DepositCompensationCent,
		RenterRefundAmountCent:  settlement.RenterRefundCent,
		OwnerIncomeAmountCent:   settlement.OwnerIncomeCent,
		Content:                 content,
		EvidenceURLS:            evidence,
	}, nil
}

func buildCheckoutSettlement(order model.RentalOrder, checkout *model.OrderCheckout) checkoutSettlement {
	return calculateCheckoutSettlement(order, checkout.ConsumableAmountCent, checkout.CoinConsumedM, checkout.DepositDeductAmountCent)
}

func calculateCheckoutSettlement(order model.RentalOrder, consumableAmountCent int64, coinConsumedM float64, depositDeductAmountCent int64) checkoutSettlement {
	orderRentAmountCent := order.RentAmountCent
	orderOwnerRentAmountCent := order.OwnerRentAmountCent
	orderDepositAmountCent := order.DepositAmountCent

	buyerCoinBasePriceCent := int64(math.Round(readOrderSnapshotPrice(order.AccountSnapshot, "buyer_coin_base_price") * 100))
	if buyerCoinBasePriceCent <= 0 || buyerCoinBasePriceCent > orderRentAmountCent {
		buyerCoinBasePriceCent = orderRentAmountCent
	}
	sellerCoinBasePriceCent := int64(math.Round(readOrderSnapshotPrice(order.AccountSnapshot, "seller_coin_base_price") * 100))
	if sellerCoinBasePriceCent <= 0 || sellerCoinBasePriceCent > orderOwnerRentAmountCent {
		sellerCoinBasePriceCent = orderOwnerRentAmountCent
	}
	prepaidConsumablePriceCent := int64(math.Round(readOrderSnapshotPrice(order.AccountSnapshot, "consumable_price") * 100))
	if prepaidConsumablePriceCent <= 0 || prepaidConsumablePriceCent > orderRentAmountCent-buyerCoinBasePriceCent {
		prepaidConsumablePriceCent = maxCent(orderRentAmountCent-buyerCoinBasePriceCent, 0)
	}
	prepaidOwnerConsumablePriceCent := maxCent(orderOwnerRentAmountCent-sellerCoinBasePriceCent, 0)
	totalCoinM := readOrderSnapshotCoinM(order.AccountSnapshot)
	coinUseRatio := 1.0
	if totalCoinM > 0 {
		coinUseRatio = minRatio(maxRatio(roundQuantity(coinConsumedM)/totalCoinM, 0), 1)
	}
	usedBuyerCoinPriceCent := int64(math.Round(float64(buyerCoinBasePriceCent) * coinUseRatio))
	usedOwnerCoinPriceCent := int64(math.Round(float64(sellerCoinBasePriceCent) * coinUseRatio))
	usedBuyerConsumablePriceCent := minCent(consumableAmountCent, prepaidConsumablePriceCent)
	consumableUseRatio := 1.0
	if prepaidConsumablePriceCent > 0 {
		consumableUseRatio = minRatio(maxRatio(float64(usedBuyerConsumablePriceCent)/float64(prepaidConsumablePriceCent), 0), 1)
	}
	usedOwnerConsumablePriceCent := int64(math.Round(float64(prepaidOwnerConsumablePriceCent) * consumableUseRatio))
	actualRentAmountCent := minCent(usedBuyerCoinPriceCent+usedBuyerConsumablePriceCent, orderRentAmountCent)
	ownerRentIncomeCent := minCent(usedOwnerCoinPriceCent+usedOwnerConsumablePriceCent, orderOwnerRentAmountCent)
	depositCompensationCent := minCent(depositDeductAmountCent, orderDepositAmountCent)
	rentRefundCent := maxCent(orderRentAmountCent-actualRentAmountCent, 0)
	depositRefundCent := maxCent(orderDepositAmountCent-depositCompensationCent, 0)

	return checkoutSettlement{
		OwnerRentIncomeCent:     ownerRentIncomeCent,
		DepositCompensationCent: depositCompensationCent,
		OwnerIncomeCent:         ownerRentIncomeCent + depositCompensationCent,
		RentRefundCent:          rentRefundCent,
		DepositRefundCent:       depositRefundCent,
		RenterRefundCent:        rentRefundCent + depositRefundCent,
		PlatformFeeCent:         maxCent(actualRentAmountCent-ownerRentIncomeCent, 0),
		ActualRentAmountCent:    actualRentAmountCent,
	}
}

func readOrderSnapshotCoinM(raw datatypes.JSON) float64 {
	if len(raw) == 0 {
		return 0
	}
	var snapshot map[string]any
	if err := json.Unmarshal(raw, &snapshot); err != nil {
		return 0
	}
	return roundQuantity(readJSONNumber(snapshot["haf_coin_amount"]) / 1000000)
}

func readOrderSnapshotAssetNumber(raw datatypes.JSON, key string) float64 {
	if len(raw) == 0 {
		return 0
	}
	var snapshot map[string]any
	if err := json.Unmarshal(raw, &snapshot); err != nil {
		return 0
	}
	assetSummary, ok := snapshot["asset_summary"].(map[string]any)
	if !ok {
		return readJSONNumber(snapshot[key])
	}
	if value, ok := assetSummary[key]; ok {
		return readJSONNumber(value)
	}
	return readJSONNumber(snapshot[key])
}

// roundMoney 使用统一的角精度（0.1元）
func roundMoney(value float64) float64 {
	return money.Round(value)
}

func roundQuantity(value float64) float64 {
	return math.Round(value*100) / 100
}

// minMoney 返回较小金额（角精度）
func minMoney(a float64, b float64) float64 {
	return money.Min(a, b)
}

// maxMoney 返回较大金额（角精度）
func maxMoney(a float64, b float64) float64 {
	return money.Max(a, b)
}

func minCent(a int64, b int64) int64 {
	if a < b {
		return a
	}
	return b
}

func maxCent(a int64, b int64) int64 {
	if a > b {
		return a
	}
	return b
}

func minRatio(a float64, b float64) float64 {
	if a < b {
		return a
	}
	return b
}

func maxRatio(a float64, b float64) float64 {
	if a > b {
		return a
	}
	return b
}
