package logic

import (
	"context"
	"errors"
	"strconv"
	"time"

	"juwan-backend/app/wallet/rpc/internal/models/predicate"
	"juwan-backend/app/wallet/rpc/internal/models/wallettransactions"
	"juwan-backend/app/wallet/rpc/internal/svc"
	"juwan-backend/app/wallet/rpc/pb"

	"github.com/shopspring/decimal"

	"github.com/zeromicro/go-zero/core/logx"
)

type SearchWalletTransactionsLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

func NewSearchWalletTransactionsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SearchWalletTransactionsLogic {
	return &SearchWalletTransactionsLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

func (l *SearchWalletTransactionsLogic) SearchWalletTransactions(in *pb.SearchWalletTransactionsReq) (*pb.SearchWalletTransactionsResp, error) {
	if in.Limit <= 0 {
		in.Limit = 20
	}
	if in.Limit > 100 {
		return nil, errors.New("limit too large")
	}
	if in.Offset < 0 {
		in.Offset = 0
	}

	preds := make([]predicate.WalletTransactions, 0, 8)
	if in.Id > 0 {
		preds = append(preds, wallettransactions.IDEQ(strconv.FormatInt(in.Id, 10)))
	}
	if in.UserId != nil {
		preds = append(preds, wallettransactions.UserIDEQ(in.GetUserId()))
	}
	if in.Type != nil && in.GetType() != "" {
		preds = append(preds, wallettransactions.TypeEQ(in.GetType()))
	}
	if in.Amount != nil {
		amount, perr := decimal.NewFromString(in.GetAmount())
		if perr != nil {
			return nil, errors.New("invalid amount")
		}
		preds = append(preds, wallettransactions.AmountEQ(amount))
	}
	if in.BalanceAfter != nil {
		balanceAfter, perr := decimal.NewFromString(in.GetBalanceAfter())
		if perr != nil {
			return nil, errors.New("invalid balanceAfter")
		}
		preds = append(preds, wallettransactions.BalanceAfterEQ(balanceAfter))
	}
	if in.OrderId != nil {
		preds = append(preds, wallettransactions.OrderIDEQ(in.GetOrderId()))
	}
	if in.CreatedAt != nil && in.GetCreatedAt() > 0 {
		preds = append(preds, wallettransactions.CreatedAtGTE(time.Unix(in.GetCreatedAt(), 0)))
	}
	if in.SearchText != nil && in.GetSearchText() != "" {
		preds = append(preds, wallettransactions.SearchTextContainsFold(in.GetSearchText()))
	}

	query := l.svcCtx.WalletModelsRO.WalletTransactions.Query()
	if len(preds) > 0 {
		query = query.Where(wallettransactions.And(preds...))
	}

	list, err := query.
		Offset(int(in.Offset)).
		Limit(int(in.Limit)).
		All(l.ctx)
	if err != nil {
		return nil, err
	}

	result := make([]*pb.WalletTransactions, 0, len(list))
	for _, item := range list {
		id, _ := strconv.ParseInt(item.ID, 10, 64)
		description := ""
		if len(item.Description) > 0 {
			description = item.Description[0]
		}
		result = append(result, &pb.WalletTransactions{
			Id:           id,
			UserId:       item.UserID,
			Type:         item.Type,
			Amount:       item.Amount.String(),
			BalanceAfter: item.BalanceAfter.String(),
			Description:  description,
			OrderId:      item.OrderID,
			CreatedAt:    item.CreatedAt.Unix(),
			SearchText:   item.SearchText,
		})
	}

	return &pb.SearchWalletTransactionsResp{WalletTransactions: result}, nil
}
