package user

import (
	"context"
	"os"
	"strings"
	"time"

	logicCommon "github.com/perfect-panel/server/internal/logic/common"
	"github.com/perfect-panel/server/internal/model/log"
	"github.com/perfect-panel/server/internal/svc"
	"github.com/perfect-panel/server/internal/types"
	"github.com/perfect-panel/server/pkg/logger"
	"github.com/perfect-panel/server/pkg/tool"
	"github.com/perfect-panel/server/pkg/xerr"
	"github.com/pkg/errors"
	"gorm.io/gorm"
)

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

// NewUpdateUserBasicInfoLogic Update user basic info
func NewUpdateUserBasicInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateUserBasicInfoLogic {
	return &UpdateUserBasicInfoLogic{
		Logger: logger.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *UpdateUserBasicInfoLogic) UpdateUserBasicInfo(req *types.UpdateUserBasiceInfoRequest) error {
	userInfo, err := l.svcCtx.UserModel.FindOne(l.ctx, req.UserId)
	if err != nil {
		l.Errorw("[UpdateUserBasicInfoLogic] Find User Error:", logger.Field("err", err.Error()), logger.Field("userId", req.UserId))
		return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "Find User Error")
	}

	isDemo := strings.ToLower(os.Getenv("PPANEL_MODE")) == "demo"

	if req.Avatar != "" && !tool.IsValidImageSize(req.Avatar, 1024) {
		return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "Invalid Image Size")
	}

	err = l.svcCtx.UserModel.Transaction(l.ctx, func(tx *gorm.DB) error {
		if userInfo.Balance != req.Balance {
			change := req.Balance - userInfo.Balance
			balanceLog := log.Balance{
				Type:      log.BalanceTypeAdjust,
				Amount:    change,
				OrderNo:   "",
				Balance:   req.Balance,
				Timestamp: time.Now().UnixMilli(),
			}
			content, _ := balanceLog.Marshal()

			err = tx.Create(&log.SystemLog{
				Type:     log.TypeBalance.Uint8(),
				Date:     time.Now().Format(time.DateOnly),
				ObjectID: userInfo.Id,
				Content:  string(content),
			}).Error
			if err != nil {
				return err
			}
			userInfo.Balance = req.Balance
		}

		if userInfo.GiftAmount != req.GiftAmount {
			change := req.GiftAmount - userInfo.GiftAmount
			if change != 0 {
				var changeType uint16
				if userInfo.GiftAmount < req.GiftAmount {
					changeType = log.GiftTypeIncrease
				} else {
					changeType = log.GiftTypeReduce
				}
				giftLog := log.Gift{
					Type:      changeType,
					Amount:    change,
					Balance:   req.GiftAmount,
					Remark:    "Admin adjustment",
					Timestamp: time.Now().UnixMilli(),
				}
				content, _ := giftLog.Marshal()
				// Add gift amount change log
				err = tx.Create(&log.SystemLog{
					Type:     log.TypeGift.Uint8(),
					Date:     time.Now().Format(time.DateOnly),
					ObjectID: userInfo.Id,
					Content:  string(content),
				}).Error
				if err != nil {
					return err
				}
				userInfo.GiftAmount = req.GiftAmount
			}
		}

		if req.Commission != userInfo.Commission {
			if isWithdrawalScene(req.Remark) {
				logWithdrawalGuard(l.Logger, userInfo.Id)
				return errors.Wrapf(xerr.NewErrCode(xerr.InvalidAccess), "commission overwrite is blocked in withdrawal scene")
			}
			change := req.Commission - userInfo.Commission
			if err = l.svcCtx.UserModel.UpdateCommission(l.ctx, userInfo.Id, change, tx); err != nil {
				return err
			}
			if err = logicCommon.WriteCommissionLog(tx, userInfo.Id, log.CommissionTypeAdjust, change, ""); err != nil {
				return err
			}
			userInfo.Commission = req.Commission
		}
		if req.Avatar != "" {
			userInfo.Avatar = req.Avatar
		}
		if req.ReferCode != "" {
			userInfo.ReferCode = req.ReferCode
		}
		if req.RefererId != 0 {
			userInfo.RefererId = req.RefererId
		}
		if req.Enable != nil {
			userInfo.Enable = req.Enable
		}
		if req.IsAdmin != nil {
			userInfo.IsAdmin = req.IsAdmin
		}
		if req.Remark != "" {
			userInfo.Remark = req.Remark
		}
		if req.OnlyFirstPurchase != nil {
			userInfo.OnlyFirstPurchase = req.OnlyFirstPurchase
		}
		if req.ReferralPercentage != 0 {
			userInfo.ReferralPercentage = req.ReferralPercentage
		}

		if req.Password != "" {
			if userInfo.Id == 2 && isDemo {
				return errors.New("Demo mode does not allow modification of the admin user password")
			}
			userInfo.Password = tool.EncodePassWord(req.Password)
			userInfo.Algo = "default"
		}

		err = l.svcCtx.UserModel.Update(l.ctx, userInfo, tx)
		if err != nil {
			return err
		}

		return nil
	})

	if err != nil {
		l.Errorw("[UpdateUserBasicInfoLogic] Update User Error:", logger.Field("err", err.Error()), logger.Field("userId", req.UserId))
		if err.Error() == "Demo mode does not allow modification of the admin user password" {
			return errors.Wrapf(xerr.NewErrCodeMsg(503, "Demo mode does not allow modification of the admin user password"), "UpdateUserBasicInfo failed: cannot update admin user password in demo mode")
		}
		return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "Update User Error")
	}

	return nil
}
