import { Injectable, Logger, Inject } from '@nestjs/common';
import {
  AddressDerivationAdapter,
  DerivedAddress,
} from '@/infrastructure/blockchain/address-derivation.adapter';
import { RecoveryMnemonicAdapter } from '@/infrastructure/blockchain/recovery-mnemonic.adapter';
import { AddressCacheService } from '@/infrastructure/redis/address-cache.service';
import { EventPublisherService } from '@/infrastructure/kafka/event-publisher.service';
import { PrismaService } from '@/infrastructure/persistence/prisma/prisma.service';
import {
  MONITORED_ADDRESS_REPOSITORY,
  IMonitoredAddressRepository,
} from '@/domain/repositories/monitored-address.repository.interface';
import { MonitoredAddress } from '@/domain/aggregates/monitored-address';
import { WalletAddressCreatedEvent } from '@/domain/events';
import { ChainType, EvmAddress } from '@/domain/value-objects';
import { ChainTypeEnum } from '@/domain/enums';

export interface DeriveAddressParams {
  userId: bigint;
  accountSequence: string;
  publicKey: string;
}

export interface DeriveAddressResult {
  userId: bigint;
  accountSequence: string;
  publicKey: string;
  addresses: DerivedAddress[];
}

/**
 * 地址派生服务
 * 处理从 MPC 公钥派生钱包地址的业务逻辑
 *
 * 派生策略：
 * - KAVA: EVM 格式 (0x...) - Kava EVM 兼容链
 * - DST: Cosmos bech32 格式 (dst1...)
 * - BSC: EVM 格式 (0x...)
 *
 * 监控策略：
 * - EVM 链 (BSC, KAVA) 的地址会被注册到监控列表用于充值检测
 * - Cosmos 链 (DST) 需要不同的监控机制
 */
@Injectable()
export class AddressDerivationService {
  private readonly logger = new Logger(AddressDerivationService.name);

  // EVM 链类型列表，用于判断是否需要注册监控
  private readonly evmChains = new Set([ChainTypeEnum.BSC, ChainTypeEnum.KAVA]);

  constructor(
    private readonly addressDerivation: AddressDerivationAdapter,
    private readonly recoveryMnemonic: RecoveryMnemonicAdapter,
    private readonly addressCache: AddressCacheService,
    private readonly eventPublisher: EventPublisherService,
    private readonly prisma: PrismaService,
    @Inject(MONITORED_ADDRESS_REPOSITORY)
    private readonly monitoredAddressRepo: IMonitoredAddressRepository,
  ) {}

  /**
   * 从公钥派生地址并注册监控
   */
  async deriveAndRegister(params: DeriveAddressParams): Promise<DeriveAddressResult> {
    const { userId, accountSequence, publicKey } = params;
    this.logger.log(`[DERIVE] Starting address derivation for user ${userId}, account ${accountSequence}`);
    this.logger.log(`[DERIVE] Public key: ${publicKey.substring(0, 30)}...`);

    // 1. 派生所有链的地址 (包括 Cosmos 和 EVM)
    const derivedAddresses = this.addressDerivation.deriveAllAddresses(publicKey);
    this.logger.log(`[DERIVE] Derived ${derivedAddresses.length} addresses`);

    // 2. 只为 EVM 链注册监控地址 (用于充值检测)
    for (const derived of derivedAddresses) {
      if (this.evmChains.has(derived.chainType)) {
        await this.registerEvmAddressForMonitoring(userId, accountSequence, derived);
      } else {
        this.logger.log(`[DERIVE] Skipping monitoring registration for Cosmos chain: ${derived.chainType} - ${derived.address}`);
      }
    }

    // 3. 生成恢复助记词 (与账户序列号关联)
    this.logger.log(`[MNEMONIC] Generating recovery mnemonic for account ${accountSequence}`);
    const mnemonicResult = await this.recoveryMnemonic.generateMnemonic({
      userId: userId.toString(),
      publicKey,
    });
    this.logger.log(`[MNEMONIC] Recovery mnemonic generated, hash: ${mnemonicResult.mnemonicHash.slice(0, 16)}...`);

    // 4. 存储恢复助记词到 blockchain-service 数据库 (使用 accountSequence 关联)
    // 检查是否已存在，避免重复创建
    const existingMnemonic = await this.prisma.recoveryMnemonic.findFirst({
      where: {
        accountSequence,
        status: 'ACTIVE',
      },
    });

    if (existingMnemonic) {
      this.logger.warn(`[MNEMONIC] Recovery mnemonic already exists for account ${accountSequence}, skipping creation`);
    } else {
      await this.prisma.recoveryMnemonic.create({
        data: {
          accountSequence,
          publicKey,
          encryptedMnemonic: mnemonicResult.encryptedMnemonic,
          mnemonicHash: mnemonicResult.mnemonicHash,
          status: 'ACTIVE',
          isBackedUp: false,
        },
      });
      this.logger.log(`[MNEMONIC] Recovery mnemonic saved for account ${accountSequence}`);
    }

    // 5. 发布钱包地址创建事件 (包含所有链的地址和助记词)
    const event = new WalletAddressCreatedEvent({
      userId: userId.toString(),
      accountSequence,
      publicKey,
      addresses: derivedAddresses.map((a) => ({
        chainType: a.chainType,
        address: a.address,
      })),
      // 恢复助记词 (明文仅在事件中传递给客户端首次显示)
      mnemonic: mnemonicResult.mnemonic,
      encryptedMnemonic: mnemonicResult.encryptedMnemonic,
      mnemonicHash: mnemonicResult.mnemonicHash,
    });

    this.logger.log(`[PUBLISH] Publishing WalletAddressCreated event for account ${accountSequence}`);
    this.logger.log(`[PUBLISH] Addresses: ${JSON.stringify(derivedAddresses)}`);
    await this.eventPublisher.publish(event);
    this.logger.log(`[PUBLISH] WalletAddressCreated event published successfully`);

    return {
      userId,
      accountSequence,
      publicKey,
      addresses: derivedAddresses,
    };
  }

  /**
   * 注册 EVM 地址用于充值监控
   */
  private async registerEvmAddressForMonitoring(
    userId: bigint,
    accountSequence: string,
    derived: DerivedAddress,
  ): Promise<void> {
    const chainType = ChainType.fromEnum(derived.chainType);
    const address = EvmAddress.create(derived.address);

    // 检查是否已存在
    const exists = await this.monitoredAddressRepo.existsByChainAndAddress(chainType, address);
    if (!exists) {
      // 创建监控地址 - 使用 accountSequence 作为跨服务关联键
      const monitored = MonitoredAddress.create({
        chainType,
        address,
        accountSequence,
        userId,
      });

      await this.monitoredAddressRepo.save(monitored);

      // 添加到缓存
      await this.addressCache.addAddress(chainType, address.lowercase);

      this.logger.log(`[MONITOR] Registered EVM address for monitoring: ${derived.chainType} - ${derived.address} (account ${accountSequence})`);
    } else {
      this.logger.debug(`[MONITOR] Address already registered: ${derived.chainType} - ${derived.address}`);
    }
  }

  /**
   * 获取用户的所有地址
   */
  async getUserAddresses(userId: bigint): Promise<MonitoredAddress[]> {
    return this.monitoredAddressRepo.findByUserId(userId);
  }
}
