package user

import (
	"context"
	"fmt"
	"sort"
	"strings"

	authlogic "github.com/perfect-panel/server/internal/logic/auth"
	logicCommon "github.com/perfect-panel/server/internal/logic/common"
	modelOrder "github.com/perfect-panel/server/internal/model/order"
	"github.com/perfect-panel/server/pkg/constant"
	"github.com/perfect-panel/server/pkg/uuidx"
	"github.com/perfect-panel/server/pkg/xerr"
	"github.com/pkg/errors"

	"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/logger"
	"github.com/perfect-panel/server/pkg/phone"
	"github.com/perfect-panel/server/pkg/tool"
	"gorm.io/gorm"
)

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

// Query User Info
func NewQueryUserInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryUserInfoLogic {
	return &QueryUserInfoLogic{
		Logger: logger.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *QueryUserInfoLogic) QueryUserInfo() (resp *types.User, err error) {
	resp = &types.User{}
	u, ok := l.ctx.Value(constant.CtxKeyUser).(*user.User)
	if !ok {
		logger.Error("current user is not found in context")
		return nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidAccess), "Invalid Access")
	}
	tool.DeepCopy(resp, u)
	resp.UseStatus = true

	// 用家庭范围查设备，而不是只看当前用户自己的 UserDevices
	scopeHelper := newFamilyScopeHelper(l.ctx, l.svcCtx)
	scopeUserIds, scopeErr := scopeHelper.resolveScopedUserIds(u.Id)
	if scopeErr != nil {
		l.Errorw("resolve scoped user ids failed", logger.Field("user_id", u.Id), logger.Field("error", scopeErr.Error()))
		scopeUserIds = []int64{u.Id}
	}
	deviceList, _, deviceErr := l.svcCtx.UserModel.QueryDeviceListByUserIds(l.ctx, scopeUserIds)
	if deviceErr != nil {
		l.Errorw("query device list failed", logger.Field("user_id", u.Id), logger.Field("error", deviceErr.Error()))
	} else {
		userDevices := make([]types.UserDevice, 0, len(deviceList))
		tool.DeepCopy(&userDevices, deviceList)
		for i, d := range deviceList {
			if i < len(userDevices) {
				userDevices[i].DeviceNo = tool.DeviceIdToHash(d.Id)
			}
		}
		resp.UserDevices = userDevices
	}

	useStatus, useStatusErr := l.resolveBindEmailTrialUseStatus(u.Id, scopeUserIds)
	if useStatusErr != nil {
		l.Errorw("resolve bind email trial use status failed", logger.Field("user_id", u.Id), logger.Field("error", useStatusErr.Error()))
	} else {
		resp.UseStatus = useStatus
	}

	// refer_code 为空时自动生成
	if resp.ReferCode == "" {
		resp.ReferCode = uuidx.UserInviteCode(u.Id)
		if err := l.svcCtx.DB.Model(&user.User{}).Where("id = ?", u.Id).Update("refer_code", resp.ReferCode).Error; err != nil {
			l.Errorw("auto generate refer_code failed", logger.Field("user_id", u.Id), logger.Field("error", err.Error()))
		} else {
			_ = l.svcCtx.UserModel.ClearUserCache(l.ctx, u)
		}
	}

	ownerEmailMethod := l.fillFamilyContext(resp, u.Id)

	var userMethods []types.UserAuthMethod
	for _, method := range resp.AuthMethods {
		var item types.UserAuthMethod
		tool.DeepCopy(&item, method)

		switch method.AuthType {
		case "mobile":
			item.AuthIdentifier = phone.MaskPhoneNumber(method.AuthIdentifier)
		case "email":
		default:
			item.AuthIdentifier = maskOpenID(method.AuthIdentifier)
		}
		userMethods = append(userMethods, item)
	}
	userMethods = appendFamilyOwnerEmailIfNeeded(userMethods, resp.FamilyJoined, ownerEmailMethod)

	sortUserAuthMethodsByPriority(userMethods)

	resp.AuthMethods = userMethods

	// 生成邀请短链接
	if resp.ReferCode != "" {
		shortLink := logicCommon.NewInviteLinkResolver(l.ctx, l.svcCtx).ResolveInviteLink(resp.ReferCode)
		if shortLink != "" {
			resp.ShareLink = shortLink
		}
	}

	return resp, nil
}

// resolveBindEmailTrialUseStatus determines whether userinfo should show the
// "bind email to get free trial" prompt. `true` means show the prompt.
func (l *QueryUserInfoLogic) resolveBindEmailTrialUseStatus(currentUserId int64, scopeUserIds []int64) (bool, error) {
	if len(scopeUserIds) == 0 {
		scopeUserIds = []int64{currentUserId}
	}

	var hasBoundEmailCount int64
	if err := l.svcCtx.DB.WithContext(l.ctx).
		Model(&user.AuthMethods{}).
		Where("user_id IN ? AND auth_type = ? AND auth_identifier != ''", scopeUserIds, "email").
		Count(&hasBoundEmailCount).Error; err != nil {
		return false, err
	}

	var hasPurchaseCount int64
	if err := l.svcCtx.DB.WithContext(l.ctx).
		Model(&modelOrder.Order{}).
		Where("user_id IN ? AND type IN ? AND status IN ?", scopeUserIds, []int64{1, 2}, []int64{2, 5}).
		Count(&hasPurchaseCount).Error; err != nil {
		return false, err
	}

	hasTrial := false
	registerCfg := l.svcCtx.Config.Register
	if authlogic.IsTrialConfigReady(registerCfg) && registerCfg.TrialSubscribe > 0 {
		var hasTrialCount int64
		if err := l.svcCtx.DB.WithContext(l.ctx).
			Model(&user.Subscribe{}).
			Where("user_id IN ? AND subscribe_id = ?", scopeUserIds, registerCfg.TrialSubscribe).
			Count(&hasTrialCount).Error; err != nil {
			return false, err
		}
		hasTrial = hasTrialCount > 0
	}

	return shouldShowBindEmailTrialPrompt(hasBoundEmailCount > 0, hasPurchaseCount > 0, hasTrial), nil
}

func shouldShowBindEmailTrialPrompt(hasBoundEmail, hasPurchased, hasTrial bool) bool {
	return !hasBoundEmail && !hasPurchased && !hasTrial
}

func (l *QueryUserInfoLogic) fillFamilyContext(resp *types.User, userId int64) *user.AuthMethods {
	type familyRelation struct {
		FamilyId     int64
		Role         uint8
		FamilyStatus uint8
		OwnerUserId  int64
		MaxMembers   int64
	}

	var relation familyRelation
	relationErr := l.svcCtx.DB.WithContext(l.ctx).
		Table("user_family_member").
		Select("user_family_member.family_id, user_family_member.role, user_family.status as family_status, user_family.owner_user_id, user_family.max_members").
		Joins("JOIN user_family ON user_family.id = user_family_member.family_id AND user_family.deleted_at IS NULL").
		Where("user_family_member.user_id = ? AND user_family_member.deleted_at IS NULL AND user_family_member.status = ?", userId, user.FamilyMemberActive).
		First(&relation).Error
	if relationErr != nil {
		if !errors.Is(relationErr, gorm.ErrRecordNotFound) {
			l.Errorw("query family relation failed", logger.Field("user_id", userId), logger.Field("error", relationErr.Error()))
		}
		return nil
	}

	resp.FamilyJoined = true
	resp.FamilyId = relation.FamilyId
	resp.FamilyRole = relation.Role
	resp.FamilyRoleName = getFamilyRoleName(relation.Role)
	resp.FamilyOwnerUserId = relation.OwnerUserId
	resp.FamilyStatus = getFamilyStatusName(relation.FamilyStatus)
	resp.FamilyMaxMembers = relation.MaxMembers

	var activeMemberCount int64
	countErr := l.svcCtx.DB.WithContext(l.ctx).
		Table("user_family_member").
		Where("family_id = ? AND status = ? AND deleted_at IS NULL", relation.FamilyId, user.FamilyMemberActive).
		Count(&activeMemberCount).Error
	if countErr != nil {
		l.Errorw("count family members failed", logger.Field("family_id", relation.FamilyId), logger.Field("error", countErr.Error()))
	} else {
		resp.FamilyMemberCount = activeMemberCount
	}

	ownerEmailMethod, ownerEmailErr := l.svcCtx.UserModel.FindUserAuthMethodByUserId(l.ctx, "email", relation.OwnerUserId)
	if ownerEmailErr != nil {
		if !errors.Is(ownerEmailErr, gorm.ErrRecordNotFound) {
			l.Errorw("query family owner email failed", logger.Field("owner_user_id", relation.OwnerUserId), logger.Field("error", ownerEmailErr.Error()))
		}
		return nil
	}
	return ownerEmailMethod
}

func appendFamilyOwnerEmailIfNeeded(methods []types.UserAuthMethod, familyJoined bool, ownerEmailMethod *user.AuthMethods) []types.UserAuthMethod {
	if !familyJoined || ownerEmailMethod == nil {
		return methods
	}
	ownerEmail := strings.TrimSpace(ownerEmailMethod.AuthIdentifier)
	if ownerEmail == "" {
		return methods
	}
	if hasEmailAuthMethod(methods) {
		return methods
	}
	return append(methods, types.UserAuthMethod{
		AuthType:       "email",
		AuthIdentifier: ownerEmail,
		Verified:       ownerEmailMethod.Verified,
	})
}

func hasEmailAuthMethod(methods []types.UserAuthMethod) bool {
	for _, method := range methods {
		if strings.EqualFold(strings.TrimSpace(method.AuthType), "email") && strings.TrimSpace(method.AuthIdentifier) != "" {
			return true
		}
	}
	return false
}

func sortUserAuthMethodsByPriority(methods []types.UserAuthMethod) {
	sort.SliceStable(methods, func(i, j int) bool {
		return getAuthTypePriority(methods[i].AuthType) < getAuthTypePriority(methods[j].AuthType)
	})
}

func getFamilyRoleName(role uint8) string {
	switch role {
	case user.FamilyRoleOwner:
		return "owner"
	case user.FamilyRoleMember:
		return "member"
	default:
		return fmt.Sprintf("role_%d", role)
	}
}

func getFamilyStatusName(status uint8) string {
	if status == user.FamilyStatusActive {
		return "active"
	}
	return "disabled"
}

// getAuthTypePriority 获取认证类型的排序优先级
// email: 1 (第一位)
// mobile: 2 (第二位)
// 其他类型: 100+ (后续位置)
func getAuthTypePriority(authType string) int {
	switch authType {
	case "email":
		return 1
	case "mobile":
		return 2
	default:
		return 100
	}
}

// maskOpenID 脱敏 OpenID，只保留前 3 和后 3 位
func maskOpenID(openID string) string {
	length := len(openID)
	if length <= 6 {
		return "***" // 如果 ID 太短，直接返回 "***"
	}

	// 计算中间需要被替换的 `*` 数量
	maskLength := length - 6
	mask := make([]byte, maskLength)
	for i := range mask {
		mask[i] = '*'
	}

	// 组合脱敏后的 OpenID
	return openID[:3] + string(mask) + openID[length-3:]
}
