package repoimpl

import (
	"context"
	"gorm.io/gorm"
	"time"
	"voucher/internal/biz/bo"
	"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() repo.OrderRepo {
	return &OrderRepoImpl{}
}

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

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:      vo.OrderStatusWait.GetValue(),
		AppID:       req.AppID,
		MerchantNo:  req.MerchantNo,
		Channel:     req.Channel.GetValue(),
		CreateTime:  &now,
		UpdateTime:  &now,
	}

	tx := p.db.DB(ctx).Create(info)
	if tx.Error != nil {
		return nil, tx.Error
	}

	return p.ToBo(info), nil
}

func (p *OrderRepoImpl) GetByOutBizNo(ctx context.Context, outBizNo string) (*bo.OrderBo, error) {
	info := &model.Order{}

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

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

	tx := p.DB(ctx).Where(model.Order{OrderNo: orderNo}).Find(&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()

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

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

	return nil
}

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

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

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

	return nil
}

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

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

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

	return nil
}
