import { Injectable, Inject, NotFoundException, BadRequestException } from '@nestjs/common';
import { randomBytes } from 'crypto';
import { v4 as uuidv4 } from 'uuid';
import {
  CoManagedWalletSessionEntity,
  CoManagedWalletEntity,
} from '../../domain/entities/co-managed-wallet.entity';
import {
  CO_MANAGED_WALLET_SESSION_REPOSITORY,
  CO_MANAGED_WALLET_REPOSITORY,
  CoManagedWalletSessionRepository,
  CoManagedWalletRepository,
} from '../../domain/repositories/co-managed-wallet.repository';
import { WalletSessionStatus } from '../../domain/enums/wallet-session-status.enum';

@Injectable()
export class CoManagedWalletService {
  constructor(
    @Inject(CO_MANAGED_WALLET_SESSION_REPOSITORY)
    private readonly sessionRepository: CoManagedWalletSessionRepository,
    @Inject(CO_MANAGED_WALLET_REPOSITORY)
    private readonly walletRepository: CoManagedWalletRepository,
  ) {}

  /**
   * 生成邀请码
   */
  private generateInviteCode(): string {
    const bytes = randomBytes(6);
    const code = bytes.toString('hex').toUpperCase();
    // 格式化为 XXXX-XXXX-XXXX
    return `${code.slice(0, 4)}-${code.slice(4, 8)}-${code.slice(8, 12)}`;
  }

  /**
   * 创建共管钱包会话
   */
  async createSession(params: {
    walletName: string;
    thresholdT: number;
    thresholdN: number;
    initiatorName: string;
    createdBy: string;
  }): Promise<CoManagedWalletSessionEntity> {
    // 验证阈值
    if (params.thresholdT > params.thresholdN) {
      throw new BadRequestException('签名阈值不能大于参与方总数');
    }
    if (params.thresholdT < 1) {
      throw new BadRequestException('签名阈值至少为 1');
    }
    if (params.thresholdN < 2) {
      throw new BadRequestException('参与方总数至少为 2');
    }

    const session = CoManagedWalletSessionEntity.create({
      id: uuidv4(),
      walletName: params.walletName,
      threshold: { t: params.thresholdT, n: params.thresholdN },
      inviteCode: this.generateInviteCode(),
      createdBy: params.createdBy,
      initiatorName: params.initiatorName,
      initiatorPartyId: uuidv4(), // 为发起者生成 partyId
    });

    return this.sessionRepository.save(session);
  }

  /**
   * 获取会话详情
   */
  async getSession(sessionId: string): Promise<CoManagedWalletSessionEntity> {
    const session = await this.sessionRepository.findById(sessionId);
    if (!session) {
      throw new NotFoundException('会话不存在');
    }
    return session;
  }

  /**
   * 验证邀请码
   */
  async validateInviteCode(inviteCode: string): Promise<{
    valid: boolean;
    session?: CoManagedWalletSessionEntity;
    error?: string;
  }> {
    const session = await this.sessionRepository.findByInviteCode(inviteCode);

    if (!session) {
      return { valid: false, error: '无效的邀请码' };
    }

    if (session.status !== WalletSessionStatus.WAITING) {
      return { valid: false, error: '会话已不在等待状态' };
    }

    if (!session.canAddParticipant()) {
      return { valid: false, error: '会话参与方已满' };
    }

    return { valid: true, session };
  }

  /**
   * 加入会话
   */
  async joinSession(
    inviteCode: string,
    participantName: string,
    partyId: string,
  ): Promise<CoManagedWalletSessionEntity> {
    const { valid, session, error } = await this.validateInviteCode(inviteCode);

    if (!valid || !session) {
      throw new BadRequestException(error || '无法加入会话');
    }

    session.addParticipant(partyId, participantName);
    return this.sessionRepository.save(session);
  }

  /**
   * 开始密钥生成
   */
  async startKeygen(sessionId: string): Promise<CoManagedWalletSessionEntity> {
    const session = await this.getSession(sessionId);

    if (session.status !== WalletSessionStatus.READY) {
      throw new BadRequestException('会话未就绪，需要所有参与方加入后才能开始');
    }

    session.startKeygen();
    return this.sessionRepository.save(session);
  }

  /**
   * 更新会话轮次
   */
  async updateSessionRound(
    sessionId: string,
    round: number,
  ): Promise<CoManagedWalletSessionEntity> {
    const session = await this.getSession(sessionId);
    session.updateRound(round);
    return this.sessionRepository.save(session);
  }

  /**
   * 完成会话并创建钱包
   */
  async completeSession(
    sessionId: string,
    publicKey: string,
  ): Promise<{ session: CoManagedWalletSessionEntity; wallet: CoManagedWalletEntity }> {
    const session = await this.getSession(sessionId);
    session.complete(publicKey);
    const savedSession = await this.sessionRepository.save(session);

    // 创建钱包记录
    const wallet = CoManagedWalletEntity.create({
      id: uuidv4(),
      sessionId: session.id,
      name: session.walletName,
      publicKey,
      threshold: session.threshold,
      participants: session.participants.map((p) => ({
        partyId: p.partyId,
        name: p.name,
      })),
      createdBy: session.createdBy,
    });

    const savedWallet = await this.walletRepository.save(wallet);

    return { session: savedSession, wallet: savedWallet };
  }

  /**
   * 标记会话失败
   */
  async failSession(
    sessionId: string,
    error: string,
  ): Promise<CoManagedWalletSessionEntity> {
    const session = await this.getSession(sessionId);
    session.fail(error);
    return this.sessionRepository.save(session);
  }

  /**
   * 取消会话
   */
  async cancelSession(sessionId: string): Promise<void> {
    const session = await this.getSession(sessionId);
    session.cancel();
    await this.sessionRepository.save(session);
  }

  /**
   * 获取会话列表
   */
  async listSessions(params: {
    page: number;
    pageSize: number;
    status?: WalletSessionStatus;
  }): Promise<{
    items: CoManagedWalletSessionEntity[];
    total: number;
    page: number;
    pageSize: number;
    totalPages: number;
  }> {
    const { items, total } = await this.sessionRepository.findAll(params);
    const totalPages = Math.ceil(total / params.pageSize);

    return {
      items,
      total,
      page: params.page,
      pageSize: params.pageSize,
      totalPages,
    };
  }

  /**
   * 获取钱包详情
   */
  async getWallet(walletId: string): Promise<CoManagedWalletEntity> {
    const wallet = await this.walletRepository.findById(walletId);
    if (!wallet) {
      throw new NotFoundException('钱包不存在');
    }
    return wallet;
  }

  /**
   * 获取钱包列表
   */
  async listWallets(params: {
    page: number;
    pageSize: number;
  }): Promise<{
    items: CoManagedWalletEntity[];
    total: number;
    page: number;
    pageSize: number;
    totalPages: number;
  }> {
    const { items, total } = await this.walletRepository.findAll(params);
    const totalPages = Math.ceil(total / params.pageSize);

    return {
      items,
      total,
      page: params.page,
      pageSize: params.pageSize,
      totalPages,
    };
  }
}
