package auth

import (
	"context"
	"strings"

	"hyapp/pkg/appcode"
	"hyapp/pkg/xerr"
	authdomain "hyapp/services/user-service/internal/domain/auth"
	invitedomain "hyapp/services/user-service/internal/domain/invite"
	userdomain "hyapp/services/user-service/internal/domain/user"
)

// QuickCreateAccount 创建一个已完成资料、已设置密码、可立即登录的账号。
// 该能力只复用内部领域事务，不依赖 firebase/mock provider，方便游戏方联调时快速拿 uid/token。
func (s *Service) QuickCreateAccount(ctx context.Context, password string, registration authdomain.ThirdPartyRegistration, meta Meta) (authdomain.Token, error) {
	meta.AppCode = appcode.Normalize(meta.AppCode)
	registration.AppCode = meta.AppCode
	registration = normalizeThirdPartyRegistration(registration, meta)
	meta.Platform = registration.Platform
	meta.Language = registration.Language
	meta.Timezone = registration.Timezone
	password = strings.TrimSpace(password)
	if password == "" {
		return authdomain.Token{}, xerr.New(xerr.InvalidArgument, "password is required")
	}
	if registration.DeviceID == "" || registration.Platform == "" {
		// refresh session 必须绑定设备，平台也会进入注册来源和风控审计。
		return authdomain.Token{}, xerr.New(xerr.InvalidArgument, "device_id and platform are required")
	}
	if err := validateThirdPartyRegistrationRequiredForCreate(registration); err != nil {
		// 快捷账号直接跳过 onboarding，因此必须一次性写齐客户端准入所需的最小资料。
		return authdomain.Token{}, err
	}
	if err := validateThirdPartyRegistration(registration); err != nil {
		return authdomain.Token{}, err
	}
	if s.authRepository == nil {
		return authdomain.Token{}, xerr.New(xerr.Unavailable, "auth repository is not configured")
	}

	registration, err := s.resolveRegistrationCountry(ctx, registration)
	if err != nil {
		return authdomain.Token{}, err
	}
	passwordHash, err := hashPassword(password)
	if err != nil {
		return authdomain.Token{}, err
	}

	var lastErr error
	for attempt := 0; attempt < s.allocateMaxAttempts; attempt++ {
		user, displayIdentity := s.newUserWithIdentity(attempt)
		user = applyThirdPartyRegistration(user, registration)
		user.ProfileCompleted = true
		user.ProfileCompletedAtMs = user.CreatedAtMs
		user.OnboardingStatus = userdomain.OnboardingStatusCompleted

		displayUserID, err := s.authRepository.AllocateDisplayUserID(ctx, registration.AppCode)
		if err != nil {
			return authdomain.Token{}, err
		}
		user.DefaultDisplayUserID = displayUserID
		user.CurrentDisplayUserID = displayUserID
		displayIdentity.AppCode = registration.AppCode
		displayIdentity.DisplayUserID = displayUserID
		displayIdentity.DefaultDisplayUserID = displayUserID
		if !userdomain.ValidDisplayUserID(displayIdentity.DisplayUserID) {
			// 分配器或历史种子异常时不写库，直接尝试下一个候选。
			continue
		}

		session, refreshToken, err := s.newSession(registration.AppCode, user.UserID, registration.DeviceID)
		if err != nil {
			return authdomain.Token{}, err
		}
		account := authdomain.PasswordAccount{
			AppCode:       registration.AppCode,
			UserID:        user.UserID,
			DisplayUserID: displayUserID,
			PasswordHash:  passwordHash,
			HashAlg:       hashAlgBcrypt,
			CreatedAtMs:   user.CreatedAtMs,
			UpdatedAtMs:   user.UpdatedAtMs,
		}
		inviteBind := invitedomain.BindCommand{
			AppCode:         registration.AppCode,
			InvitedUserID:   user.UserID,
			InvitedRegionID: registration.RegionID,
			InviteCode:      registration.InviteCode,
			Source:          "quick_account_creation",
			RequestID:       meta.RequestID,
			BoundAtMs:       user.CreatedAtMs,
		}
		err = s.authRepository.CreatePasswordUser(ctx, user, displayIdentity, account, session, inviteBind)
		if err == nil {
			s.audit(ctx, meta, user.UserID, loginPassword, "", resultSuccess, "")
			token, tokenErr := s.issueToken(user, session.SessionID, refreshToken)
			if tokenErr != nil {
				return authdomain.Token{}, tokenErr
			}
			s.importIMAccountAfterCommit(ctx, user, meta)
			s.enqueueLoginIPRiskJob(ctx, meta, token, "account", registration.Platform, registration.Language, registration.Timezone)
			return token, nil
		}
		if xerr.IsCode(err, xerr.DisplayUserIDExists) {
			// 默认短号冲突可重试，避免少量并发建号把快捷入口打成失败。
			lastErr = err
			continue
		}

		s.audit(ctx, meta, 0, loginPassword, "", resultFailed, string(xerr.CodeOf(err)))
		return authdomain.Token{}, err
	}

	if lastErr != nil {
		s.audit(ctx, meta, 0, loginPassword, "", resultFailed, string(xerr.DisplayUserIDAllocateFailed))
	}
	return authdomain.Token{}, xerr.New(xerr.DisplayUserIDAllocateFailed, "display_user_id allocation failed")
}
