import { DomainError } from '@/shared/exceptions/domain.exception';
import {
  UserId,
  AccountSequence,
  PhoneNumber,
  ReferralCode,
  DeviceInfo,
  ChainType,
  KYCInfo,
  KYCStatus,
  AccountStatus,
} from '@/domain/value-objects';
import { WalletAddress } from '@/domain/entities/wallet-address.entity';
import {
  DomainEvent,
  UserAccountAutoCreatedEvent,
  UserAccountCreatedEvent,
  DeviceAddedEvent,
  DeviceRemovedEvent,
  PhoneNumberBoundEvent,
  WalletAddressBoundEvent,
  MultipleWalletAddressesBoundEvent,
  KYCSubmittedEvent,
  KYCVerifiedEvent,
  KYCRejectedEvent,
  UserAccountFrozenEvent,
  UserAccountDeactivatedEvent,
} from '@/domain/events';

export class UserAccount {
  private readonly _userId: UserId;
  private readonly _accountSequence: AccountSequence;
  private _devices: Map<string, DeviceInfo>;
  private _phoneNumber: PhoneNumber | null;
  private _nickname: string;
  private _avatarUrl: string | null;
  private readonly _inviterSequence: AccountSequence | null;
  private readonly _referralCode: ReferralCode;
  private _walletAddresses: Map<ChainType, WalletAddress>;
  private _kycInfo: KYCInfo | null;
  private _kycStatus: KYCStatus;
  private _status: AccountStatus;
  private readonly _registeredAt: Date;
  private _lastLoginAt: Date | null;
  private _updatedAt: Date;
  private _domainEvents: DomainEvent[] = [];

  // Getters
  get userId(): UserId {
    return this._userId;
  }
  get accountSequence(): AccountSequence {
    return this._accountSequence;
  }
  get phoneNumber(): PhoneNumber | null {
    return this._phoneNumber;
  }
  get nickname(): string {
    return this._nickname;
  }
  get avatarUrl(): string | null {
    return this._avatarUrl;
  }
  get inviterSequence(): AccountSequence | null {
    return this._inviterSequence;
  }
  get referralCode(): ReferralCode {
    return this._referralCode;
  }
  get kycInfo(): KYCInfo | null {
    return this._kycInfo;
  }
  get kycStatus(): KYCStatus {
    return this._kycStatus;
  }
  get status(): AccountStatus {
    return this._status;
  }
  get registeredAt(): Date {
    return this._registeredAt;
  }
  get lastLoginAt(): Date | null {
    return this._lastLoginAt;
  }
  get updatedAt(): Date {
    return this._updatedAt;
  }
  get isActive(): boolean {
    return this._status === AccountStatus.ACTIVE;
  }
  get isKYCVerified(): boolean {
    return this._kycStatus === KYCStatus.VERIFIED;
  }
  get domainEvents(): DomainEvent[] {
    return [...this._domainEvents];
  }

  private constructor(
    userId: UserId,
    accountSequence: AccountSequence,
    devices: Map<string, DeviceInfo>,
    phoneNumber: PhoneNumber | null,
    nickname: string,
    avatarUrl: string | null,
    inviterSequence: AccountSequence | null,
    referralCode: ReferralCode,
    walletAddresses: Map<ChainType, WalletAddress>,
    kycInfo: KYCInfo | null,
    kycStatus: KYCStatus,
    status: AccountStatus,
    registeredAt: Date,
    lastLoginAt: Date | null,
    updatedAt: Date,
  ) {
    this._userId = userId;
    this._accountSequence = accountSequence;
    this._devices = devices;
    this._phoneNumber = phoneNumber;
    this._nickname = nickname;
    this._avatarUrl = avatarUrl;
    this._inviterSequence = inviterSequence;
    this._referralCode = referralCode;
    this._walletAddresses = walletAddresses;
    this._kycInfo = kycInfo;
    this._kycStatus = kycStatus;
    this._status = status;
    this._registeredAt = registeredAt;
    this._lastLoginAt = lastLoginAt;
    this._updatedAt = updatedAt;
  }

  static createAutomatic(params: {
    accountSequence: AccountSequence;
    initialDeviceId: string;
    deviceName?: string;
    deviceInfo?: Record<string, unknown>; // 完整的设备信息 JSON
    inviterSequence: AccountSequence | null;
    nickname?: string;
    avatarSvg?: string;
  }): UserAccount {
    const devices = new Map<string, DeviceInfo>();
    devices.set(
      params.initialDeviceId,
      new DeviceInfo(
        params.initialDeviceId,
        params.deviceName || '未命名设备',
        new Date(),
        new Date(),
        params.deviceInfo, // 传递完整的 JSON
      ),
    );

    // UserID将由数据库自动生成(autoincrement)，这里使用临时值0
    const nickname =
      params.nickname || `用户${params.accountSequence.dailySequence}`;
    const avatarUrl = params.avatarSvg || null;

    const account = new UserAccount(
      UserId.create(0),
      params.accountSequence,
      devices,
      null,
      nickname,
      avatarUrl,
      params.inviterSequence,
      ReferralCode.generate(),
      new Map(),
      null,
      KYCStatus.NOT_VERIFIED,
      AccountStatus.ACTIVE,
      new Date(),
      null,
      new Date(),
    );

    account.addDomainEvent(
      new UserAccountAutoCreatedEvent({
        userId: account.userId.toString(),
        accountSequence: params.accountSequence.value,
        referralCode: account._referralCode.value, // 用户的推荐码
        initialDeviceId: params.initialDeviceId,
        inviterSequence: params.inviterSequence?.value || null,
        registeredAt: account._registeredAt,
      }),
    );

    return account;
  }

  static create(params: {
    accountSequence: AccountSequence;
    phoneNumber: PhoneNumber;
    initialDeviceId: string;
    deviceName?: string;
    deviceInfo?: Record<string, unknown>; // 完整的设备信息 JSON
    inviterSequence: AccountSequence | null;
  }): UserAccount {
    const devices = new Map<string, DeviceInfo>();
    devices.set(
      params.initialDeviceId,
      new DeviceInfo(
        params.initialDeviceId,
        params.deviceName || '未命名设备',
        new Date(),
        new Date(),
        params.deviceInfo,
      ),
    );

    // UserID将由数据库自动生成(autoincrement)，这里使用临时值0
    const account = new UserAccount(
      UserId.create(0),
      params.accountSequence,
      devices,
      params.phoneNumber,
      `用户${params.accountSequence.dailySequence}`,
      null,
      params.inviterSequence,
      ReferralCode.generate(),
      new Map(),
      null,
      KYCStatus.NOT_VERIFIED,
      AccountStatus.ACTIVE,
      new Date(),
      null,
      new Date(),
    );

    account.addDomainEvent(
      new UserAccountCreatedEvent({
        userId: account.userId.toString(),
        accountSequence: params.accountSequence.value,
        referralCode: account._referralCode.value, // 用户的推荐码
        phoneNumber: params.phoneNumber.value,
        initialDeviceId: params.initialDeviceId,
        inviterSequence: params.inviterSequence?.value || null,
        registeredAt: account._registeredAt,
      }),
    );

    return account;
  }

  static reconstruct(params: {
    userId: string;
    accountSequence: string;
    devices: DeviceInfo[];
    phoneNumber: string | null;
    nickname: string;
    avatarUrl: string | null;
    inviterSequence: string | null;
    referralCode: string;
    walletAddresses: WalletAddress[];
    kycInfo: KYCInfo | null;
    kycStatus: KYCStatus;
    status: AccountStatus;
    registeredAt: Date;
    lastLoginAt: Date | null;
    updatedAt: Date;
  }): UserAccount {
    const deviceMap = new Map<string, DeviceInfo>();
    params.devices.forEach((d) => deviceMap.set(d.deviceId, d));

    const walletMap = new Map<ChainType, WalletAddress>();
    params.walletAddresses.forEach((w) => walletMap.set(w.chainType, w));

    return new UserAccount(
      UserId.create(params.userId),
      AccountSequence.create(params.accountSequence),
      deviceMap,
      params.phoneNumber ? PhoneNumber.create(params.phoneNumber) : null,
      params.nickname,
      params.avatarUrl,
      params.inviterSequence
        ? AccountSequence.create(params.inviterSequence)
        : null,
      ReferralCode.create(params.referralCode),
      walletMap,
      params.kycInfo,
      params.kycStatus,
      params.status,
      params.registeredAt,
      params.lastLoginAt,
      params.updatedAt,
    );
  }

  addDevice(
    deviceId: string,
    deviceName?: string,
    deviceInfo?: Record<string, unknown>,
  ): void {
    this.ensureActive();
    if (this._devices.size >= 5 && !this._devices.has(deviceId)) {
      throw new DomainError('最多允许5个设备同时登录');
    }
    if (this._devices.has(deviceId)) {
      const device = this._devices.get(deviceId)!;
      device.updateActivity();
      if (deviceInfo) {
        device.updateDeviceInfo(deviceInfo);
      }
    } else {
      this._devices.set(
        deviceId,
        new DeviceInfo(
          deviceId,
          deviceName || '未命名设备',
          new Date(),
          new Date(),
          deviceInfo,
        ),
      );
      this.addDomainEvent(
        new DeviceAddedEvent({
          userId: this.userId.toString(),
          accountSequence: this.accountSequence.value,
          deviceId,
          deviceName: deviceName || '未命名设备',
        }),
      );
    }
    this._updatedAt = new Date();
  }

  removeDevice(deviceId: string): void {
    this.ensureActive();
    if (!this._devices.has(deviceId)) throw new DomainError('设备不存在');
    if (this._devices.size <= 1) throw new DomainError('至少保留一个设备');
    this._devices.delete(deviceId);
    this._updatedAt = new Date();
    this.addDomainEvent(
      new DeviceRemovedEvent({ userId: this.userId.toString(), deviceId }),
    );
  }

  isDeviceAuthorized(deviceId: string): boolean {
    return this._devices.has(deviceId);
  }

  getAllDevices(): DeviceInfo[] {
    return Array.from(this._devices.values());
  }

  updateProfile(params: { nickname?: string; avatarUrl?: string }): void {
    this.ensureActive();
    if (params.nickname) this._nickname = params.nickname;
    if (params.avatarUrl !== undefined) this._avatarUrl = params.avatarUrl;
    this._updatedAt = new Date();
  }

  bindPhoneNumber(phoneNumber: PhoneNumber): void {
    this.ensureActive();
    if (this._phoneNumber) throw new DomainError('已绑定手机号,不可重复绑定');
    this._phoneNumber = phoneNumber;
    this._updatedAt = new Date();
    this.addDomainEvent(
      new PhoneNumberBoundEvent({
        userId: this.userId.toString(),
        phoneNumber: phoneNumber.value,
      }),
    );
  }

  bindWalletAddress(chainType: ChainType, address: string): void {
    this.ensureActive();
    if (this._walletAddresses.has(chainType))
      throw new DomainError(`已绑定${chainType}地址`);
    const walletAddress = WalletAddress.create({
      userId: this.userId,
      chainType,
      address,
    });
    this._walletAddresses.set(chainType, walletAddress);
    this._updatedAt = new Date();
    this.addDomainEvent(
      new WalletAddressBoundEvent({
        userId: this.userId.toString(),
        chainType,
        address,
      }),
    );
  }

  bindMultipleWalletAddresses(wallets: Map<ChainType, WalletAddress>): void {
    this.ensureActive();
    for (const [chainType, wallet] of wallets) {
      if (this._walletAddresses.has(chainType))
        throw new DomainError(`已绑定${chainType}地址`);
      this._walletAddresses.set(chainType, wallet);
    }
    this._updatedAt = new Date();
    this.addDomainEvent(
      new MultipleWalletAddressesBoundEvent({
        userId: this.userId.toString(),
        addresses: Array.from(wallets.entries()).map(([chainType, wallet]) => ({
          chainType,
          address: wallet.address,
        })),
      }),
    );
  }

  submitKYC(kycInfo: KYCInfo): void {
    this.ensureActive();
    if (this._kycStatus === KYCStatus.VERIFIED)
      throw new DomainError('已通过KYC认证,不可重复提交');
    this._kycInfo = kycInfo;
    this._kycStatus = KYCStatus.PENDING;
    this._updatedAt = new Date();
    this.addDomainEvent(
      new KYCSubmittedEvent({
        userId: this.userId.toString(),
        realName: kycInfo.realName,
        idCardNumber: kycInfo.idCardNumber,
      }),
    );
  }

  approveKYC(): void {
    if (this._kycStatus !== KYCStatus.PENDING)
      throw new DomainError('只有待审核状态才能通过KYC');
    this._kycStatus = KYCStatus.VERIFIED;
    this._updatedAt = new Date();
    this.addDomainEvent(
      new KYCVerifiedEvent({
        userId: this.userId.toString(),
        accountSequence: this.accountSequence.value,
        verifiedAt: new Date(),
      }),
    );
  }

  rejectKYC(reason: string): void {
    if (this._kycStatus !== KYCStatus.PENDING)
      throw new DomainError('只有待审核状态才能拒绝KYC');
    this._kycStatus = KYCStatus.REJECTED;
    this._updatedAt = new Date();
    this.addDomainEvent(
      new KYCRejectedEvent({ userId: this.userId.toString(), reason }),
    );
  }

  recordLogin(): void {
    this.ensureActive();
    this._lastLoginAt = new Date();
    this._updatedAt = new Date();
  }

  freeze(reason: string): void {
    if (this._status === AccountStatus.FROZEN)
      throw new DomainError('账户已冻结');
    this._status = AccountStatus.FROZEN;
    this._updatedAt = new Date();
    this.addDomainEvent(
      new UserAccountFrozenEvent({ userId: this.userId.toString(), reason }),
    );
  }

  unfreeze(): void {
    if (this._status !== AccountStatus.FROZEN)
      throw new DomainError('账户未冻结');
    this._status = AccountStatus.ACTIVE;
    this._updatedAt = new Date();
  }

  deactivate(): void {
    if (this._status === AccountStatus.DEACTIVATED)
      throw new DomainError('账户已注销');
    this._status = AccountStatus.DEACTIVATED;
    this._updatedAt = new Date();
    this.addDomainEvent(
      new UserAccountDeactivatedEvent({
        userId: this.userId.toString(),
        deactivatedAt: new Date(),
      }),
    );
  }

  getWalletAddress(chainType: ChainType): WalletAddress | null {
    return this._walletAddresses.get(chainType) || null;
  }

  getAllWalletAddresses(): WalletAddress[] {
    return Array.from(this._walletAddresses.values());
  }

  private ensureActive(): void {
    if (this._status !== AccountStatus.ACTIVE)
      throw new DomainError('账户已冻结或注销');
  }

  private addDomainEvent(event: DomainEvent): void {
    this._domainEvents.push(event);
  }

  clearDomainEvents(): void {
    this._domainEvents = [];
  }

  /**
   * 更新领域事件中的 userId
   *
   * [2026-01-29] 修复：事件在 aggregate 创建时添加，此时 userId 是临时值 0
   * 在 repository.save() 后调用此方法，将事件中的 userId 更新为数据库生成的真实值
   */
  updateEventsUserId(): void {
    const realUserId = this._userId.toString();
    // 只更新 userId 为 '0' 的事件（即新创建时的临时值）
    for (const event of this._domainEvents) {
      if ('payload' in event && (event as any).payload?.userId === '0') {
        (event as any).payload.userId = realUserId;
      }
    }
  }

  /**
   * 创建钱包生成事件（用于重试）
   *
   * 重新发布 UserAccountCreatedEvent 以触发 MPC 钱包生成流程
   * 这个方法是幂等的，可以安全地多次调用
   */
  createWalletGenerationEvent(): UserAccountCreatedEvent {
    // 获取第一个设备的信息
    const firstDevice = this._devices.values().next().value as
      | DeviceInfo
      | undefined;

    return new UserAccountCreatedEvent({
      userId: this._userId.toString(),
      accountSequence: this._accountSequence.value,
      referralCode: this._referralCode.value,
      phoneNumber: this._phoneNumber?.value || null,
      initialDeviceId: firstDevice?.deviceId || 'retry-unknown',
      inviterSequence: null, // 重试时不需要邀请人信息
      registeredAt: this._registeredAt,
    });
  }
}
