package user

import (
	"context"
	"strconv"
	"strings"

	"github.com/perfect-panel/server/internal/model/log"
	"github.com/perfect-panel/server/internal/model/user"
	"github.com/perfect-panel/server/internal/svc"
	"github.com/perfect-panel/server/internal/types"
	"github.com/perfect-panel/server/pkg/constant"
	"github.com/perfect-panel/server/pkg/logger"
	"github.com/perfect-panel/server/pkg/xerr"
	"github.com/pkg/errors"
)

type commissionReturnLogRecord struct {
	LogID     int64
	UserID    int64
	Amount    int64
	EventType uint16
	Content   string
	CreatedAt int64
	UpdatedAt int64
}

type QueryCommissionReturnLogLogic struct {
	logger.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

// NewQueryCommissionReturnLogLogic Query Commission Return Log
func NewQueryCommissionReturnLogLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryCommissionReturnLogLogic {
	return &QueryCommissionReturnLogLogic{
		Logger: logger.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *QueryCommissionReturnLogLogic) QueryCommissionReturnLog(req *types.QueryCommissionReturnLogRequest) (*types.QueryCommissionReturnLogResponse, error) {
	u, ok := l.ctx.Value(constant.CtxKeyUser).(*user.User)
	if !ok {
		l.Error("current user is not found in context")
		return nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidAccess), "Invalid Access")
	}

	page, size := normalizePagination(req.Page, req.Size)
	list, total, err := l.queryCommissionReturnLogRecords(u.Id, page, size)
	if err != nil {
		return nil, err
	}

	respList := make([]types.CommissionReturnLog, 0, len(list))
	for _, item := range list {
		respList = append(respList, types.CommissionReturnLog{
			Id:        item.LogID,
			UserId:    item.UserID,
			Amount:    item.Amount,
			EventType: item.EventType,
			Content:   item.Content,
			CreatedAt: item.CreatedAt,
			UpdatedAt: item.UpdatedAt,
		})
	}

	return &types.QueryCommissionReturnLogResponse{
		List:  respList,
		Total: total,
	}, nil
}

func normalizePagination(page, size int) (int, int) {
	if page <= 0 {
		page = 1
	}
	if size <= 0 {
		size = 10
	}
	return page, size
}

func (l *QueryCommissionReturnLogLogic) queryCommissionReturnLogRecords(userID int64, page, size int) ([]commissionReturnLogRecord, int64, error) {
	return l.queryCommissionReturnLogRecordsByEventTypes(userID, page, size,
		log.CommissionTypeRefund,
		log.CommissionTypeWithdrawReject,
		log.CommissionTypeWithdrawCancel,
	)
}

func (l *QueryCommissionReturnLogLogic) queryCommissionReturnLogRecordsByEventTypes(userID int64, page, size int, eventTypes ...uint16) ([]commissionReturnLogRecord, int64, error) {
	if len(eventTypes) == 0 {
		return []commissionReturnLogRecord{}, 0, nil
	}

	likeClauses := make([]string, 0, len(eventTypes))
	args := make([]interface{}, 0, len(eventTypes)+2)
	args = append(args, log.TypeCommission.Uint8(), userID)
	for _, eventType := range eventTypes {
		likeClauses = append(likeClauses, "`content` LIKE ?")
		args = append(args, commissionReturnLogEventTypePattern(eventType))
	}
	query := l.svcCtx.DB.WithContext(l.ctx).
		Model(&log.SystemLog{}).
		Where("`type` = ? AND object_id = ? AND ("+strings.Join(likeClauses, " OR ")+")", args...)

	var total int64
	if err := query.Count(&total).Error; err != nil {
		return nil, 0, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "count commission return logs failed: %v", err)
	}

	var rows []log.SystemLog
	if err := query.Order("id DESC").Limit(size).Offset((page - 1) * size).Find(&rows).Error; err != nil {
		return nil, 0, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query commission return logs failed: %v", err)
	}

	list := make([]commissionReturnLogRecord, 0, len(rows))
	for _, row := range rows {
		var content log.Commission
		if err := content.Unmarshal([]byte(row.Content)); err != nil {
			l.Errorw("unmarshal commission return log content failed",
				logger.Field("log_id", row.Id),
				logger.Field("error", err.Error()),
			)
			continue
		}
		if !isCommissionReturnEventType(content.Type) {
			continue
		}

		timestamp := content.Timestamp
		if timestamp == 0 {
			timestamp = row.CreatedAt.UnixMilli()
		}
		list = append(list, commissionReturnLogRecord{
			LogID:     row.Id,
			UserID:    row.ObjectID,
			Amount:    content.Amount,
			EventType: content.Type,
			Content:   row.Content,
			CreatedAt: timestamp,
			UpdatedAt: timestamp,
		})
	}

	return list, total, nil
}

func commissionReturnLogEventTypePattern(eventType uint16) string {
	return "%\"type\":" + strconv.FormatUint(uint64(eventType), 10) + "%"
}

func isCommissionReturnEventType(eventType uint16) bool {
	switch eventType {
	case log.CommissionTypeRefund, log.CommissionTypeWithdrawReject, log.CommissionTypeWithdrawCancel:
		return true
	default:
		return false
	}
}
