package usecase

import (
	errs "backend/pkg/library/errors"
	"context"
	"errors"
	"fmt"
	"math"

	"go.mongodb.org/mongo-driver/v2/mongo"

	"backend/pkg/member/domain/entity"
	"backend/pkg/member/domain/member"
	"backend/pkg/member/domain/repository"
	"backend/pkg/member/domain/usecase"

	"github.com/zeromicro/go-zero/core/stores/mon"
)

// HasPasswordFunc is a function variable for password hashing, used for testing
var HasPasswordFunc = HashPassword

// CreateUserAccount creates a new user account with the provided credentials.
// It handles password hashing for Digimon platform accounts and stores the account data.
func (use *MemberUseCase) CreateUserAccount(ctx context.Context, req usecase.CreateLoginUserRequest) error {
	// Validate input
	if err := use.validateCreateUserAccountRequest(req); err != nil {
		return err
	}

	// Process password for Digimon platform
	token, err := use.processPasswordForPlatform(req.Platform, req.Token)
	if err != nil {
		return err
	}

	// Create account entity
	account := &entity.Account{
		LoginID:  req.LoginID,
		Token:    token,
		Platform: req.Platform,
	}

	// Insert account into database
	return use.insertAccount(ctx, account, req)
}

// validateCreateUserAccountRequest validates the create user account request
func (use *MemberUseCase) validateCreateUserAccountRequest(req usecase.CreateLoginUserRequest) error {
	if req.LoginID == "" {
		return errs.InputInvalidRangeError("login ID is required")
	}

	if req.Platform == member.Digimon && req.Token == "" {
		return errs.InputInvalidRangeError("password is required for Digimon platform")
	}

	return nil
}

// processPasswordForPlatform processes the password based on the platform type
func (use *MemberUseCase) processPasswordForPlatform(platform member.Platform, password string) (string, error) {
	if platform != member.Digimon {
		return "", nil // No password processing for non-Digimon platforms
	}

	// Hash password for Digimon platform
	token, err := HasPasswordFunc(password, use.Config.Bcrypt.Cost)
	if err != nil {
		e := errs.SvcInternalErrorL(
			use.Logger,
			[]errs.LogField{
				{Key: "platform", Val: platform},
				{Key: "password", Val: password},
				{Key: "func", Val: "HasPasswordFunc"},
				{Key: "error", Val: err.Error()},
			},
			fmt.Sprintf("failed to encrypt password"),
		).Wrap(err)

		return "", e
	}

	return token, nil
}

// insertAccount inserts the account into the database
func (use *MemberUseCase) insertAccount(ctx context.Context, account *entity.Account, req usecase.CreateLoginUserRequest) error {
	err := use.Account.Insert(ctx, account)

	if err != nil {
		if mongo.IsDuplicateKeyError(err) {
			return errs.ResAlreadyExistError("account duplicate").Wrap(err)
		}

		return errs.DBErrorErrorL(use.Logger,
			[]errs.LogField{
				{Key: "req", Val: req},
				{Key: "func", Val: "Account.Insert"},
				{Key: "err", Val: err.Error()},
			},
			"failed to insert to database").Wrap(err)
	}

	return nil
}

func (use *MemberUseCase) GetUIDByAccount(ctx context.Context, req usecase.GetUIDByAccountRequest) (usecase.GetUIDByAccountResponse, error) {
	account, err := use.AccountUID.FindUIDByLoginID(ctx, req.Account)
	if err != nil {
		var e *errs.Error
		switch {
		case errors.Is(err, mon.ErrNotFound):
			e = errs.ResNotFoundError(
				fmt.Sprintf("failed to find uid by account: %s", req.Account),
			)
		default:
			e = errs.DBErrorErrorL(
				use.Logger,
				[]errs.LogField{
					{Key: "req", Val: req},
					{Key: "func", Val: "AccountUID.FindUIDByLoginID"},
					{Key: "err", Val: err.Error()},
				},
				"failed to find uid by account").Wrap(err)
		}

		return usecase.GetUIDByAccountResponse{}, e
	}

	return usecase.GetUIDByAccountResponse{
		UID:     account.UID,
		Account: req.Account,
	}, nil
}

func (use *MemberUseCase) GetUserAccountInfo(ctx context.Context, req usecase.GetUIDByAccountRequest) (usecase.GetAccountInfoResponse, error) {
	account, err := use.Account.FindOneByAccount(ctx, req.Account)
	if err != nil {
		var e *errs.Error
		switch {
		case errors.Is(err, mon.ErrNotFound):
			// 錯誤代碼 20-301-08
			e = errs.ResNotFoundError(
				fmt.Sprintf("failed to find account: %s", req.Account),
			)
		default:
			// 錯誤代碼 20-201-08
			e = errs.DBErrorErrorL(
				use.Logger,
				[]errs.LogField{
					{Key: "req", Val: req},
					{Key: "func", Val: "Account.FindOneByAccount"},
					{Key: "err", Val: err.Error()},
				},
				"failed to find account").Wrap(err)
		}

		return usecase.GetAccountInfoResponse{}, e
	}

	return usecase.GetAccountInfoResponse{
		Data: usecase.CreateLoginUserRequest{
			LoginID:  account.LoginID,
			Platform: account.Platform,
			Token:    account.Token,
		},
	}, nil
}

// ===========================

func (use *MemberUseCase) GetUserInfo(ctx context.Context, req usecase.GetUserInfoRequest) (usecase.UserInfo, error) {
	var user *entity.User
	var err error

	switch {
	case req.UID != "":
		user, err = use.User.FindOneByUID(ctx, req.UID)
	case req.NickName != "":
		user, err = use.User.FindOneByNickName(ctx, req.NickName)
	default:
		// 驗證至少提供一個查詢參數
		return usecase.UserInfo{}, errs.InputInvalidRangeError("UID or NickName must be provided")
	}
	// 查詢失敗時處理錯誤
	if err != nil {
		return usecase.UserInfo{}, use.handleUserQueryError(ctx, err, req)
	}

	// 返回查詢結果
	return mapUserEntityToUserInfo(user), nil
}

// 將查詢錯誤處理邏輯封裝為單獨的函數
func (use *MemberUseCase) handleUserQueryError(ctx context.Context, err error, req usecase.GetUserInfoRequest) error {
	if errors.Is(err, mon.ErrNotFound) {
		return errs.ResNotFoundError(fmt.Sprintf("user not found: %s", req.UID))
	}

	return errs.DBErrorErrorL(
		use.Logger,
		[]errs.LogField{
			{Key: "req", Val: req},
			{Key: "func", Val: "MemberUseCase.GetUserInfo"},
			{Key: "err", Val: err.Error()},
		},
		"failed to query user info").Wrap(err)
}

// 將用戶實體轉換為業務層數據結構
func mapUserEntityToUserInfo(user *entity.User) usecase.UserInfo {
	return usecase.UserInfo{
		CreateUserInfoRequest: usecase.CreateUserInfoRequest{
			UID:               user.UID,
			AlarmCategory:     user.AlarmCategory,
			UserStatus:        user.UserStatus,
			PreferredLanguage: user.PreferredLanguage,
			Currency:          user.Currency,
			Nickname:          user.Nickname,
			AvatarURL:         user.AvatarURL,
			FullName:          user.FullName,
			GenderCode:        user.GenderCode,
			Birthdate:         user.Birthdate,
			PhoneNumber:       user.PhoneNumber,
			Address:           user.Address,
			Email:             user.Email,
		},
		CreateTime: GetOriginalInt64(user.CreateAt),
		UpdateTime: GetOriginalInt64(user.UpdateAt),
	}
}

// ===========================

func (use *MemberUseCase) UpdateUserToken(ctx context.Context, req usecase.UpdateTokenRequest) error {
	// 密碼加密
	token, e := HasPasswordFunc(req.Token, use.Config.Bcrypt.Cost)
	if e != nil {
		return errs.SysInternalError(fmt.Sprintf("failed to encrypt err: %s", e.Error()))
	}
	toInt8, err := safeInt64ToInt8(req.Platform)
	if err != nil {
		return err
	}
	err = use.Account.UpdateTokenByLoginID(ctx, req.Account, token, member.Platform(toInt8))
	if err != nil {
		var e *errs.Error
		switch {
		case errors.Is(err, mon.ErrNotFound):
			e = errs.ResNotFoundError(fmt.Sprintf("failed to upadte password since account not found: %s", req.Account))
		default:
			e = errs.DBErrorErrorL(
				use.Logger,
				[]errs.LogField{
					{Key: "req", Val: req},
					{Key: "func", Val: "Account.UpdateTokenByLoginID"},
					{Key: "err", Val: err.Error()},
				},
				"failed to update password").Wrap(err)
		}

		return e
	}

	return nil
}

func (use *MemberUseCase) UpdateUserInfo(ctx context.Context, req *usecase.UpdateUserInfoRequest) error {
	err := use.User.UpdateUserDetailsByUID(ctx, &repository.UpdateUserInfoRequest{
		UID:               req.UID,
		AvatarURL:         req.AvatarURL,
		FullName:          req.FullName,
		Nickname:          req.Nickname,
		GenderCode:        req.GenderCode,
		Birthdate:         req.Birthdate,
		Address:           req.Address,
		AlarmCategory:     req.AlarmCategory,
		UserStatus:        req.UserStatus,
		PreferredLanguage: req.PreferredLanguage,
		Currency:          req.Currency,
	})
	if err != nil {
		var e *errs.Error
		switch {
		case errors.Is(err, mon.ErrNotFound):
			e = errs.ResNotFoundError(fmt.Sprintf("failed to upadte password since account not found: %s", req.UID))
		default:
			e = errs.DBErrorErrorL(
				use.Logger,
				[]errs.LogField{
					{Key: "req", Val: req},
					{Key: "func", Val: "User.UpdateUserDetailsByUid"},
					{Key: "err", Val: err.Error()},
				},
				"failed to update user info").Wrap(err)
		}

		return e
	}

	return nil
}

func (use *MemberUseCase) UpdateStatus(ctx context.Context, req usecase.UpdateStatusRequest) error {
	err := use.User.UpdateStatus(ctx, req.UID, req.Status.ToInt32())
	if err != nil {
		var e *errs.Error
		switch {
		case errors.Is(err, mon.ErrNotFound):
			e = errs.ResNotFoundError(fmt.Sprintf("failed to upadte password since account not found: %s", req.UID))

		default:
			e = errs.DBErrorErrorL(
				use.Logger,
				[]errs.LogField{
					{Key: "req", Val: req},
					{Key: "func", Val: "User.UpdateStatus"},
					{Key: "err", Val: err.Error()},
				},
				"failed to update user info").Wrap(err)
		}

		return e
	}

	return nil
}

func (use *MemberUseCase) ListMember(ctx context.Context, req usecase.ListUserInfoRequest) (usecase.ListUserInfoResponse, error) {
	listMembers, total, err := use.User.ListMembers(ctx, &repository.UserQueryParams{
		AlarmCategory:   req.AlarmCategory,
		UserStatus:      req.UserStatus,
		CreateStartTime: req.CreateStartTime,
		CreateEndTime:   req.CreateEndTime,
		PageSize:        req.PageSize,
		PageIndex:       req.PageIndex,
	})
	if err != nil {
		e := errs.DBErrorErrorL(
			use.Logger,
			[]errs.LogField{
				{Key: "req", Val: req},
				{Key: "func", Val: "User.ListMembers"},
				{Key: "err", Val: err.Error()},
			},
			"failed to list members").Wrap(err)

		return usecase.ListUserInfoResponse{}, e
	}

	var data = make([]usecase.UserInfo, 0, len(listMembers))

	for _, item := range listMembers {
		data = append(data, usecase.UserInfo{
			CreateUserInfoRequest: usecase.CreateUserInfoRequest{
				UID:               item.UID,
				AlarmCategory:     item.AlarmCategory,
				UserStatus:        item.UserStatus,
				PreferredLanguage: item.PreferredLanguage,
				Currency:          item.Currency,
				Nickname:          item.Nickname,
				AvatarURL:         item.AvatarURL, // 按照先前的命名 AvatarURL
				FullName:          item.FullName,
				GenderCode:        item.GenderCode,
				Birthdate:         item.Birthdate,
				PhoneNumber:       item.PhoneNumber,
				Address:           item.Address,
				Email:             item.Email,
			},
			CreateTime: GetOriginalInt64(item.CreateAt), // 使用自定義指標轉換函數
			UpdateTime: GetOriginalInt64(item.UpdateAt),
		})
	}

	return usecase.ListUserInfoResponse{
		Data: data,
		Page: usecase.Pager{
			Total: total,
			Index: req.PageIndex,
			Size:  req.PageSize,
		},
	}, nil
}

// GetOriginalInt64 取得原始值的函數
func GetOriginalInt64(value *int64) int64 {
	if value == nil {
		return 0 // 處理 nil 的情況
	}

	return *value
}

func safeInt64ToInt8(n int64) (int8, error) {
	if n < math.MinInt8 || n > math.MaxInt8 {
		return 0, fmt.Errorf("int64 value %d out of int8 range", n)
	}

	return int8(n), nil
}
