package postgres

import (
	"context"

	"github.com/break/junhong_cmp_fiber/internal/model"
	"github.com/redis/go-redis/v9"
	"gorm.io/gorm"
)

type PaymentStore struct {
	db    *gorm.DB
	redis *redis.Client
}

func NewPaymentStore(db *gorm.DB, redis *redis.Client) *PaymentStore {
	return &PaymentStore{
		db:    db,
		redis: redis,
	}
}

func (s *PaymentStore) Create(ctx context.Context, payment *model.Payment) error {
	return s.db.WithContext(ctx).Create(payment).Error
}

func (s *PaymentStore) CreateWithTx(ctx context.Context, tx *gorm.DB, payment *model.Payment) error {
	return tx.WithContext(ctx).Create(payment).Error
}

func (s *PaymentStore) GetByPaymentNo(ctx context.Context, paymentNo string) (*model.Payment, error) {
	var payment model.Payment
	err := s.db.WithContext(ctx).
		Where("payment_no = ?", paymentNo).
		First(&payment).Error
	if err != nil {
		return nil, err
	}
	return &payment, nil
}

func (s *PaymentStore) GetByID(ctx context.Context, id uint) (*model.Payment, error) {
	var payment model.Payment
	err := s.db.WithContext(ctx).First(&payment, id).Error
	if err != nil {
		return nil, err
	}
	return &payment, nil
}

func (s *PaymentStore) UpdateStatus(ctx context.Context, id uint, status int) error {
	return s.db.WithContext(ctx).
		Model(&model.Payment{}).
		Where("id = ?", id).
		Update("status", status).Error
}

func (s *PaymentStore) UpdateStatusWithTx(ctx context.Context, tx *gorm.DB, id uint, status int) error {
	return tx.WithContext(ctx).
		Model(&model.Payment{}).
		Where("id = ?", id).
		Update("status", status).Error
}

func (s *PaymentStore) UpdateStatusWithOptimisticLock(ctx context.Context, id uint, oldStatus *int, newStatus int, paidAt interface{}) error {
	return s.UpdateStatusWithOptimisticLockDB(ctx, s.db, id, oldStatus, newStatus, paidAt)
}

func (s *PaymentStore) UpdateStatusWithOptimisticLockDB(ctx context.Context, db *gorm.DB, id uint, oldStatus *int, newStatus int, paidAt interface{}) error {
	updates := map[string]interface{}{
		"status": newStatus,
	}
	if paidAt != nil {
		updates["paid_at"] = paidAt
	}

	query := db.WithContext(ctx).Model(&model.Payment{}).Where("id = ?", id)

	if oldStatus != nil {
		query = query.Where("status = ?", *oldStatus)
	}

	result := query.Updates(updates)
	if result.Error != nil {
		return result.Error
	}
	if result.RowsAffected == 0 {
		return gorm.ErrRecordNotFound
	}
	return nil
}

func (s *PaymentStore) UpdatePaymentInfo(ctx context.Context, id uint, thirdPartyTradeNo string, paymentConfigID *uint) error {
	return s.UpdatePaymentInfoWithDB(ctx, s.db, id, thirdPartyTradeNo, paymentConfigID)
}

func (s *PaymentStore) UpdatePaymentInfoWithDB(ctx context.Context, db *gorm.DB, id uint, thirdPartyTradeNo string, paymentConfigID *uint) error {
	updates := map[string]interface{}{}
	if thirdPartyTradeNo != "" {
		updates["third_party_trade_no"] = thirdPartyTradeNo
	}
	if paymentConfigID != nil {
		updates["payment_config_id"] = *paymentConfigID
	}

	if len(updates) == 0 {
		return nil
	}

	result := db.WithContext(ctx).Model(&model.Payment{}).Where("id = ?", id).Updates(updates)
	if result.Error != nil {
		return result.Error
	}
	if result.RowsAffected == 0 {
		return gorm.ErrRecordNotFound
	}
	return nil
}

func (s *PaymentStore) ListByOrderID(ctx context.Context, orderID uint, orderType string) ([]*model.Payment, error) {
	var payments []*model.Payment
	err := s.db.WithContext(ctx).
		Where("order_id = ? AND order_type = ?", orderID, orderType).
		Order("created_at DESC").
		Find(&payments).Error
	if err != nil {
		return nil, err
	}
	return payments, nil
}

func (s *PaymentStore) ListByOrderIDAndStatus(ctx context.Context, orderID uint, orderType string, status int) (*model.Payment, error) {
	var payment model.Payment
	err := s.db.WithContext(ctx).
		Where("order_id = ? AND order_type = ? AND status = ?", orderID, orderType, status).
		First(&payment).Error
	if err != nil {
		return nil, err
	}
	return &payment, nil
}

func (s *PaymentStore) GetByOrderIDAndStatus(ctx context.Context, orderID uint, status int) (*model.Payment, error) {
	var payment model.Payment
	err := s.db.WithContext(ctx).
		Where("order_id = ? AND status = ?", orderID, status).
		First(&payment).Error
	if err != nil {
		return nil, err
	}
	return &payment, nil
}

// FindLatestPendingByOrderAndMethod 按业务单、支付方式查找最新一条待支付记录
// 用于支付宝支付单复用：同一业务单同一支付方式只允许一个有效 pending 支付单
func (s *PaymentStore) FindLatestPendingByOrderAndMethod(ctx context.Context, orderID uint, orderType string, paymentMethod string) (*model.Payment, error) {
	var payment model.Payment
	err := s.db.WithContext(ctx).
		Where("order_id = ? AND order_type = ? AND payment_method = ? AND status = ?",
			orderID, orderType, paymentMethod, model.PaymentRecordStatusPending).
		Order("created_at DESC").
		First(&payment).Error
	if err != nil {
		return nil, err
	}
	return &payment, nil
}
