import { Injectable, Logger } from '@nestjs/common';
import { PrismaService } from '../../infrastructure/persistence/prisma/prisma.service';
import {
  SystemMiningAccountRepository,
  SystemAccountType,
} from '../../infrastructure/persistence/repositories/system-mining-account.repository';
import { ShareAmount } from '../../domain/value-objects/share-amount.vo';
import Decimal from 'decimal.js';

interface SystemAccountSyncedData {
  accountType: string;       // OPERATION / PROVINCE / CITY / HEADQUARTERS
  regionCode: string | null; // 省/市代码，如 440000, 440100
  name: string;
  contributionBalance: string;
}

interface NetworkProgressUpdatedData {
  totalTreeCount: number;
  totalAdoptionOrders: number;
  totalAdoptedUsers: number;
  currentUnit: number;
  currentMultiplier: string;
  currentContributionPerTree: string;
  nextUnitTreeCount: number;
}

interface UnallocatedContributionSyncedData {
  sourceAdoptionId: string;
  sourceAccountSequence: string;
  wouldBeAccountSequence: string | null;
  contributionType: string;
  amount: string;
  reason: string | null;
  effectiveDate: string;
  expireDate: string;
}

/**
 * 全网同步服务
 * 处理系统账户算力同步和全网理论算力更新
 */
@Injectable()
export class NetworkSyncService {
  private readonly logger = new Logger(NetworkSyncService.name);

  constructor(
    private readonly prisma: PrismaService,
    private readonly systemAccountRepository: SystemMiningAccountRepository,
  ) {}

  /**
   * 处理系统账户同步事件
   * accountType: OPERATION / PROVINCE / CITY / HEADQUARTERS
   * regionCode: 省/市代码，如 440000, 440100
   */
  async handleSystemAccountSynced(data: SystemAccountSyncedData): Promise<void> {
    try {
      // 验证 accountType 是否有效
      const validTypes: SystemAccountType[] = ['OPERATION', 'PROVINCE', 'CITY', 'HEADQUARTERS'];
      const accountType = data.accountType as SystemAccountType;

      if (!validTypes.includes(accountType)) {
        this.logger.warn(`Unknown system account type: ${accountType}`);
        return;
      }

      const contribution = new ShareAmount(data.contributionBalance);

      // 使用 upsert 动态创建或更新账户
      await this.systemAccountRepository.updateContribution(
        accountType,
        data.regionCode,
        data.name,
        contribution,
      );

      this.logger.log(
        `Synced system account ${accountType} (regionCode=${data.regionCode}): contribution=${data.contributionBalance}`,
      );
    } catch (error) {
      this.logger.error(`Failed to sync system account ${data.accountType}`, error);
      throw error;
    }
  }

  /**
   * 处理全网进度更新事件
   */
  async handleNetworkProgressUpdated(data: NetworkProgressUpdatedData): Promise<void> {
    try {
      // 防御性检查
      const contributionPerTree = data.currentContributionPerTree || '20';
      const treeCount = data.totalTreeCount || 0;

      // 计算全网理论算力
      const networkTotalContribution = new Decimal(treeCount).mul(contributionPerTree);

      // 更新 MiningConfig 中的全网理论算力
      const config = await this.prisma.miningConfig.findFirst();
      if (!config) {
        this.logger.warn('MiningConfig not found, skipping network progress update');
        return;
      }

      await this.prisma.miningConfig.update({
        where: { id: config.id },
        data: {
          networkTotalContribution: networkTotalContribution,
          totalTreeCount: treeCount,
          contributionPerTree: new Decimal(contributionPerTree),
          networkLastSyncedAt: new Date(),
        },
      });

      this.logger.log(
        `Updated network progress: trees=${treeCount}, contribution=${networkTotalContribution.toString()}`,
      );
    } catch (error) {
      this.logger.error('Failed to update network progress', error);
      throw error;
    }
  }

  /**
   * 从 contribution-service 同步全网数据
   * 可通过 HTTP API 手动触发
   */
  async syncFromContributionService(contributionServiceUrl: string): Promise<{
    success: boolean;
    message: string;
    data?: {
      systemAccounts: number;
      networkTotalContribution: string;
    };
  }> {
    try {
      // 1. 同步系统账户
      const systemAccountsResponse = await fetch(
        `${contributionServiceUrl}/api/v2/admin/system-accounts`,
      );
      if (!systemAccountsResponse.ok) {
        throw new Error(`Failed to fetch system accounts: ${systemAccountsResponse.status}`);
      }
      const systemAccountsResult = await systemAccountsResponse.json();
      const systemAccounts = systemAccountsResult.accounts || [];

      for (const account of systemAccounts) {
        await this.handleSystemAccountSynced({
          accountType: account.accountType,
          regionCode: account.regionCode || null,
          name: account.name,
          contributionBalance: account.contributionBalance,
        });
      }

      // 2. 同步全网进度
      const progressResponse = await fetch(
        `${contributionServiceUrl}/api/v2/admin/network-progress`,
      );
      if (!progressResponse.ok) {
        throw new Error(`Failed to fetch network progress: ${progressResponse.status}`);
      }
      const progressResult = await progressResponse.json();
      // 从响应中提取 data 字段
      const progressData = progressResult.data || progressResult;

      // 防御性检查：确保必要字段存在
      if (!progressData.currentContributionPerTree) {
        this.logger.warn(
          `Network progress missing currentContributionPerTree, response: ${JSON.stringify(progressResult)}`,
        );
        // 使用默认值继续
        progressData.currentContributionPerTree = '20';
      }

      await this.handleNetworkProgressUpdated({
        totalTreeCount: progressData.totalTreeCount || 0,
        totalAdoptionOrders: progressData.totalAdoptionOrders || 0,
        totalAdoptedUsers: progressData.totalAdoptedUsers || 0,
        currentUnit: progressData.currentUnit || 1,
        currentMultiplier: progressData.currentMultiplier || '1',
        currentContributionPerTree: progressData.currentContributionPerTree,
        nextUnitTreeCount: progressData.nextUnitTreeCount || 100000,
      });

      // 3. 获取最新的 MiningConfig 来返回结果
      // 注：未分配算力通过 CDC (Kafka) 实时同步，不需要定时拉取
      const config = await this.prisma.miningConfig.findFirst();

      return {
        success: true,
        message: `Synced ${systemAccounts.length} system accounts and network progress`,
        data: {
          systemAccounts: systemAccounts.length,
          networkTotalContribution: config?.networkTotalContribution?.toString() || '0',
        },
      };
    } catch (error) {
      this.logger.error('Failed to sync from contribution service', error);
      return {
        success: false,
        message: `Sync failed: ${error.message}`,
      };
    }
  }

  /**
   * 获取全网理论算力
   */
  async getNetworkTotalContribution(): Promise<ShareAmount> {
    const config = await this.prisma.miningConfig.findFirst();
    if (!config || !config.networkTotalContribution) {
      return ShareAmount.zero();
    }
    return new ShareAmount(config.networkTotalContribution);
  }

  /**
   * 获取系统账户总算力
   */
  async getSystemAccountsTotalContribution(): Promise<ShareAmount> {
    return this.systemAccountRepository.getTotalContribution();
  }

  /**
   * 处理未分配算力同步事件
   * 将待解锁算力同步到 PendingContributionMining 表
   */
  async handleUnallocatedContributionSynced(data: UnallocatedContributionSyncedData): Promise<void> {
    try {
      const sourceAdoptionId = BigInt(data.sourceAdoptionId);
      const effectiveDate = new Date(data.effectiveDate);
      const expireDate = new Date(data.expireDate);

      await this.prisma.pendingContributionMining.upsert({
        where: {
          sourceAdoptionId_wouldBeAccountSequence_contributionType: {
            sourceAdoptionId,
            wouldBeAccountSequence: data.wouldBeAccountSequence || '',
            contributionType: data.contributionType,
          },
        },
        create: {
          sourceAdoptionId,
          sourceAccountSequence: data.sourceAccountSequence,
          wouldBeAccountSequence: data.wouldBeAccountSequence,
          contributionType: data.contributionType,
          amount: new Decimal(data.amount),
          reason: data.reason,
          effectiveDate,
          expireDate,
          isExpired: false,
          lastSyncedAt: new Date(),
        },
        update: {
          amount: new Decimal(data.amount),
          reason: data.reason,
          effectiveDate,
          expireDate,
          lastSyncedAt: new Date(),
        },
      });

      this.logger.log(
        `Synced unallocated contribution: adoption=${data.sourceAdoptionId}, type=${data.contributionType}, amount=${data.amount}`,
      );
    } catch (error) {
      this.logger.error(`Failed to sync unallocated contribution: adoption=${data.sourceAdoptionId}`, error);
      throw error;
    }
  }

}
