import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { HttpService } from '@nestjs/axios';
import { firstValueFrom } from 'rxjs';
import { FundAllocationDTO } from '../../domain/value-objects/fund-allocation.vo';

export interface DeductForPlantingRequest {
  userId: string;
  amount: number;
  orderId: string;
}

export interface AllocateFundsRequest {
  orderId: string;
  allocations: FundAllocationDTO[];
}

export interface WalletBalance {
  userId: string;
  available: number;
  locked: number;
  currency: string;
}

export interface FreezeForPlantingRequest {
  userId: string;
  accountSequence?: string;  // 跨服务关联标识（优先使用）
  amount: number;
  orderId: string;
}

export interface ConfirmPlantingDeductionRequest {
  userId: string;
  accountSequence?: string;  // 跨服务关联标识（优先使用）
  orderId: string;
}

export interface UnfreezeForPlantingRequest {
  userId: string;
  accountSequence?: string;  // 跨服务关联标识（优先使用）
  orderId: string;
}

export interface FreezeResult {
  success: boolean;
  frozenAmount: number;
}

/**
 * HTTP 重试配置
 */
interface RetryConfig {
  maxRetries: number;
  baseDelayMs: number;
  maxDelayMs: number;
}

const DEFAULT_RETRY_CONFIG: RetryConfig = {
  maxRetries: 3,
  baseDelayMs: 1000,
  maxDelayMs: 10000,
};

@Injectable()
export class WalletServiceClient {
  private readonly logger = new Logger(WalletServiceClient.name);
  private readonly baseUrl: string;
  private readonly retryConfig: RetryConfig;

  constructor(
    private readonly configService: ConfigService,
    private readonly httpService: HttpService,
  ) {
    this.baseUrl =
      this.configService.get<string>('WALLET_SERVICE_URL') ||
      'http://localhost:3002';
    this.retryConfig = DEFAULT_RETRY_CONFIG;
  }

  /**
   * 带重试的 HTTP 请求包装器
   * - 使用指数退避策略
   * - 只对网络错误和 5xx 错误进行重试
   * - 4xx 错误（客户端错误）不重试
   */
  private async withRetry<T>(
    operation: string,
    fn: () => Promise<T>,
    config: RetryConfig = this.retryConfig,
  ): Promise<T> {
    let lastError: Error | undefined;

    for (let attempt = 0; attempt <= config.maxRetries; attempt++) {
      try {
        return await fn();
      } catch (error: unknown) {
        lastError = error as Error;

        // 判断是否应该重试
        const shouldRetry = this.shouldRetry(error, attempt, config.maxRetries);
        if (!shouldRetry) {
          throw error;
        }

        // 计算退避延迟（指数退避 + 随机抖动）
        const delay = this.calculateBackoffDelay(attempt, config);
        this.logger.warn(
          `${operation} failed (attempt ${attempt + 1}/${config.maxRetries + 1}), ` +
            `retrying in ${delay}ms: ${(error as Error).message}`,
        );

        await this.delay(delay);
      }
    }

    throw lastError;
  }

  /**
   * 判断是否应该重试
   */
  private shouldRetry(
    error: unknown,
    attempt: number,
    maxRetries: number,
  ): boolean {
    // 已达到最大重试次数
    if (attempt >= maxRetries) {
      return false;
    }

    // 检查是否是 HTTP 响应错误
    const axiosError = error as { response?: { status?: number }; code?: string };

    // 网络错误（无响应）- 应该重试
    if (!axiosError.response) {
      // 常见的网络错误码
      const retryableCodes = ['ECONNREFUSED', 'ECONNRESET', 'ETIMEDOUT', 'ENOTFOUND'];
      if (axiosError.code && retryableCodes.includes(axiosError.code)) {
        return true;
      }
      // 超时错误
      if ((error as Error).message?.includes('timeout')) {
        return true;
      }
      return true; // 网络问题默认重试
    }

    // 5xx 服务器错误 - 应该重试
    if (axiosError.response.status && axiosError.response.status >= 500) {
      return true;
    }

    // 4xx 客户端错误 - 不重试（业务错误）
    // 429 Too Many Requests - 可以重试
    if (axiosError.response.status === 429) {
      return true;
    }

    return false;
  }

  /**
   * 计算指数退避延迟
   */
  private calculateBackoffDelay(attempt: number, config: RetryConfig): number {
    // 基础延迟 * 2^attempt + 随机抖动
    const exponentialDelay = config.baseDelayMs * Math.pow(2, attempt);
    const jitter = Math.random() * config.baseDelayMs * 0.5;
    return Math.min(exponentialDelay + jitter, config.maxDelayMs);
  }

  private delay(ms: number): Promise<void> {
    return new Promise((resolve) => setTimeout(resolve, ms));
  }

  /**
   * 获取用户钱包余额
   */
  async getBalance(userId: string): Promise<WalletBalance> {
    try {
      return await this.withRetry(`getBalance(${userId})`, async () => {
        const response = await firstValueFrom(
          this.httpService.get<WalletBalance>(
            `${this.baseUrl}/api/v1/wallets/${userId}/balance`,
          ),
        );
        return response.data;
      });
    } catch (error) {
      this.logger.error(`Failed to get balance for user ${userId}`, error);
      // 在开发环境返回模拟数据
      if (this.configService.get('NODE_ENV') === 'development') {
        return {
          userId,
          available: 100000,
          locked: 0,
          currency: 'USDT',
        };
      }
      throw error;
    }
  }

  /**
   * 认种扣款（幂等，支持重试）
   */
  async deductForPlanting(request: DeductForPlantingRequest): Promise<boolean> {
    try {
      return await this.withRetry(
        `deductForPlanting(${request.orderId})`,
        async () => {
          const response = await firstValueFrom(
            this.httpService.post(
              `${this.baseUrl}/api/v1/wallets/deduct-for-planting`,
              request,
            ),
          );
          return response.data.success;
        },
      );
    } catch (error) {
      this.logger.error(
        `Failed to deduct for planting: ${request.orderId}`,
        error,
      );
      // 在开发环境模拟成功
      if (this.configService.get('NODE_ENV') === 'development') {
        this.logger.warn('Development mode: simulating successful deduction');
        return true;
      }
      throw error;
    }
  }

  /**
   * 冻结资金用于认种
   */
  async freezeForPlanting(request: FreezeForPlantingRequest): Promise<FreezeResult> {
    try {
      return await this.withRetry(
        `freezeForPlanting(${request.orderId})`,
        async () => {
          const response = await firstValueFrom(
            this.httpService.post<FreezeResult>(
              `${this.baseUrl}/api/v1/wallets/freeze-for-planting`,
              request,
            ),
          );
          return response.data;
        },
      );
    } catch (error) {
      this.logger.error(
        `Failed to freeze for planting: ${request.orderId}`,
        error,
      );
      // 在开发环境模拟成功
      if (this.configService.get('NODE_ENV') === 'development') {
        this.logger.warn('Development mode: simulating successful freeze');
        return { success: true, frozenAmount: request.amount };
      }
      throw error;
    }
  }

  /**
   * 确认认种扣款（从冻结金额扣除）
   */
  async confirmPlantingDeduction(request: ConfirmPlantingDeductionRequest): Promise<boolean> {
    try {
      return await this.withRetry(
        `confirmPlantingDeduction(${request.orderId})`,
        async () => {
          const response = await firstValueFrom(
            this.httpService.post(
              `${this.baseUrl}/api/v1/wallets/confirm-planting-deduction`,
              request,
            ),
          );
          return response.data.success;
        },
      );
    } catch (error) {
      this.logger.error(
        `Failed to confirm planting deduction: ${request.orderId}`,
        error,
      );
      // 在开发环境模拟成功
      if (this.configService.get('NODE_ENV') === 'development') {
        this.logger.warn('Development mode: simulating successful confirmation');
        return true;
      }
      throw error;
    }
  }

  /**
   * 解冻资金（认种失败时回滚）
   */
  async unfreezeForPlanting(request: UnfreezeForPlantingRequest): Promise<boolean> {
    try {
      return await this.withRetry(
        `unfreezeForPlanting(${request.orderId})`,
        async () => {
          const response = await firstValueFrom(
            this.httpService.post(
              `${this.baseUrl}/api/v1/wallets/unfreeze-for-planting`,
              request,
            ),
          );
          return response.data.success;
        },
      );
    } catch (error) {
      this.logger.error(
        `Failed to unfreeze for planting: ${request.orderId}`,
        error,
      );
      // 在开发环境模拟成功
      if (this.configService.get('NODE_ENV') === 'development') {
        this.logger.warn('Development mode: simulating successful unfreeze');
        return true;
      }
      throw error;
    }
  }

  /**
   * 执行资金分配（幂等，支持重试）
   */
  async allocateFunds(request: AllocateFundsRequest): Promise<boolean> {
    try {
      return await this.withRetry(
        `allocateFunds(${request.orderId})`,
        async () => {
          const response = await firstValueFrom(
            this.httpService.post(
              `${this.baseUrl}/api/v1/wallets/allocate-funds`,
              request,
            ),
          );
          return response.data.success;
        },
      );
    } catch (error) {
      this.logger.error(
        `Failed to allocate funds for order: ${request.orderId}`,
        error,
      );
      // 在开发环境模拟成功
      if (this.configService.get('NODE_ENV') === 'development') {
        this.logger.warn('Development mode: simulating successful allocation');
        return true;
      }
      throw error;
    }
  }

  /**
   * 预种资金分配（使用与 reward-service 一致的 FundAllocationItem 格式）
   *
   * 与现有 allocateFunds 的区别：
   * - targetType: 'USER' | 'SYSTEM'（而非 FundAllocationTargetType 枚举）
   * - targetId: 账户序列号（而非 targetAccountId）
   * - allocationType: 权益类型字符串（用于 wallet-service ledger 分类）
   *
   * 规则：账户序列号以 S 开头 → SYSTEM，否则 → USER
   */
  async allocatePrePlantingFunds(request: {
    orderId: string;
    allocations: Array<{
      targetType: 'USER' | 'SYSTEM';
      targetId: string;
      allocationType: string;
      amount: number;
      metadata?: Record<string, unknown>;
    }>;
  }): Promise<boolean> {
    try {
      return await this.withRetry(
        `allocatePrePlantingFunds(${request.orderId})`,
        async () => {
          const response = await firstValueFrom(
            this.httpService.post(
              `${this.baseUrl}/api/v1/wallets/allocate-funds`,
              request,
            ),
          );
          // wallet-service 使用 TransformInterceptor，响应格式为
          // { success: true, data: { success: bool, allocatedCount: N, ... }, timestamp: "..." }
          const data = response.data?.data ?? response.data;
          return data?.success === true;
        },
      );
    } catch (error) {
      this.logger.error(
        `Failed to allocate pre-planting funds for order: ${request.orderId}`,
        error,
      );
      if (this.configService.get('NODE_ENV') === 'development') {
        this.logger.warn('Development mode: simulating successful pre-planting allocation');
        return true;
      }
      throw error;
    }
  }

  /**
   * [2026-03-01] 预种购买后触发待领取→可结算转换
   *
   * 调用 wallet-service 内部 API，将用户标记为 hasPlanted=true，
   * 并结算所有 PENDING 奖励为 SETTLED。
   * 幂等：重复调用安全，不会重复结算。
   */
  async settleAfterPrePlanting(accountSequence: string): Promise<{
    markedAsPlanted: boolean;
    settledCount: number;
    totalUsdt: number;
    totalHashpower: number;
  }> {
    try {
      return await this.withRetry(
        `settleAfterPrePlanting(${accountSequence})`,
        async () => {
          const response = await firstValueFrom(
            this.httpService.post(
              `${this.baseUrl}/api/v1/wallets/settle-after-pre-planting`,
              { accountSequence },
            ),
          );
          // wallet-service 使用 TransformInterceptor，响应格式为
          // { success: true, data: { markedAsPlanted, settledCount, ... }, timestamp: "..." }
          const data = response.data?.data ?? response.data;
          return data;
        },
      );
    } catch (error) {
      this.logger.error(
        `Failed to settle after pre-planting for ${accountSequence}`,
        error,
      );
      if (this.configService.get('NODE_ENV') === 'development') {
        this.logger.warn('Development mode: simulating successful settle-after-pre-planting');
        return { markedAsPlanted: false, settledCount: 0, totalUsdt: 0, totalHashpower: 0 };
      }
      throw error;
    }
  }

  /**
   * 注入底池
   */
  async injectToPool(
    batchId: string,
    amount: number,
  ): Promise<{ txHash: string }> {
    try {
      return await this.withRetry(`injectToPool(${batchId})`, async () => {
        const response = await firstValueFrom(
          this.httpService.post<{ txHash: string }>(
            `${this.baseUrl}/api/v1/pool/inject`,
            { batchId, amount },
          ),
        );
        return response.data;
      });
    } catch (error) {
      this.logger.error(`Failed to inject to pool: batch ${batchId}`, error);
      // 在开发环境返回模拟交易哈希
      if (this.configService.get('NODE_ENV') === 'development') {
        this.logger.warn('Development mode: simulating pool injection');
        return {
          txHash: `0x${Date.now().toString(16)}${Math.random().toString(16).substring(2)}`,
        };
      }
      throw error;
    }
  }

  /**
   * 确保区域账户存在
   * 在用户选择省市确认后调用，动态创建省/市区域账户
   */
  async ensureRegionAccounts(params: {
    provinceCode: string;
    provinceName: string;
    cityCode: string;
    cityName: string;
  }): Promise<{
    provinceAccount: { accountSequence: string; created: boolean };
    cityAccount: { accountSequence: string; created: boolean };
    provinceTeamAccount: { accountSequence: string; created: boolean };
    cityTeamAccount: { accountSequence: string; created: boolean };
  }> {
    try {
      return await this.withRetry(
        `ensureRegionAccounts(${params.provinceCode}, ${params.cityCode})`,
        async () => {
          const response = await firstValueFrom(
            this.httpService.post<{
              provinceAccount: { accountSequence: string; created: boolean };
              cityAccount: { accountSequence: string; created: boolean };
              provinceTeamAccount: { accountSequence: string; created: boolean };
              cityTeamAccount: { accountSequence: string; created: boolean };
            }>(
              `${this.baseUrl}/api/v1/wallets/ensure-region-accounts`,
              params,
            ),
          );
          return response.data;
        },
      );
    } catch (error) {
      this.logger.error(
        `Failed to ensure region accounts: province=${params.provinceCode}, city=${params.cityCode}`,
        error,
      );
      // 在开发环境模拟成功
      if (this.configService.get('NODE_ENV') === 'development') {
        this.logger.warn('Development mode: simulating region accounts creation');
        return {
          provinceAccount: { accountSequence: `9${params.provinceCode}`, created: true },
          cityAccount: { accountSequence: `8${params.cityCode}`, created: true },
          provinceTeamAccount: { accountSequence: `7${params.provinceCode}`, created: true },
          cityTeamAccount: { accountSequence: `6${params.cityCode}`, created: true },
        };
      }
      throw error;
    }
  }
}
