package listing

import (
	"context"
	"math"
	"strings"
	"time"

	"hfb_sys/backend/internal/modules/notification"

	"gorm.io/gorm"
)

func (r *Repository) ListPendingReview(ctx context.Context) ([]ListingDTO, error) {
	var rows []listingRow
	err := r.baseQuery(ctx).
		Where("l.review_status = ? AND l.status <> ?", "pending", "offline").
		Order("l.updated_at ASC, l.id ASC").
		Limit(200).
		Scan(&rows).Error
	if err != nil {
		return nil, err
	}
	return rowsToDTO(rows), nil
}

func (r *Repository) AdminOffline(ctx context.Context, adminID uint64, listingID uint64, req AdminActionRequest, meta AuditMeta) (*ListingDTO, error) {
	return r.adminUpdateStatus(ctx, adminID, listingID, req, meta, "offline", "offline", "listing.admin_offline", "商品已被后台下架", "你的租号商品已被后台下架，请查看原因后处理。")
}

func (r *Repository) AdminMarkAbnormal(ctx context.Context, adminID uint64, listingID uint64, req AdminActionRequest, meta AuditMeta) (*ListingDTO, error) {
	return r.adminUpdateStatus(ctx, adminID, listingID, req, meta, "abnormal", "abnormal", "listing.mark_abnormal", "商品已被标记异常", "你的租号商品已被后台标记异常，请联系客服处理。")
}

func (r *Repository) adminUpdateStatus(ctx context.Context, adminID uint64, listingID uint64, req AdminActionRequest, meta AuditMeta, listingStatus string, accountStatus string, action string, title string, content string) (*ListingDTO, error) {
	var dto *ListingDTO
	err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		listing, account, err := r.findForReviewUpdate(tx, listingID)
		if err != nil {
			return err
		}
		if listing.Status == "rented" || listing.InTransaction {
			return ErrListingLocked
		}
		beforeListingStatus := listing.Status
		beforeAccountStatus := account.Status
		beforeReviewReason := listing.ReviewReason
		listing.Status = listingStatus
		listing.ReviewReason = req.Reason
		if listingStatus != "published" {
			listing.PublishedAt = nil
		}
		account.Status = accountStatus
		if err := tx.Save(account).Error; err != nil {
			return err
		}
		if err := tx.Save(listing).Error; err != nil {
			return err
		}
		if err := notification.Append(tx, notification.Entry{
			UserID:  listing.OwnerID,
			Type:    "listing_admin",
			Title:   title,
			Content: content,
			BizType: "listing",
			BizID:   &listingID,
		}); err != nil {
			return err
		}
		if err := appendAuditLog(tx, adminID, action, "listing", listing.ID, meta, map[string]any{
			"listing_id":            listing.ID,
			"account_id":            account.ID,
			"owner_id":              listing.OwnerID,
			"reason":                req.Reason,
			"before_listing_status": beforeListingStatus,
			"after_listing_status":  listing.Status,
			"before_account_status": beforeAccountStatus,
			"after_account_status":  account.Status,
			"before_review_reason":  beforeReviewReason,
			"after_review_reason":   listing.ReviewReason,
		}); err != nil {
			return err
		}
		dto = toDTO(*account, *listing)
		return nil
	})
	return dto, err
}

func (r *Repository) Approve(ctx context.Context, listingID uint64) (*ListingDTO, error) {
	var dto *ListingDTO
	err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		listing, account, err := r.findForReviewUpdate(tx, listingID)
		if err != nil {
			return err
		}
		if listing.Status == "rented" || listing.InTransaction {
			return ErrListingLocked
		}
		now := time.Now()
		listing.Status = "published"
		listing.ReviewStatus = "approved"
		listing.ReviewReason = ""
		listing.PublishedAt = &now
		account.Status = "published"
		if err := tx.Save(account).Error; err != nil {
			return err
		}
		if err := tx.Save(listing).Error; err != nil {
			return err
		}
		listingID := listing.ID
		if err := notification.Append(tx, notification.Entry{
			UserID:  listing.OwnerID,
			Type:    "listing_review",
			Title:   "发布审核通过",
			Content: "你的租号发布已审核通过并上架。",
			BizType: "listing",
			BizID:   &listingID,
		}); err != nil {
			return err
		}
		dto = toDTO(*account, *listing)
		return nil
	})
	return dto, err
}

func (r *Repository) AdjustReviewPrice(ctx context.Context, adminID uint64, listingID uint64, req AdminPriceAdjustRequest, meta AuditMeta) (*ListingDTO, error) {
	var dto *ListingDTO
	err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		listing, account, err := r.findForReviewUpdate(tx, listingID)
		if err != nil {
			return err
		}
		if listing.Status == "rented" || listing.InTransaction {
			return ErrListingLocked
		}
		summary := decodeAssetSummary(account.AssetSummary)
		if summary == nil {
			summary = map[string]any{}
		}
		breakdown := ensurePriceBreakdown(summary)
		coinWan := float64(account.HafCoinAmount) / 10000
		consumablePrice := readSummaryNumber(breakdown["consumable_price"])
		if consumablePrice <= 0 {
			consumablePrice = consumableValue(summary)
		}
		sellerTotalPrice := readSummaryNumber(breakdown["seller_total_price"])
		if sellerTotalPrice <= 0 {
			sellerTotalPrice = math.Max(0, centToYuan(listing.PriceCent)-consumablePrice)
		}
		sellerCoinBasePrice := readSummaryNumber(breakdown["seller_coin_base_price"])
		if sellerCoinBasePrice <= 0 {
			sellerCoinBasePrice = math.Max(0, sellerTotalPrice-consumablePrice)
		}
		sellerRatio := readSummaryNumber(breakdown["seller_ratio"])
		if sellerRatio <= 0 && sellerCoinBasePrice > 0 {
			sellerRatio = roundRatio(coinWan / sellerCoinBasePrice)
		}
		buyerCoinBasePrice, buyerTotalPrice, buyerRatio := calculateAdminAdjustedPrice(req, coinWan, consumablePrice)
		if buyerCoinBasePrice <= 0 || buyerTotalPrice <= 0 || buyerRatio <= 0 {
			return ErrInvalidPrice
		}

		beforePriceCent := listing.PriceCent
		beforeRatio := readSummaryNumber(breakdown["buyer_ratio"])
		if beforeRatio <= 0 && centToYuan(listing.PriceCent) > consumablePrice {
			beforeRatio = roundRatio(coinWan / (centToYuan(listing.PriceCent) - consumablePrice))
		}

		listing.PriceCent = yuanToCent(buyerTotalPrice)
		summary["publish_ratio"] = buyerRatio
		breakdown["seller_coin_base_price"] = roundMoney(sellerCoinBasePrice)
		breakdown["seller_total_price"] = roundMoney(sellerTotalPrice)
		breakdown["seller_ratio"] = sellerRatio
		breakdown["buyer_coin_base_price"] = buyerCoinBasePrice
		breakdown["buyer_total_price"] = buyerTotalPrice
		breakdown["buyer_ratio"] = buyerRatio
		breakdown["platform_markup_amount"] = roundMoney(buyerTotalPrice - sellerTotalPrice)
		breakdown["platform_rule_type"] = "admin_adjusted"
		breakdown["admin_adjust_reason"] = strings.TrimSpace(req.Reason)
		breakdown["admin_adjusted_at"] = time.Now().Format(time.RFC3339)
		breakdown["admin_adjusted_by"] = adminID
		summary["price_breakdown"] = breakdown

		assetSummary, err := marshalAssetSummary(summary)
		if err != nil {
			return err
		}
		account.AssetSummary = assetSummary
		if err := tx.Save(account).Error; err != nil {
			return err
		}
		if err := tx.Save(listing).Error; err != nil {
			return err
		}
		if err := appendAuditLog(tx, adminID, "listing.adjust_review_price", "listing", listing.ID, meta, map[string]any{
			"listing_id":         listing.ID,
			"account_id":         account.ID,
			"owner_id":           listing.OwnerID,
			"before_price_cent":  beforePriceCent,
			"after_price_cent":   listing.PriceCent,
			"before_buyer_ratio": beforeRatio,
			"after_buyer_ratio":  buyerRatio,
			"platform_markup":    breakdown["platform_markup_amount"],
			"adjust_reason":      req.Reason,
			"buyer_coin_base":    buyerCoinBasePrice,
			"consumable_price":   consumablePrice,
			"seller_total_price": sellerTotalPrice,
		}); err != nil {
			return err
		}
		dto = toDTO(*account, *listing)
		return nil
	})
	return dto, err
}

func (r *Repository) Reject(ctx context.Context, listingID uint64, req ReviewRequest) (*ListingDTO, error) {
	var dto *ListingDTO
	err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		listing, account, err := r.findForReviewUpdate(tx, listingID)
		if err != nil {
			return err
		}
		if listing.Status == "rented" || listing.InTransaction {
			return ErrListingLocked
		}
		listing.Status = "draft"
		listing.ReviewStatus = "rejected"
		listing.ReviewReason = req.Reason
		listing.PublishedAt = nil
		account.Status = "draft"
		if err := tx.Save(account).Error; err != nil {
			return err
		}
		if err := tx.Save(listing).Error; err != nil {
			return err
		}
		listingID := listing.ID
		if err := notification.Append(tx, notification.Entry{
			UserID:  listing.OwnerID,
			Type:    "listing_review",
			Title:   "发布审核未通过",
			Content: "你的租号发布未通过审核，请根据原因修改后重新提交。",
			BizType: "listing",
			BizID:   &listingID,
		}); err != nil {
			return err
		}
		dto = toDTO(*account, *listing)
		return nil
	})
	return dto, err
}
