import { Injectable, Logger, BadRequestException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { JsonRpcProvider, Wallet, parseEther, parseUnits, Contract, formatEther } from 'ethers';
import Redis from 'ioredis';

const MOCK_USDC_ABI = ['function mint(address to, uint256 amount) returns (bool)'];

@Injectable()
export class FaucetService {
  private readonly logger = new Logger(FaucetService.name);
  private provider: JsonRpcProvider;
  private faucetWallet: Wallet;
  private mockUsdc: Contract;
  private redis: Redis;
  private cooldownHours: number;
  private dripAmountGnx: string;
  private dripAmountUsdc: string;

  constructor(private config: ConfigService) {
    this.provider = new JsonRpcProvider(this.config.get('RPC_URL') || 'http://localhost:8545');
    const pk = this.config.get('FAUCET_PRIVATE_KEY');
    if (pk) {
      this.faucetWallet = new Wallet(pk, this.provider);
      const usdcAddr = this.config.get('MOCK_USDC_ADDRESS') || '0x0000000000000000000000000000000000000000';
      this.mockUsdc = new Contract(usdcAddr, MOCK_USDC_ABI, this.faucetWallet);
    }
    this.redis = new Redis(this.config.get('REDIS_URL') || 'redis://localhost:6379/3');
    this.cooldownHours = parseInt(this.config.get('COOLDOWN_HOURS') || '24', 10);
    this.dripAmountGnx = this.config.get('DRIP_AMOUNT_GNX') || '100';
    this.dripAmountUsdc = this.config.get('DRIP_AMOUNT_USDC') || '10000';
  }

  /** 分发测试代币：100 GNX + 10,000 USDC */
  async drip(address: string): Promise<{ gnxTxHash: string; usdcTxHash: string; nextClaimAt: string }> {
    if (await this.hasClaimedRecently(address)) {
      const ttl = await this.redis.ttl(`faucet:${address}`);
      throw new BadRequestException(`Already claimed. Try again in ${Math.ceil(ttl / 3600)} hours`);
    }

    // 分发 GNX（原生代币转账）
    const gnxTx = await this.faucetWallet.sendTransaction({
      to: address,
      value: parseEther(this.dripAmountGnx),
    });

    // 分发测试 USDC（调用 MockUSDC.mint）
    const usdcTx = await this.mockUsdc.mint(address, parseUnits(this.dripAmountUsdc, 6));

    // 记录领取时间
    await this.redis.setex(`faucet:${address}`, this.cooldownHours * 3600, Date.now().toString());

    this.logger.log(`Dripped ${this.dripAmountGnx} GNX + ${this.dripAmountUsdc} USDC to ${address}`);

    const nextClaimAt = new Date(Date.now() + this.cooldownHours * 3600 * 1000).toISOString();

    return {
      gnxTxHash: gnxTx.hash,
      usdcTxHash: usdcTx.hash,
      nextClaimAt,
    };
  }

  /** 检查是否在冷却期内 */
  async hasClaimedRecently(address: string): Promise<boolean> {
    return (await this.redis.exists(`faucet:${address}`)) === 1;
  }

  /** 查询地址领取状态 */
  async getClaimStatus(address: string) {
    const claimed = await this.hasClaimedRecently(address);
    const ttl = claimed ? await this.redis.ttl(`faucet:${address}`) : 0;
    return {
      address,
      canClaim: !claimed,
      nextClaimIn: claimed ? `${Math.ceil(ttl / 3600)}h` : 'now',
    };
  }

  /** 获取 Faucet 钱包余额 */
  async getBalance() {
    if (!this.faucetWallet) return { gnx: '0' };
    const balance = await this.provider.getBalance(this.faucetWallet.address);
    return {
      address: this.faucetWallet.address,
      gnx: formatEther(balance),
      dripAmountGnx: this.dripAmountGnx,
      dripAmountUsdc: this.dripAmountUsdc,
    };
  }
}
