/**
 * LOOPUMAN - BLOCKCHAIN WITHDRAWAL SERVICE
 * Handles withdrawals to Celo blockchain in cUSD, USDC, USDT
 * 
 * Uses ethers.js to send stablecoin transactions
 */

'use strict';
const { ethers } = require('ethers');
const { createClient } = require('@supabase/supabase-js');

const supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_SERVICE_KEY);

// ERC20 ABI (minimal for transfer)
const ERC20_ABI = [
  'function transfer(address to, uint256 amount) returns (bool)',
  'function balanceOf(address account) view returns (uint256)',
  'function decimals() view returns (uint8)',
];

// Celo Mainnet stablecoin addresses
const STABLECOIN_ADDRESSES = {
  cUSD: '0x765DE816845861e75A25fCA122bb6898B8B1282a',
  USDC: '0xcebA9300f2b948710d2653dD7B07f33A8B32118C',
  USDT: '0x48065fbBE25f71C9282ddf5e1cD6D6A887483D5e',
};

const CONFIG = {
  VAE_RATE: 100,              // 100 VAE = $1
  MIN_WITHDRAWAL_VAE: 100,    // Minimum Ⓥ100 ($1)
  RPC_URL: process.env.CELO_RPC_URL || 'https://forno.celo.org',
  TREASURY_KEY: process.env.TREASURY_PRIVATE_KEY,
  
  // Identity tier withdrawal limits (VAE per month)
  TIER_LIMITS: {
    0: 1000,    // $10/month - Telegram only
    1: 10000,   // $100/month - GoodDollar
    2: 50000,   // $500/month - ID+Selfie
    3: null,    // Unlimited - Self Protocol
  },
};

// Initialize provider and wallet
let provider, wallet;

function initBlockchain() {
  if (!CONFIG.TREASURY_KEY) {
    console.error('[Blockchain] Treasury private key not configured!');
    return false;
  }
  
  provider = new ethers.JsonRpcProvider(CONFIG.RPC_URL);
  wallet = new ethers.Wallet(CONFIG.TREASURY_KEY, provider);
  
  console.log(`[Blockchain] Initialized with treasury: ${wallet.address}`);
  return true;
}

/**
 * Check if user can withdraw (tier limits)
 */
async function checkWithdrawalEligibility(userId, amountVae) {
  // Get user
  const { data: user } = await supabase
    .from('app_users')
    .select('identity_tier, wallet_address')
    .eq('id', userId)
    .single();
  
  if (!user) {
    return { eligible: false, reason: 'User not found' };
  }
  
  if (!user.wallet_address) {
    return { eligible: false, reason: 'No wallet address set' };
  }
  
  // Check tier limit
  const limit = CONFIG.TIER_LIMITS[user.identity_tier || 0];
  
  if (limit !== null) {
    // Get monthly withdrawn
    const { data: withdrawals } = await supabase
      .from('withdrawals')
      .select('amount_vae')
      .eq('user_id', userId)
      .eq('status', 'completed')
      .gt('created_at', new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString());
    
    const monthlyTotal = (withdrawals || []).reduce((sum, w) => sum + w.amount_vae, 0);
    const remaining = limit - monthlyTotal;
    
    if (amountVae > remaining) {
      return {
        eligible: false,
        reason: `Monthly limit exceeded. Remaining: Ⓥ${remaining} (Tier ${user.identity_tier || 0})`,
        remaining,
        limit,
      };
    }
  }
  
  // Check minimum
  if (amountVae < CONFIG.MIN_WITHDRAWAL_VAE) {
    return { eligible: false, reason: `Minimum withdrawal: Ⓥ${CONFIG.MIN_WITHDRAWAL_VAE}` };
  }
  
  return {
    eligible: true,
    wallet: user.wallet_address,
    tier: user.identity_tier || 0,
  };
}

/**
 * Get treasury balance for a stablecoin
 */
async function getTreasuryBalance(coin) {
  if (!provider || !wallet) {
    initBlockchain();
  }
  
  const address = STABLECOIN_ADDRESSES[coin];
  if (!address) {
    throw new Error(`Unknown stablecoin: ${coin}`);
  }
  
  const contract = new ethers.Contract(address, ERC20_ABI, provider);
  const balance = await contract.balanceOf(wallet.address);
  const decimals = await contract.decimals();
  
  return parseFloat(ethers.formatUnits(balance, decimals));
}

/**
 * Process withdrawal
 */
async function processWithdrawal(userId, amountVae, stablecoin) {
  // Validate stablecoin
  if (!STABLECOIN_ADDRESSES[stablecoin]) {
    throw new Error(`Invalid stablecoin: ${stablecoin}`);
  }
  
  // Check eligibility
  const eligibility = await checkWithdrawalEligibility(userId, amountVae);
  if (!eligibility.eligible) {
    throw new Error(eligibility.reason);
  }
  
  // Check user balance
  const { data: balance } = await supabase
    .from('vae_balances')
    .select('available')
    .eq('user_id', userId)
    .single();
  
  if (!balance || balance.available < amountVae) {
    throw new Error('Insufficient balance');
  }
  
  const amountUsd = amountVae / CONFIG.VAE_RATE;
  
  // Create withdrawal record
  const { data: withdrawal, error } = await supabase
    .from('withdrawals')
    .insert({
      user_id: userId,
      amount_vae: amountVae,
      amount_usd: amountUsd,
      stablecoin,
      wallet_address: eligibility.wallet,
      status: 'pending',
    })
    .select()
    .single();
  
  if (error) {
    throw new Error('Failed to create withdrawal record');
  }
  
  // Deduct from balance immediately
  await supabase
    .from('vae_balances')
    .update({
      available: balance.available - amountVae,
      total_withdrawn: (balance.total_withdrawn || 0) + amountVae,
      updated_at: new Date().toISOString(),
    })
    .eq('user_id', userId);
  
  // Record transaction
  await supabase.from('transactions').insert({
    user_id: userId,
    type: 'withdrawal',
    amount: -amountVae,
    description: `Withdrawal: $${amountUsd} ${stablecoin}`,
    metadata: { withdrawal_id: withdrawal.id },
  });
  
  // Process blockchain transfer (async)
  processBlockchainTransfer(withdrawal.id, eligibility.wallet, amountUsd, stablecoin)
    .catch(err => console.error('[Blockchain] Transfer error:', err));
  
  return {
    withdrawalId: withdrawal.id,
    amountVae,
    amountUsd,
    stablecoin,
    wallet: eligibility.wallet,
    status: 'processing',
  };
}

/**
 * Process the actual blockchain transfer
 */
async function processBlockchainTransfer(withdrawalId, toAddress, amountUsd, stablecoin) {
  if (!provider || !wallet) {
    if (!initBlockchain()) {
      await markWithdrawalFailed(withdrawalId, 'Blockchain not configured');
      return;
    }
  }
  
  try {
    const contractAddress = STABLECOIN_ADDRESSES[stablecoin];
    const contract = new ethers.Contract(contractAddress, ERC20_ABI, wallet);
    
    // Get decimals
    const decimals = await contract.decimals();
    const amount = ethers.parseUnits(amountUsd.toString(), decimals);
    
    // Check treasury balance
    const treasuryBalance = await contract.balanceOf(wallet.address);
    if (treasuryBalance < amount) {
      await markWithdrawalFailed(withdrawalId, 'Insufficient treasury balance');
      // Alert admin
      await supabase.from('system_alerts').insert({
        service: 'blockchain',
        level: 'critical',
        message: `Low ${stablecoin} balance in treasury`,
        metadata: { required: amountUsd, available: ethers.formatUnits(treasuryBalance, decimals) },
      });
      return;
    }
    
    // Send transaction
    console.log(`[Blockchain] Sending $${amountUsd} ${stablecoin} to ${toAddress}`);
    // SAFETY LAYER 4: Validate destination is not a contract before sending
    const { validateWalletAddress: validateDest } = require("./wallet-validation");
    const destCheck = await validateDest(toAddress);
    if (!destCheck.valid) {
      console.error("[Blockchain] BLOCKED send to contract:", toAddress, destCheck.code);
      await markWithdrawalFailed(withdrawalId, "Destination is a smart contract: " + toAddress);
      return;
    }
    const tx = await contract.transfer(toAddress, amount);
    
    // Wait for confirmation
    const receipt = await tx.wait();
    
    // Update withdrawal record
    await supabase
      .from('withdrawals')
      .update({
        status: 'completed',
        tx_hash: receipt.hash,
        completed_at: new Date().toISOString(),
      })
      .eq('id', withdrawalId);
    
    console.log(`[Blockchain] Withdrawal ${withdrawalId} completed: ${receipt.hash}`);
    
    return receipt.hash;
    
  } catch (error) {
    console.error('[Blockchain] Transfer failed:', error);
    await markWithdrawalFailed(withdrawalId, error.message);
    throw error;
  }
}

/**
 * Mark withdrawal as failed and refund
 */
async function markWithdrawalFailed(withdrawalId, reason) {
  const { data: withdrawal } = await supabase
    .from('withdrawals')
    .select('*')
    .eq('id', withdrawalId)
    .single();
  
  if (!withdrawal) return;
  
  // Update status
  await supabase
    .from('withdrawals')
    .update({
      status: 'failed',
      failure_reason: reason,
    })
    .eq('id', withdrawalId);
  
  // Refund user
  const { data: balance } = await supabase
    .from('vae_balances')
    .select('available, total_withdrawn')
    .eq('user_id', withdrawal.user_id)
    .single();
  
  await supabase
    .from('vae_balances')
    .update({
      available: (balance?.available || 0) + withdrawal.amount_vae,
      total_withdrawn: Math.max(0, (balance?.total_withdrawn || 0) - withdrawal.amount_vae),
    })
    .eq('user_id', withdrawal.user_id);
  
  // Record refund transaction
  await supabase.from('transactions').insert({
    user_id: withdrawal.user_id,
    type: 'withdrawal_refund',
    amount: withdrawal.amount_vae,
    description: `Withdrawal refund: ${reason}`,
    metadata: { withdrawal_id: withdrawalId },
  });
  
  console.log(`[Blockchain] Withdrawal ${withdrawalId} failed and refunded: ${reason}`);
}

/**
 * Get withdrawal history for user
 */
async function getWithdrawalHistory(userId, limit = 10) {
  const { data } = await supabase
    .from('withdrawals')
    .select('*')
    .eq('user_id', userId)
    .order('created_at', { ascending: false })
    .limit(limit);
  
  return data || [];
}

/**
 * Retry failed withdrawals (called by scheduler)
 */
async function retryFailedWithdrawals() {
  // DISABLED — This was the root cause of the infinite money bug.
  // Old logic: re-deduct → retry → fail → refund → repeat every 30 min = free money.
  // Workers are refunded on failure and can retry manually.
  console.log("[Blockchain] Auto-retry disabled (safety). Workers retry manually.");
  return;
}

module.exports = {
  initBlockchain,
  checkWithdrawalEligibility,
  getTreasuryBalance,
  processWithdrawal,
  getWithdrawalHistory,
  retryFailedWithdrawals,
  STABLECOIN_ADDRESSES,
  CONFIG,
};
