package repoimpl

import (
	"context"
	"errors"
	"fmt"
	"gorm.io/gorm"
	"time"
	err2 "voucher/api/err"
	"voucher/internal/biz/bo"
	"voucher/internal/biz/do"
	"voucher/internal/biz/repo"
	"voucher/internal/biz/vo"
	"voucher/internal/data"
	"voucher/internal/data/model"
)

// OrderBakRepoImpl .
type OrderBakRepoImpl struct {
	Base[model.OrderBak, bo.OrderBo]
	db *data.Db
}

// NewOrderBakRepoImpl .
func NewOrderBakRepoImpl(db *data.Db) repo.OrderBakRepo {
	return &OrderBakRepoImpl{db: db}
}

func (p *OrderBakRepoImpl) DB(ctx context.Context) *gorm.DB {
	return p.db.DB(ctx).Model(model.OrderBak{})
}

func (p *OrderBakRepoImpl) SpecifyFindInBatches(ctx context.Context, req *bo.FindInBatchesBo, fun func(ctx context.Context, rows []*bo.OrderBo) error) error {

	tx := p.DB(ctx).Where("activity_id = ''")

	if req.ProductNo != "" {
		tx = tx.Where("product_no = ?", req.ProductNo)
	}

	if req.StartTime != "" && req.EndTime != "" {
		tx = tx.Where("receive_success_time BETWEEN ? AND ?", req.StartTime, req.EndTime)
	}

	if req.OrderNos != nil {
		tx = tx.Where("order_no IN (?)", req.OrderNos)
	}

	if req.OutBizNos != nil {
		tx = tx.Where("out_biz_no IN (?)", req.OutBizNos)
	}

	if req.VoucherNos != nil {
		tx = tx.Where("voucher_no IN (?)", req.VoucherNos)
	}

	var results = make([]*model.OrderBak, 0)

	result := tx.FindInBatches(&results, 100, func(tx *gorm.DB, batch int) error {

		return fun(ctx, p.ToBos(results))
	})

	if result.Error != nil {
		return result.Error
	}

	return nil
}

func (p *OrderBakRepoImpl) FindRetryQuery(ctx context.Context, req *do.RetryQueryNotice, fun func(ctx context.Context, rows []*bo.OrderBo) error) error {

	statusArr := []uint8{
		vo.OrderStatusSuccess.GetValue(),
		vo.OrderStatusUse.GetValue(),
		vo.OrderStatusExpired.GetValue(),
	}

	tx := p.DB(ctx).
		Where("`status` in (?)", statusArr).
		Where("activity_id = ''")

	if req.ProductNo != "" {
		tx = tx.Where("product_no = ?", req.ProductNo)
	}

	if req.ReceiveSuccessStartTime != "" {
		tx = tx.Where("receive_success_time > ?", req.ReceiveSuccessStartTime)
	}
	if req.ReceiveSuccessEndTime != "" {
		tx = tx.Where("receive_success_time <= ?", req.ReceiveSuccessEndTime)
	}

	if req.ProductNo != "" {
		tx = tx.Where("product_no = ?", req.ProductNo)
	}

	if req.OrderNos != nil {
		tx = tx.Where("order_no IN (?)", req.OrderNos)
	}

	if req.OutBizNos != nil {
		tx = tx.Where("out_biz_no IN (?)", req.OutBizNos)
	}

	if req.VoucherNos != nil {
		tx = tx.Where("voucher_no IN (?)", req.VoucherNos)
	}

	var results = make([]*model.OrderBak, 0)

	tx.Order("receive_success_time asc") // 显式清除排序，移除默认的 ORDER BY

	result := tx.FindInBatches(&results, 1000, func(tx *gorm.DB, batch int) error {

		return fun(ctx, p.ToBos(results))
	})

	if result.Error != nil {
		return result.Error
	}

	return nil
}

func (p *OrderBakRepoImpl) GetByID(ctx context.Context, id uint64) (*bo.OrderBo, error) {
	info := &model.OrderBak{}

	tx := p.DB(ctx).Where(model.OrderBak{ID: id}).First(&info)

	if tx.Error != nil {

		if errors.Is(tx.Error, gorm.ErrRecordNotFound) {
			return nil, err2.ErrorDbNotFound("订单数据不存在")
		}

		return nil, fmt.Errorf("order db fail %w", tx.Error)
	}

	if tx.RowsAffected == 0 {
		return nil, err2.ErrorDbNotFound("订单数据不存在")
	}

	return p.ToBo(info), nil
}

func (p *OrderBakRepoImpl) Used(ctx context.Context, id uint64) error {
	now := time.Now()

	tx := p.DB(ctx).
		Where(model.OrderBak{
			ID: id,
		}).
		Updates(model.OrderBak{
			Status:      vo.OrderStatusUse.GetValue(),
			Remark:      "核销",
			LastUseTime: &now,
			UpdateTime:  &now,
		})

	if tx.Error != nil {
		return fmt.Errorf("update db fail %w", tx.Error)
	}

	return nil
}

func (p *OrderBakRepoImpl) Expired(ctx context.Context, id uint64) error {
	now := time.Now()

	tx := p.DB(ctx).
		Where(model.OrderBak{
			ID: id,
		}).
		Updates(model.OrderBak{
			Status:     vo.OrderStatusExpired.GetValue(),
			Remark:     "过期",
			UpdateTime: &now,
		})

	if tx.Error != nil {
		return fmt.Errorf("update db fail %w", tx.Error)
	}

	return nil
}

func (p *OrderBakRepoImpl) Available(ctx context.Context, id uint64) error {
	now := time.Now()

	tx := p.DB(ctx).
		Where(model.OrderBak{
			ID:     id,
			Status: vo.OrderStatusUse.GetValue(),
		}).
		Updates(model.OrderBak{
			Status:             vo.OrderStatusSuccess.GetValue(),
			Remark:             "重置为成功，领取成功时间重置",
			ReceiveSuccessTime: &now, // 领取成功时间重置
			UpdateTime:         &now,
		})

	if tx.Error != nil {
		return fmt.Errorf("update db fail %w", tx.Error)
	}

	return nil
}
