package storage

import (
	"errors"
	"fmt"
	"time"

	"github.com/google/uuid"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"

	"mengyastore-backend/internal/database"
	"mengyastore-backend/internal/models"
)

const amountMatchEpsilon = 0.009

type OrderStore struct {
	db *gorm.DB
}

func NewOrderStore(db *gorm.DB) (*OrderStore, error) {
	return &OrderStore{db: db}, nil
}

func orderRowToModel(row database.OrderRow) models.Order {
	return models.Order{
		ID:                   row.ID,
		ProductID:            row.ProductID,
		ProductName:          row.ProductName,
		UserAccount:          row.UserAccount,
		UserName:             row.UserName,
		Quantity:             row.Quantity,
		DeliveredCodes:       row.DeliveredCodes,
		Status:               row.Status,
		DeliveryMode:         row.DeliveryMode,
		Note:                 row.Note,
		ContactPhone:         row.ContactPhone,
		ContactEmail:         row.ContactEmail,
		NotifyEmail:          row.NotifyEmail,
		PaymentMethod:        row.PaymentMethod,
		PaymentExpectedTotal: row.PaymentExpectedTotal,
		PaymentExpiresAt:     row.PaymentExpiresAt,
		CreatedAt:            row.CreatedAt,
	}
}

func (s *OrderStore) Create(order models.Order) (models.Order, error) {
	if order.ID == "" {
		order.ID = uuid.NewString()
	}
	if len(order.DeliveredCodes) == 0 {
		order.DeliveredCodes = []string{}
	}
	row := database.OrderRow{
		ID:                   order.ID,
		ProductID:            order.ProductID,
		ProductName:          order.ProductName,
		UserAccount:          order.UserAccount,
		UserName:             order.UserName,
		Quantity:             order.Quantity,
		DeliveredCodes:       database.StringSlice(order.DeliveredCodes),
		Status:               order.Status,
		DeliveryMode:         order.DeliveryMode,
		Note:                 order.Note,
		ContactPhone:         order.ContactPhone,
		ContactEmail:         order.ContactEmail,
		NotifyEmail:          order.NotifyEmail,
		PaymentMethod:        order.PaymentMethod,
		PaymentExpectedTotal: order.PaymentExpectedTotal,
		PaymentExpiresAt:     order.PaymentExpiresAt,
	}
	if err := s.db.Create(&row).Error; err != nil {
		return models.Order{}, err
	}
	order.CreatedAt = row.CreatedAt
	order.PaymentExpiresAt = row.PaymentExpiresAt
	return order, nil
}

// CreateTx 在已有事务内创建订单。
func (s *OrderStore) CreateTx(tx *gorm.DB, order models.Order) (models.Order, error) {
	if order.ID == "" {
		order.ID = uuid.NewString()
	}
	if len(order.DeliveredCodes) == 0 {
		order.DeliveredCodes = []string{}
	}
	row := database.OrderRow{
		ID:                   order.ID,
		ProductID:            order.ProductID,
		ProductName:          order.ProductName,
		UserAccount:          order.UserAccount,
		UserName:             order.UserName,
		Quantity:             order.Quantity,
		DeliveredCodes:       database.StringSlice(order.DeliveredCodes),
		Status:               order.Status,
		DeliveryMode:         order.DeliveryMode,
		Note:                 order.Note,
		ContactPhone:         order.ContactPhone,
		ContactEmail:         order.ContactEmail,
		NotifyEmail:          order.NotifyEmail,
		PaymentMethod:        order.PaymentMethod,
		PaymentExpectedTotal: order.PaymentExpectedTotal,
		PaymentExpiresAt:     order.PaymentExpiresAt,
	}
	if err := tx.Create(&row).Error; err != nil {
		return models.Order{}, err
	}
	order.CreatedAt = row.CreatedAt
	order.PaymentExpiresAt = row.PaymentExpiresAt
	return order, nil
}

func (s *OrderStore) GetByID(id string) (models.Order, error) {
	var row database.OrderRow
	if err := s.db.First(&row, "id = ?", id).Error; err != nil {
		return models.Order{}, fmt.Errorf("order not found")
	}
	return orderRowToModel(row), nil
}

func (s *OrderStore) Confirm(id string) (models.Order, error) {
	var row database.OrderRow
	if err := s.db.First(&row, "id = ?", id).Error; err != nil {
		return models.Order{}, fmt.Errorf("order not found")
	}
	if err := s.db.Model(&row).Update("status", "completed").Error; err != nil {
		return models.Order{}, err
	}
	row.Status = "completed"
	return orderRowToModel(row), nil
}

func (s *OrderStore) ListByAccount(account string) ([]models.Order, error) {
	var rows []database.OrderRow
	if err := s.db.Where("user_account = ?", account).Order("created_at DESC").Find(&rows).Error; err != nil {
		return nil, err
	}
	orders := make([]models.Order, len(rows))
	for i, r := range rows {
		orders[i] = orderRowToModel(r)
	}
	return orders, nil
}

func (s *OrderStore) ListAll() ([]models.Order, error) {
	var rows []database.OrderRow
	if err := s.db.Order("created_at DESC").Find(&rows).Error; err != nil {
		return nil, err
	}
	orders := make([]models.Order, len(rows))
	for i, r := range rows {
		orders[i] = orderRowToModel(r)
	}
	return orders, nil
}

func (s *OrderStore) CountPurchasedByAccount(account, productID string) (int, error) {
	var total int64
	// pending：手动待发货；pending_payment：萌芽支付待到账（会计入限购）。
	err := s.db.Model(&database.OrderRow{}).
		Where("user_account = ? AND product_id = ? AND status IN ?", account, productID, []string{"pending", "pending_payment", "completed"}).
		Select("COALESCE(SUM(quantity), 0)").Scan(&total).Error
	return int(total), err
}

func (s *OrderStore) Count() (int, error) {
	var count int64
	if err := s.db.Model(&database.OrderRow{}).Count(&count).Error; err != nil {
		return 0, err
	}
	return int(count), nil
}

func (s *OrderStore) Delete(id string) error {
	return s.db.Delete(&database.OrderRow{}, "id = ?", id).Error
}

func (s *OrderStore) UpdateCodes(id string, codes []string) error {
	return s.db.Model(&database.OrderRow{}).Where("id = ?", id).
		Update("delivered_codes", database.StringSlice(codes)).Error
}

// TryFulfillMengyaPayment 在事务内按 FIFO 匹配一笔待支付订单并置为 completed、增加销量。无匹配时 ok=false。
func (s *OrderStore) TryFulfillMengyaPayment(amount float64, now time.Time) (order models.Order, ok bool, err error) {
	err = s.db.Transaction(func(tx *gorm.DB) error {
		var row database.OrderRow
		res := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
			Where("status = ? AND payment_expires_at IS NOT NULL AND payment_expires_at > ?", "pending_payment", now).
			Where("ABS(payment_expected_total - ?) < ?", amount, amountMatchEpsilon).
			Order("created_at ASC").
			First(&row)
		if res.Error != nil {
			if errors.Is(res.Error, gorm.ErrRecordNotFound) {
				return nil
			}
			return res.Error
		}
		if err := tx.Model(&database.OrderRow{}).Where("id = ?", row.ID).Update("status", "completed").Error; err != nil {
			return err
		}
		if err := tx.Model(&database.ProductRow{}).Where("id = ?", row.ProductID).
			UpdateColumn("total_sold", gorm.Expr("total_sold + ?", row.Quantity)).Error; err != nil {
			return err
		}
		row.Status = "completed"
		order = orderRowToModel(row)
		ok = true
		return nil
	})
	if err != nil {
		return models.Order{}, false, err
	}
	return order, ok, nil
}

// ListExpiredPendingPayments 返回已过期仍未支付的订单（用于释放预留库存）。
func (s *OrderStore) ListExpiredPendingPayments(now time.Time) ([]models.Order, error) {
	var rows []database.OrderRow
	if err := s.db.Where("status = ? AND payment_expires_at IS NOT NULL AND payment_expires_at <= ?", "pending_payment", now).
		Find(&rows).Error; err != nil {
		return nil, err
	}
	out := make([]models.Order, len(rows))
	for i, r := range rows {
		out[i] = orderRowToModel(r)
	}
	return out, nil
}

// CancelPendingStaleIfEligible 仅在仍为 pending_payment 时将订单作废并清空预留内容；返回值 ok 表示成功取消（可安全退回库存）。
func (s *OrderStore) CancelPendingStaleIfEligible(id string) (ok bool, err error) {
	res := s.db.Model(&database.OrderRow{}).
		Where("id = ? AND status = ?", id, "pending_payment").
		Updates(map[string]interface{}{
			"status":          "cancelled",
			"delivered_codes": database.StringSlice([]string{}),
		})
	return res.RowsAffected > 0, res.Error
}
