// ============================================================
// WechatService — 微信登录 / 注册业务逻辑
//
// ── 完整流程 ──────────────────────────────────────────────
//   1. Flutter 客户端调用 fluwx.sendWeChatAuth(scope: 'snsapi_userinfo')
//      拉起微信 App → 用户确认授权 → 微信回传 code（WXAuthResp.code）
//
//   2. 客户端将 code 发送到 POST /api/v1/auth/wechat
//      （可同时传 referralCode 推荐码，仅新用户注册时有效）
//
//   3. 本服务调用 WechatProvider.exchangeCodeForToken(code)
//      → 获得 access_token、openid、unionid
//
//   4. 调用 WechatProvider.getUserInfo(access_token, openid)
//      → 获得 nickname、headimgurl（头像 URL）
//
//   5. 在 social_accounts 表查找已有绑定：
//      - 优先用 unionid 查找（跨 App 唯一，避免重复注册）
//      - 若无 unionid 则 fallback 到 openid
//
//   6a. 老用户（找到 social_account）：
//       - 同步最新 nickname / avatarUrl（头像 URL 可能变化）
//       - 若原记录无 unionid 但本次有，则补充 unionid
//       - 返回 JWT access_token + refresh_token
//
//   6b. 新用户（未找到 social_account）：
//       - 创建 users 记录（phone/email 为 null，随机密码哈希）
//       - 创建 social_accounts 绑定记录
//       - 发布 UserRegistered Kafka 事件（含 referralCode）
//       - 返回 JWT access_token + refresh_token
//
// ── unionid 优先策略原理 ───────────────────────────────────
//   微信 openid 是「每个应用 × 每个用户」独立的，同一人在 genex-mobile
//   和 admin-app 下 openid 不同，如果只用 openid 会产生两个账号。
//   unionid 是「同一开放平台账号下所有 App × 用户」统一的，可以跨 App
//   识别同一用户 → 因此优先用 unionid 查找，防止同一人注册两次。
//   注意: unionid 只在应用接入了微信开放平台且 scope=snsapi_userinfo 时才有。
//
// ── 自动生成账号字段 ───────────────────────────────────────
//   - nickname  : 取自微信昵称；若昵称为空则生成 "wx_{openid 前8位}"
//   - email     : null（微信不提供邮箱，可由用户事后在设置中绑定）
//   - phone     : null（微信仅在特定场景授权，当前不请求手机号权限）
//   - password  : 随机 32 字节 hex → bcrypt 哈希（用户无法用密码登录）
//   - walletMode: 默认 'standard'
//   - kycLevel  : 默认 0（需用户主动完成实名认证）
//
// ── 每次登录同步微信信息 ───────────────────────────────────
//   微信用户可能修改昵称/头像，每次登录时同步到 social_accounts，
//   上层业务可从 social_accounts 获取最新头像 URL 展示。
//   （users 表的 avatarUrl 不自动更新，避免覆盖用户手动设置的头像）
// ============================================================

import { Injectable, Logger, Inject } from '@nestjs/common';
import { WechatProvider } from '../../infrastructure/wechat/wechat.provider';
import { SOCIAL_ACCOUNT_REPOSITORY, ISocialAccountRepository } from '../../domain/repositories/social-account.repository.interface';
import { SocialProvider } from '../../domain/entities/social-account.entity';
import { USER_REPOSITORY, IUserRepository } from '../../domain/repositories/user.repository.interface';
import { TokenService } from './token.service';
import { EventPublisherService } from './event-publisher.service';
import { UserRole, UserStatus } from '../../domain/entities/user.entity';
import { AuthResult } from './auth.service';
import { Password } from '../../domain/value-objects/password.vo';
import * as crypto from 'crypto';

@Injectable()
export class WechatService {
  private readonly logger = new Logger('WechatService');

  constructor(
    private readonly wechatProvider: WechatProvider,
    @Inject(SOCIAL_ACCOUNT_REPOSITORY)
    private readonly socialAccountRepo: ISocialAccountRepository,
    @Inject(USER_REPOSITORY)
    private readonly userRepo: IUserRepository,
    private readonly tokenService: TokenService,
    private readonly eventPublisher: EventPublisherService,
  ) {}

  /**
   * 微信一键登录 / 自动注册
   *
   * @param code        客户端从微信 SDK 获取的 code
   * @param referralCode 推荐人的推荐码（可选，仅新用户注册时使用）
   * @param deviceInfo  设备信息（可选）
   * @param ipAddress   客户端 IP（可选）
   */
  async loginOrRegister(
    code: string,
    referralCode?: string,
    deviceInfo?: string,
    ipAddress?: string,
  ): Promise<AuthResult> {
    // Step 1: code → access_token + openid + unionid
    const tokenResp = await this.wechatProvider.exchangeCodeForToken(code);
    const { access_token, openid, unionid } = tokenResp;

    // Step 2: 获取微信用户信息（昵称、头像）
    const userInfo = await this.wechatProvider.getUserInfo(access_token, openid);

    const nickname = userInfo.nickname?.trim() || `wx_${openid.slice(0, 8)}`;
    const avatarUrl = userInfo.headimgurl || null;
    const rawData = userInfo as unknown as Record<string, unknown>;

    // Step 3: 查找已有 social_account
    let socialAccount = await this.findExistingSocialAccount(openid, unionid);

    if (socialAccount) {
      // ── 老用户：同步最新信息并登录 ──
      socialAccount.nickname = nickname;
      socialAccount.avatarUrl = avatarUrl;
      if (unionid && !socialAccount.unionid) {
        // 补充 unionid（首次绑定开放平台时 unionid 才出现）
        socialAccount.unionid = unionid;
      }
      await this.socialAccountRepo.save(socialAccount);

      const user = await this.userRepo.findById(socialAccount.userId);
      if (!user || user.status !== UserStatus.ACTIVE) {
        throw new Error('账号已禁用，请联系客服');
      }

      user.recordLoginSuccess(ipAddress);
      await this.userRepo.save(user);

      const tokens = await this.tokenService.generateTokenPair(user.id, user.role, user.kycLevel);
      await this.tokenService.storeRefreshToken(user.id, tokens.refreshToken, deviceInfo, ipAddress);

      await this.eventPublisher.publishUserLoggedIn({
        userId: user.id,
        ipAddress: ipAddress || null,
        deviceInfo: deviceInfo || null,
        timestamp: new Date().toISOString(),
      });

      this.logger.log(`WeChat login: userId=${user.id} openid=${openid.slice(0, 8)}...`);
      return { user: this.toUserDto(user), tokens };
    }

    // ── 新用户：自动注册 ──
    // 使用 Password VO 生成随机哈希（微信用户无需密码登录）
    const randomPw = await Password.create(crypto.randomBytes(32).toString('hex'));
    const randomPasswordHash = randomPw.value;

    const user = await this.userRepo.create({
      phone: null,
      email: null,
      passwordHash: randomPasswordHash,
      nickname,
      role: UserRole.USER,
      status: UserStatus.ACTIVE,
      kycLevel: 0,
      walletMode: 'standard',
    });

    await this.socialAccountRepo.create({
      userId: user.id,
      provider: SocialProvider.WECHAT,
      openid,
      unionid: unionid || undefined,
      nickname,
      avatarUrl: avatarUrl || undefined,
      rawData,
    });

    const tokens = await this.tokenService.generateTokenPair(user.id, user.role, user.kycLevel);
    await this.tokenService.storeRefreshToken(user.id, tokens.refreshToken, deviceInfo, ipAddress);

    await this.eventPublisher.publishUserRegistered({
      userId: user.id,
      phone: null,
      email: null,
      role: user.role,
      referralCode: referralCode?.toUpperCase() ?? null,
      timestamp: new Date().toISOString(),
    });

    this.logger.log(`WeChat new user registered: userId=${user.id} openid=${openid.slice(0, 8)}...`);
    return { user: this.toUserDto(user), tokens };
  }

  /**
   * 查找已有的 social_account
   * 优先用 unionid（跨 App 唯一），回退到 openid
   */
  private async findExistingSocialAccount(openid: string, unionid?: string) {
    if (unionid) {
      const byUnionid = await this.socialAccountRepo.findByProviderAndUnionid(
        SocialProvider.WECHAT,
        unionid,
      );
      if (byUnionid) return byUnionid;
    }
    return this.socialAccountRepo.findByProviderAndOpenid(SocialProvider.WECHAT, openid);
  }

  private toUserDto(user: any) {
    return {
      id: user.id,
      phone: user.phone ?? null,
      email: user.email ?? null,
      nickname: user.nickname ?? null,
      avatarUrl: user.avatarUrl ?? null,
      role: user.role,
      kycLevel: user.kycLevel,
      walletMode: user.walletMode,
    };
  }
}
