package repoimpl

import (
	"context"
	"errors"
	"fmt"
	"gorm.io/gorm"
	"time"
	"unicode/utf8"
	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"
)

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

// NewOrderRepoImpl .
func NewOrderRepoImpl(db *data.Db) repo.OrderRepo {
	return &OrderRepoImpl{db: db}
}

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

func (p *OrderRepoImpl) 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)
	}

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

	var results = make([]*model.Order, 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 *OrderRepoImpl) FinSucByStockIdInBatches(ctx context.Context, req *do.WechatQuery, fun func(ctx context.Context, rows []*bo.OrderBo) error) error {

	tx := p.DB(ctx).
		Where("`status` in (?)", []uint8{
			vo.OrderStatusSuccess.GetValue(),
			vo.OrderStatusUse.GetValue(),
			vo.OrderStatusExpired.GetValue(),
		}).Where("activity_id = ''")

	if req.ProductNo != "" {
		tx = tx.Where("product_no = ?", req.ProductNo)
	}
	if req.BatchNo != "" {
		tx = tx.Where("batch_no = ?", req.BatchNo)
	}
	if req.StartTime != "" {
		tx = tx.Where("receive_success_time > ?", req.StartTime)
	}
	if req.EndTime != "" {
		tx = tx.Where("receive_success_time <= ?", req.EndTime)
	}
	if req.OrderNo != "" {
		tx = tx.Where("order_no = ?", req.OrderNo)
	}

	var results = make([]*model.Order, 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 *OrderRepoImpl) FinUsedInBatches(ctx context.Context, req *do.WechatUsedQuery, fun func(ctx context.Context, rows []*bo.OrderBo) error) error {

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

	tx := p.DB(ctx).
		Where("`status` = ?", vo.OrderStatusUse.GetValue()).
		Where("activity_id = ''")

	if req.StartTime != "" {
		tx = tx.Where("last_use_time > ?", req.StartTime)
	}
	if req.EndTime != "" {
		tx = tx.Where("last_use_time <= ?", req.EndTime)
	}
	if req.ProductNo != "" {
		tx = tx.Where("product_no = ?", req.ProductNo)
	}
	if req.BatchNo != "" {
		tx = tx.Where("batch_no = ?", req.ProductNo)
	}
	if req.OrderNo != "" {
		tx = tx.Where("order_no = ?", req.OrderNo)
	}

	// 显式清除排序，移除默认的 ORDER BY
	result := tx.Order("last_use_time asc").FindInBatches(&results, 500, func(tx *gorm.DB, batch int) error {
		return fun(ctx, p.ToBos(results))
	})

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

	return nil
}

//func (p *OrderRepoImpl) FinFailByStockIdInBatches(ctx context.Context, batchNo string, fun func(ctx context.Context, rows []*bo.OrderBo) error) error {
//
//	var results = make([]*model.Order, 0)
//
//	result := p.DB(ctx).
//		Where("batch_no = ?", batchNo).
//		Where("`status` = ?", vo.OrderStatusFail.GetValue()).
//		Order("receive_success_time asc"). // 显式清除排序，移除默认的 ORDER BY
//		FindInBatches(&results, 200, func(tx *gorm.DB, batch int) error {
//			return fun(ctx, p.ToBos(results))
//		})
//
//	if result.Error != nil {
//		return result.Error
//	}
//
//	return nil
//}

//func (p *OrderRepoImpl) FindIngInBatches(ctx context.Context, fun func(ctx context.Context, rows []*bo.OrderBo) error) error {
//
//	var results = make([]*model.Order, 0)
//
//	result := p.DB(ctx).
//		Where("`status` = ?", vo.OrderStatusIng.GetValue()).
//		Limit(20).
//		Order("receive_success_time asc"). // 显式清除排序，移除默认的 ORDER BY
//		FindInBatches(&results, 10, func(tx *gorm.DB, batch int) error {
//			return fun(ctx, p.ToBos(results))
//		})
//
//	if result.Error != nil {
//		return result.Error
//	}
//
//	return nil
//}

func (p *OrderRepoImpl) FindInBatches(ctx context.Context, req *bo.FindInBatchesUseBo, fun func(ctx context.Context, rows []*bo.OrderBo) error) error {

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

	result := p.DB(ctx).
		Where("activity_id = ''").
		Where("`status` IN (?)", []uint8{vo.OrderStatusSuccess.GetValue(), vo.OrderStatusUse.GetValue()}).
		Where("receive_success_time BETWEEN ? AND ?", req.StartTime, req.EndTime).
		Order("receive_success_time asc"). // 显式清除排序，移除默认的 ORDER BY
		FindInBatches(&results, 1000, func(tx *gorm.DB, batch int) error {
			// tx.RowsAffected 提供当前批处理中记录的计数（the count of records in the current batch）
			// 'batch' 变量表示当前批号（the current batch number）
			// 返回 error 将阻止更多的批处理
			return fun(ctx, p.ToBos(results))
		})

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

	return nil
}

func (p *OrderRepoImpl) 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.Order, 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 *OrderRepoImpl) Create(ctx context.Context, req *bo.OrderBo) (*bo.OrderBo, error) {

	now := time.Now()

	info := &model.Order{
		OrderNo:     req.OrderNo,
		OutBizNo:    req.OutBizNo,
		ProductNo:   req.ProductNo,
		BatchNo:     req.BatchNo,
		Account:     req.Account,
		AccountType: req.AccountType.GetValue(),
		Status:      req.Status.GetValue(),
		Type:        req.Type.GetValue(),
		AppID:       req.AppID,
		MerchantNo:  req.MerchantNo,
		Channel:     req.Channel.GetValue(),
		NotifyUrl:   req.NotifyUrl,
		Attach:      req.Attach,
		CreateTime:  &now,
		UpdateTime:  &now,

		ActivityId: req.ActivityId,
	}

	tx := p.DB(ctx).Create(info)

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

	return p.ToBo(info), nil
}

func (p *OrderRepoImpl) GetByOutBizNo(ctx context.Context, t vo.OrderType, outBizNo string) (*bo.OrderBo, error) {

	info := &model.Order{}

	tx := p.DB(ctx).Where(model.Order{Type: t.GetValue(), OutBizNo: outBizNo}).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 *OrderRepoImpl) GetByID(ctx context.Context, id uint64) (*bo.OrderBo, error) {
	info := &model.Order{}

	tx := p.DB(ctx).Where(model.Order{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 *OrderRepoImpl) GetByOrderNo(ctx context.Context, orderNo string) (*bo.OrderBo, error) {
	info := &model.Order{}

	tx := p.DB(ctx).Where(model.Order{OrderNo: orderNo}).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 *OrderRepoImpl) GetByCouponId(ctx context.Context, merchantNo, batchNo, voucherNo string) (*bo.OrderBo, error) {
	info := &model.Order{}

	tx := p.DB(ctx).Where(model.Order{MerchantNo: merchantNo, BatchNo: batchNo, VoucherNo: voucherNo}).First(&info)

	if tx.Error != nil {
		return nil, tx.Error
	}

	if tx.RowsAffected == 0 {
		return nil, gorm.ErrRecordNotFound
	}

	return p.ToBo(info), nil
}

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

	tx := p.DB(ctx).
		Where(model.Order{
			ID:     id,
			Status: vo.OrderStatusWait.GetValue(),
		}).
		Updates(model.Order{
			Status:     vo.OrderStatusIng.GetValue(),
			UpdateTime: &now,
		})

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

	return nil
}

func (p *OrderRepoImpl) Success(ctx context.Context, id uint64, voucherNo string) error {
	now := time.Now()

	tx := p.DB(ctx).
		Where(model.Order{
			ID: id,
		}).
		Updates(model.Order{
			Status:             vo.OrderStatusSuccess.GetValue(),
			VoucherNo:          voucherNo,
			Remark:             "成功",
			ReceiveSuccessTime: &now,
			UpdateTime:         &now,
		})

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

	if tx.RowsAffected == 0 {
		return fmt.Errorf("no rows affected")
	}

	return nil
}

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

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

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

	return nil
}

func (p *OrderRepoImpl) Fail(ctx context.Context, id uint64, remark string) error {
	now := time.Now()

	if utf8.RuneCountInString(remark) > 100 {
		runes := []rune(remark)
		if len(runes) > 100 {
			remark = string(runes[:100])
		}
	}

	tx := p.DB(ctx).
		Where(model.Order{
			ID: id,
		}).
		Updates(model.Order{
			Status:     vo.OrderStatusFail.GetValue(),
			Remark:     remark,
			UpdateTime: &now,
		})

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

	if tx.RowsAffected == 0 {
		return fmt.Errorf("no rows affected")
	}

	return nil
}

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

	tx := p.DB(ctx).
		Where(model.Order{
			ID: id,
		}).
		Updates(model.Order{
			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 *OrderRepoImpl) MultiLastUsed(ctx context.Context, id uint64, lastUseTime time.Time) error {
	now := time.Now()

	tx := p.DB(ctx).
		Where(model.Order{
			ID: id,
		}).
		Updates(model.Order{
			Remark:      "核销",
			LastUseTime: &lastUseTime,
			UpdateTime:  &now,
		})

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

	return nil
}

func (p *OrderRepoImpl) MultiOverUsed(ctx context.Context, id uint64, lastUseTime time.Time, remark string) error {
	now := time.Now()

	tx := p.DB(ctx).
		Where(model.Order{
			ID: id,
		}).
		Updates(model.Order{
			Status:      vo.OrderStatusUse.GetValue(),
			Remark:      remark,
			LastUseTime: &lastUseTime,
			UpdateTime:  &now,
		})

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

	return nil
}

func (p *OrderRepoImpl) NotifyUsed(ctx context.Context, id uint64, transactionId string, lastUseTime time.Time) error {
	now := time.Now()

	tx := p.DB(ctx).
		Where(model.Order{
			ID: id,
		}).
		Updates(model.Order{
			Status:        vo.OrderStatusUse.GetValue(),
			TransactionId: transactionId,
			Remark:        "微信回调核销",
			LastUseTime:   &lastUseTime,
			UpdateTime:    &now,
		})

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

	return nil
}

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

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

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

	return nil
}
