/**
 * LOOPUMAN - ADMIN BOT v3.1 FIXED
 * FIX: dotenv was inside comment block - token never loaded!
 */
'use strict';
require('dotenv').config();

const { Telegraf, Markup } = require('telegraf');
const { createClient } = require('@supabase/supabase-js');
const { ethers } = require('ethers');

// Batch 1 services
let disputeService = null;
let dashboardService = null;
try { disputeService = require('./services/disputes'); console.log('✅ Dispute service loaded'); } catch (e) { console.log('⚠️ Dispute service not available:', e.message); }
try { dashboardService = require('./services/dashboard'); console.log('✅ Dashboard service loaded'); } catch (e) { console.log('⚠️ Dashboard service not available:', e.message); }

// Treasury balance checker
const TREASURY = (process.env.TREASURY_ADDRESS || '').toLowerCase();
const CELO_RPC = process.env.CELO_RPC_URL || 'https://forno.celo.org';
const ERC20_ABI = ['function balanceOf(address) view returns (uint256)'];
const STABLECOINS = {
  cUSD:  { address: '0x765DE816845861e75A25fCA122bb6898B8B1282a', decimals: 18 },
  USDC:  { address: '0xcebA9300f2b948710d2653dD7B07f33A8B32118C', decimals: 6 },
  USDT:  { address: '0x48065fbBE25f71C9282ddf5e1cD6D6A887483D5e', decimals: 6 },
};

async function getTreasuryBalance() {
  if (!TREASURY) return null;
  try {
    const provider = new ethers.JsonRpcProvider(CELO_RPC);
    const balances = {};
    let totalUsd = 0;
    for (const [symbol, token] of Object.entries(STABLECOINS)) {
      const contract = new ethers.Contract(token.address, ERC20_ABI, provider);
      const raw = await contract.balanceOf(TREASURY);
      const amount = parseFloat(ethers.formatUnits(raw, token.decimals));
      balances[symbol] = amount;
      totalUsd += amount;
    }
    return { balances, totalUsd };
  } catch (e) {
    console.error('[Treasury] Balance check error:', e.message);
    return null;
  }
}

const CONFIG = { NAME: 'Loopuman', VAE_RATE: 100, VAE_SYMBOL: 'Ⓥ' };

if (!process.env.TELEGRAM_ADMIN_BOT_TOKEN) {
  console.error('❌ TELEGRAM_ADMIN_BOT_TOKEN not found!');
  process.exit(1);
}

const bot = new Telegraf(process.env.TELEGRAM_ADMIN_BOT_TOKEN);
const supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_SERVICE_KEY);
const ADMIN_IDS = (process.env.ADMIN_TELEGRAM_IDS || '').split(',').filter(Boolean);

console.log(`🔐 Admin Bot initialized with ${ADMIN_IDS.length} admin(s)`);

const adminState = new Map();

const fmt = {
  vae: (n) => `${CONFIG.VAE_SYMBOL}${Number(n || 0).toLocaleString()}`,
  usd: (v) => `$${(Number(v || 0) / CONFIG.VAE_RATE).toFixed(2)}`,
  both: (v) => `${fmt.vae(v)} (${fmt.usd(v)})`,
  date: (d) => d ? new Date(d).toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric' }) : 'N/A',
  esc: (t) => String(t || '').replace(/[<>&]/g, c => ({ '<': '&lt;', '>': '&gt;', '&': '&amp;' }[c])),
  short: (t, n = 50) => t && t.length > n ? t.slice(0, n - 3) + '...' : t || '',
};

const setState = (uid, data) => adminState.set(String(uid), { ...data, _ts: Date.now() });
const getState = (uid) => adminState.get(String(uid)) || {};
const clearState = (uid) => adminState.delete(String(uid));

bot.use(async (ctx, next) => {
  if (!ADMIN_IDS.includes(String(ctx.from?.id))) {
    return ctx.reply('⛔ Unauthorized. This bot is for admins only.');
  }
  return next();
});

async function cb(ctx, text = '') { try { await ctx.answerCbQuery(text); } catch {} }

async function edit(ctx, text, kb = {}) {
  try { return await ctx.editMessageText(text, { parse_mode: 'HTML', ...kb }); }
  catch (e) { if (!e.message?.includes('not modified')) try { return await ctx.reply(text, { parse_mode: 'HTML', ...kb }); } catch {} }
}

async function send(ctx, text, kb = {}) {
  try { return await ctx.reply(text, { parse_mode: 'HTML', ...kb }); } catch {}
}

async function notifyUser(telegramId, text) {
  try { await bot.telegram.sendMessage(telegramId, text, { parse_mode: 'HTML' }); return true; } catch { return false; }
}

const back = (action = 'menu') => Markup.inlineKeyboard([[Markup.button.callback('🔙 BACK', action)]]);

const db = {
  async getStats() {
    const now = new Date();
    const today = new Date(now.getFullYear(), now.getMonth(), now.getDate()).toISOString();
    const [
      users, todayU, totalT, openT, pendWd, disputes, tickets, subs, reports,
      workers, requesters, waUsers, deposits, pendingDeps, feeRevenue, depositRevenue,
      firstTask, firstComplete, firstWithdraw, aiUsers
    ] = await Promise.all([
      // Exclude AI agent users (telegram_id starts with 'ai_')
      supabase.from('app_users').select('*', { count: 'exact', head: true }).not('telegram_id', 'like', 'ai_%'),
      supabase.from('app_users').select('*', { count: 'exact', head: true }).gte('created_at', today).not('telegram_id', 'like', 'ai_%'),
      supabase.from('tasks').select('*', { count: 'exact', head: true }),
      // Count OPEN + ACTIVE tasks (browsable), not just 'active'
      supabase.from('tasks').select('*', { count: 'exact', head: true }).in('status', ['open', 'active']),
      supabase.from('withdrawals').select('*', { count: 'exact', head: true }).eq('status', 'pending'),
      supabase.from('disputes').select('*', { count: 'exact', head: true }).eq('status', 'open'),
      supabase.from('support_tickets').select('*', { count: 'exact', head: true }).eq('status', 'open'),
      supabase.from('submissions').select('platform_fee').eq('status', 'approved'),
      supabase.from('system_alerts').select('*', { count: 'exact', head: true }).eq('type', 'task_report').eq('acknowledged', false),
      // Workers & requesters (exclude AI)
      supabase.from('app_users').select('*', { count: 'exact', head: true }).eq('is_worker', true).not('telegram_id', 'like', 'ai_%'),
      supabase.from('app_users').select('*', { count: 'exact', head: true }).eq('is_requester', true).not('telegram_id', 'like', 'ai_%'),
      // WhatsApp users (have phone)
      supabase.from('app_users').select('*', { count: 'exact', head: true }).not('phone', 'is', null).not('telegram_id', 'like', 'ai_%'),
      // Crypto deposits
      supabase.from('crypto_deposits').select('amount_usd, amount_vae').eq('status', 'completed').then(r => r).catch(() => ({ data: [] })),
      // Unmatched deposits
      supabase.from('unmatched_deposits').select('*', { count: 'exact', head: true }).eq('status', 'pending').then(r => r).catch(() => ({ count: 0 })),
      // Fee revenue
      supabase.from('transactions').select('amount').eq('type', 'platform_fee').then(r => r).catch(() => ({ data: [] })),
      // Deposit revenue
      supabase.from('transactions').select('amount').eq('type', 'crypto_deposit').then(r => r).catch(() => ({ data: [] })),
      // Funnel: users who posted at least 1 task
      supabase.from('tasks').select('requester_id').limit(10000).then(r => r).catch(() => ({ data: [] })),
      // Funnel: users who completed at least 1 task
      supabase.from('app_users').select('*', { count: 'exact', head: true }).gt('tasks_completed', 0).not('telegram_id', 'like', 'ai_%').then(r => r).catch(() => ({ count: 0 })),
      // Funnel: users who withdrew at least once
      supabase.from('withdrawals').select('user_id').eq('status', 'completed').limit(10000).then(r => r).catch(() => ({ data: [] })),
      // Count AI agents separately
      supabase.from('app_users').select('*', { count: 'exact', head: true }).like('telegram_id', 'ai_%'),
    ]);

    const platformRevenue = subs.data?.reduce((a, b) => a + (b.platform_fee || 0), 0) || 0;
    const totalDepositsUsd = deposits.data?.reduce((a, b) => a + (b.amount_usd || 0), 0) || 0;
    const totalDepositsVae = deposits.data?.reduce((a, b) => a + (b.amount_vae || 0), 0) || 0;
    const uniquePosters = new Set(firstTask.data?.map(t => t.requester_id) || []).size;
    const uniqueWithdrawers = new Set(firstWithdraw.data?.map(w => w.user_id) || []).size;

    return {
      totalUsers: users.count || 0,
      todayUsers: todayU.count || 0,
      totalTasks: totalT.count || 0,
      openTasks: openT.count || 0,
      pendingWithdrawals: pendWd.count || 0,
      openDisputes: disputes.count || 0,
      openTickets: tickets.count || 0,
      platformRevenue,
      openReports: reports.count || 0,
      // User breakdown (all exclude AI agents)
      totalWorkers: workers.count || 0,
      totalRequesters: requesters.count || 0,
      whatsappUsers: waUsers.count || 0,
      aiAgents: aiUsers.count || 0,
      // Financial
      totalDepositsUsd,
      totalDepositsVae,
      pendingDeposits: pendingDeps.count || 0,
      // Funnel
      usersPostedTask: uniquePosters,
      usersCompletedTask: firstComplete.count || 0,
      usersWithdrawn: uniqueWithdrawers,
    };
  },
  async searchUser(query) {
    if (/^\d+$/.test(query)) {
      const { data } = await supabase.from('app_users').select('*').eq('telegram_id', query).maybeSingle();
      if (data) return data;
    }
    if (query.startsWith('@')) {
      const { data } = await supabase.from('app_users').select('*').ilike('username', query.slice(1)).maybeSingle();
      if (data) return data;
    }
    const { data } = await supabase.from('app_users').select('*').ilike('id', `%${query}%`).limit(1).maybeSingle();
    return data;
  },
  async getBalance(userId) {
    const { data } = await supabase.from('vae_balances').select('*').eq('user_id', userId).maybeSingle();
    return data || { available: 0, pending: 0, total_earned: 0, total_withdrawn: 0 };
  },
  async creditUser(userId, amount, reason) {
    const { data: bal } = await supabase.from('vae_balances').select('available, total_earned').eq('user_id', userId).single();
    await supabase.from('vae_balances').update({ available: (bal?.available || 0) + amount, total_earned: (bal?.total_earned || 0) + amount }).eq('user_id', userId);
    await supabase.from('transactions').insert({ user_id: userId, type: 'admin_credit', amount, description: reason });
  },
  async debitUser(userId, amount, reason) {
    const { data: bal } = await supabase.from('vae_balances').select('available').eq('user_id', userId).single();
    if ((bal?.available || 0) < amount) return false;
    await supabase.from('vae_balances').update({ available: bal.available - amount }).eq('user_id', userId);
    await supabase.from('transactions').insert({ user_id: userId, type: 'admin_debit', amount: -amount, description: reason });
    return true;
  },
  async banUser(userId, days) {
    const banUntil = new Date(Date.now() + days * 86400000).toISOString();
    await supabase.from('app_users').update({ banned_until: banUntil }).eq('id', userId);
  },
  async unbanUser(userId) {
    await supabase.from('app_users').update({ banned_until: null }).eq('id', userId);
  },
  async getUserById(userId) {
    const { data } = await supabase.from('app_users').select('*').eq('id', userId).single();
    return data;
  },
  async getPendingWithdrawals() {
    const { data } = await supabase.from('withdrawals').select('*, user:app_users(id, telegram_id, username, first_name)').eq('status', 'pending').order('created_at');
    return data || [];
  },
  async getOpenDisputes() {
    // Use dispute service if available (consistent queries)
    if (disputeService) return await disputeService.getOpen();
    const { data } = await supabase.from('disputes').select('*, worker:app_users!disputes_worker_id_fkey(*), submission:submissions(*, task:tasks(*))').eq('status', 'open').order('created_at');
    return data || [];
  },
  async resolveDispute(disputeId, winner, resolution) {
    // Use dispute service for atomic resolution (all-or-nothing via RPC)
    if (disputeService) {
      const result = await disputeService.resolve(disputeId, resolution, winner, resolution);
      if (result.error) {
        console.error('[Admin] Dispute resolution failed:', result.error);
        return { error: result.error };
      }
      return result;
    }
    // Fallback: just update status (no payment handling — legacy)
    await supabase.from('disputes').update({ status: 'resolved', resolution: `${winner}: ${resolution}`, resolved_at: new Date().toISOString() }).eq('id', disputeId);
    return { success: true };
  },
  async getOpenTickets() {
    // Try with join first
    const { data, error } = await supabase.from('support_tickets').select('*, user:app_users(id, telegram_id, username, first_name)').eq('status', 'open').order('created_at');
    if (error) {
      console.error('[Tickets] Join query failed:', error.message, '- falling back');
      // Fallback: query without join
      const { data: plain } = await supabase.from('support_tickets').select('*').eq('status', 'open').order('created_at');
      return plain || [];
    }
    return data || [];
  },
  async replyToTicket(ticketId, response) {
    await supabase.from('support_tickets').update({ response, status: 'resolved', updated_at: new Date().toISOString() }).eq('id', ticketId);
  },
  async getTicket(ticketId) {
    const { data, error } = await supabase.from('support_tickets').select('*, user:app_users(id, telegram_id, username, first_name)').eq('id', ticketId).single();
    if (error) {
      console.error('[Ticket] Join query failed:', error.message, '- falling back');
      const { data: plain } = await supabase.from('support_tickets').select('*').eq('id', ticketId).single();
      return plain;
    }
    return data;
  },
  async getAllUsers(limit = 1000) {
    const { data } = await supabase.from('app_users').select('telegram_id, is_worker, is_requester').limit(limit);
    return data || [];
  },
  async getPendingKYC() {
    const { data } = await supabase.from('app_users').select('*').not('id_photo', 'is', null).not('selfie_photo', 'is', null).eq('identity_tier', 1);
    return data || [];
  },
};

function getDisputeSuggestion(dispute) {
  const worker = dispute.worker;
  let suggestion = '';
  let recommended = 'split';
  if (worker?.reputation >= 90 && worker?.tasks_completed >= 20) {
    suggestion += '• Worker has excellent reputation and history.\n';
    recommended = 'worker';
  } else if (worker?.reputation < 70) {
    suggestion += '• Worker has low reputation, may indicate issues.\n';
    recommended = 'requester';
  }
  if (!dispute.submission?.rejection_reason || dispute.submission.rejection_reason.length < 20) {
    suggestion += '• Requester gave no/vague rejection reason.\n';
    recommended = 'worker';
  }
  return { suggestion: suggestion || '• Insufficient data for recommendation.', recommended };
}

async function showMenu(ctx, useEdit = false) {
  const msg = `🔐 <b>${CONFIG.NAME} Admin Panel</b>\n\nWelcome, Admin!`;
  const kb = Markup.inlineKeyboard([
    [Markup.button.callback('📊 Dashboard', 'dashboard')],
    [Markup.button.callback('👥 Users', 'users'), Markup.button.callback('💰 Withdrawals', 'withdrawals')],
    [Markup.button.callback('⚠️ Disputes', 'disputes'), Markup.button.callback('🎫 Tickets', 'tickets')],
    [Markup.button.callback('🛡️ KYC Review', 'kyc'), Markup.button.callback('📢 Broadcast', 'broadcast')],
    [Markup.button.callback('🚩 Reports', 'reports')],
  ]);
  return useEdit ? edit(ctx, msg, kb) : send(ctx, msg, kb);
}

bot.start(async (ctx) => {
  await showMenu(ctx, false);
});

bot.action('menu', async (ctx) => { await cb(ctx); return showMenu(ctx, true); });

bot.action('dashboard', async (ctx) => {
  await cb(ctx);
  const s = await db.getStats();

  let msg = `📊 <b>DASHBOARD</b>\n━━━━━━━━━━━━━━━━━━━━\n\n`;

  // Users section (AI agents excluded from counts)
  msg += `<b>👥 USERS</b> (${s.totalUsers} real${s.aiAgents ? ` + ${s.aiAgents} AI` : ''})\n`;
  msg += `   +${s.todayUsers} today\n`;
  msg += `   Workers: ${s.totalWorkers} | Requesters: ${s.totalRequesters}\n`;
  msg += `   WhatsApp: ${s.whatsappUsers}\n\n`;

  // Tasks - show open+active as "Open"
  msg += `<b>📋 TASKS</b>\n`;
  msg += `   Open: ${s.openTasks} / ${s.totalTasks} total\n\n`;

  // Revenue
  msg += `<b>💰 REVENUE</b>\n`;
  msg += `   Platform fees: ${fmt.both(s.platformRevenue)}\n`;
  msg += `   Crypto deposits: $${s.totalDepositsUsd?.toFixed(2) || '0.00'} (${fmt.vae(s.totalDepositsVae)})\n`;

  // Dashboard service extras
  if (dashboardService) {
    try {
      const overview = await dashboardService.getOverview();
      msg += `   Weekly volume: ${fmt.both(overview.weeklyVolume)}\n`;
      msg += `   Today's txns: ${overview.todayTransactions}\n`;
    } catch {}
  }

  // Attention items
  msg += `\n<b>⚠️ ATTENTION</b>\n`;
  msg += `   ${s.pendingWithdrawals || 0} pending withdrawals\n`;
  msg += `   ${s.pendingDeposits || 0} unmatched deposits\n`;
  msg += `   ${s.openDisputes || 0} open disputes\n`;
  msg += `   ${s.openTickets || 0} open tickets\n`;
  msg += `   🚩 ${s.openReports || 0} task reports`;

  const btns = [
    [Markup.button.callback('🔄 Refresh', 'dashboard')],
    [Markup.button.callback('🏦 Treasury', 'treasury'), Markup.button.callback('🔀 Funnel', 'funnel')],
    [Markup.button.callback('📈 7-Day Growth', 'growth'), Markup.button.callback('🏆 Top Workers', 'top_workers')],
  ];
  const actionRow = [];
  if (s.pendingWithdrawals > 0) actionRow.push(Markup.button.callback(`💰 ${s.pendingWithdrawals} WD`, 'withdrawals'));
  if (s.openDisputes > 0) actionRow.push(Markup.button.callback(`⚠️ ${s.openDisputes} Disputes`, 'disputes'));
  if (s.openTickets > 0) actionRow.push(Markup.button.callback(`🎫 ${s.openTickets} Tickets`, 'tickets'));
  if (actionRow.length) btns.push(actionRow);
  btns.push([Markup.button.callback('🔙 BACK', 'menu')]);
  await edit(ctx, msg, Markup.inlineKeyboard(btns));
});

// Treasury Balance (live on-chain)
bot.action('treasury', async (ctx) => {
  await cb(ctx);
  const treasury = await getTreasuryBalance();
  if (!treasury) return edit(ctx, `🏦 <b>Treasury</b>\n\n❌ TREASURY_ADDRESS not configured or RPC error.`, back('dashboard'));

  let msg = `🏦 <b>Treasury Balance</b>\n━━━━━━━━━━━━━━━━━━━━\n\n`;
  msg += `Address: <code>${TREASURY}</code>\n\n`;
  for (const [symbol, amount] of Object.entries(treasury.balances)) {
    msg += `   ${symbol}: $${amount.toFixed(2)}\n`;
  }
  msg += `\n💵 <b>Total: $${treasury.totalUsd.toFixed(2)}</b>`;
  await edit(ctx, msg, Markup.inlineKeyboard([
    [Markup.button.callback('🔄 Refresh', 'treasury')],
    [Markup.button.callback('🔙 BACK', 'dashboard')],
  ]));
});

// Conversion Funnel
bot.action('funnel', async (ctx) => {
  await cb(ctx);
  const s = await db.getStats();
  const total = s.totalUsers || 1;

  const steps = [
    { label: 'Signed Up', count: s.totalUsers, pct: 100 },
    { label: 'Posted/Accepted Task', count: s.usersPostedTask + s.usersCompletedTask, pct: Math.round(((s.usersPostedTask + s.usersCompletedTask) / total) * 100) },
    { label: 'Completed Task', count: s.usersCompletedTask, pct: Math.round((s.usersCompletedTask / total) * 100) },
    { label: 'Made Withdrawal', count: s.usersWithdrawn, pct: Math.round((s.usersWithdrawn / total) * 100) },
  ];

  let msg = `🔀 <b>Conversion Funnel</b>\n━━━━━━━━━━━━━━━━━━━━\n\n`;
  for (const step of steps) {
    const bar = '█'.repeat(Math.max(1, Math.round(step.pct / 5)));
    msg += `${step.label}\n${bar} ${step.count} (${step.pct}%)\n\n`;
  }
  await edit(ctx, msg, back('dashboard'));
});

// 7-Day Growth (from dashboard service)
bot.action('growth', async (ctx) => {
  await cb(ctx);
  if (!dashboardService) return edit(ctx, '❌ Dashboard service not available.', back('dashboard'));
  try {
    const days = await dashboardService.getGrowth();
    let msg = `📈 <b>7-Day Growth</b>\n━━━━━━━━━━━━━━━━━━━━\n\n`;
    for (const d of days) {
      const bar = '█'.repeat(Math.min(d.new_users, 20)) || '░';
      msg += `${d.date.slice(5)}: ${bar} ${d.new_users}\n`;
    }
    await edit(ctx, msg, back('dashboard'));
  } catch (e) {
    await edit(ctx, `❌ Error: ${e.message}`, back('dashboard'));
  }
});

// Top Workers (from dashboard service)
bot.action('top_workers', async (ctx) => {
  await cb(ctx);
  if (!dashboardService) return edit(ctx, '❌ Dashboard service not available.', back('dashboard'));
  try {
    const workers = await dashboardService.getTopWorkers(10);
    let msg = `🏆 <b>Top Workers</b>\n━━━━━━━━━━━━━━━━━━━━\n\n`;
    workers.forEach((w, i) => {
      msg += `${i + 1}. ${fmt.esc(w.first_name || w.username || 'Unknown')} — ${w.tasks_completed || 0} tasks, ${fmt.vae(w.total_earned)}\n`;
    });
    if (!workers.length) msg += 'No workers yet.';
    await edit(ctx, msg, back('dashboard'));
  } catch (e) {
    await edit(ctx, `❌ Error: ${e.message}`, back('dashboard'));
  }
});

bot.action('users', async (ctx) => {
  await cb(ctx);
  setState(ctx.from.id, { action: 'user_search' });
  await edit(ctx, `👥 <b>User Management</b>\n\nSearch by:\n• Telegram ID\n• @username\n• Short ID\n\nType your search:`, back('menu'));
});

bot.action('withdrawals', async (ctx) => {
  await cb(ctx);
  const pending = await db.getPendingWithdrawals();
  // Also get recent completed/failed (last 10)
  const { data: recent } = await supabase.from('withdrawals')
    .select('*, user:app_users(id, telegram_id, username, first_name)')
    .in('status', ['completed', 'failed'])
    .order('created_at', { ascending: false })
    .limit(10);
  
  let msg = `💰 <b>Withdrawals</b>\n\n`;
  
  if (pending.length) {
    msg += `⏳ <b>Pending</b> (${pending.length})\n`;
    pending.slice(0, 10).forEach((w, i) => { msg += `${i+1}. ${w.user?.first_name} — ${fmt.both(w.amount_vae)} ${w.stablecoin}\n`; });
    msg += `\n`;
  } else {
    msg += `✅ No pending withdrawals\n\n`;
  }
  
  if (recent && recent.length) {
    msg += `📜 <b>Recent</b>\n`;
    recent.forEach(w => {
      const icon = w.status === 'completed' ? '✅' : '❌';
      const time = new Date(w.created_at).toLocaleDateString('en-GB', { day: '2-digit', month: 'short', hour: '2-digit', minute: '2-digit' });
      msg += `${icon} ${w.user?.first_name || 'User'} — ${fmt.both(w.amount_vae)} ${w.stablecoin} • ${time}\n`;
      if (w.tx_hash) msg += `   🔗 <code>${w.tx_hash.slice(0, 14)}...</code>\n`;
    });
  }
  
  const btns = pending.slice(0, 5).map((w, i) => [Markup.button.callback(`${i+1}. ${w.user?.first_name} — ${fmt.usd(w.amount_vae)}`, `wd_${w.id}`)]);
  btns.push([Markup.button.callback('🔙 BACK', 'menu')]);
  await edit(ctx, msg, Markup.inlineKeyboard(btns));
});

bot.action(/^wd_(.+)$/, async (ctx) => {
  await cb(ctx);
  const { data: wd } = await supabase.from('withdrawals').select('*, user:app_users(*)').eq('id', ctx.match[1]).single();
  if (!wd) return edit(ctx, '❌ Not found', back('withdrawals'));
  let msg = `💰 <b>Withdrawal</b>\n\n👤 ${wd.user?.first_name}\n💵 ${fmt.both(wd.amount_vae)} ${wd.stablecoin}\n📍 <code>${wd.wallet_address}</code>`;
  await edit(ctx, msg, Markup.inlineKeyboard([
    [Markup.button.callback('✅ Approve', `approvewd_${wd.id}`), Markup.button.callback('❌ Reject', `rejectwd_${wd.id}`)],
    [Markup.button.callback('🔙 BACK', 'withdrawals')],
  ]));
});

bot.action(/^approvewd_(.+)$/, async (ctx) => {
  await cb(ctx);
  await supabase.from('withdrawals').update({ status: 'completed', completed_at: new Date().toISOString() }).eq('id', ctx.match[1]);
  await edit(ctx, `✅ Approved! Send funds manually.`, back('withdrawals'));
});

bot.action(/^rejectwd_(.+)$/, async (ctx) => {
  await cb(ctx);
  setState(ctx.from.id, { action: 'reject_withdrawal', wdId: ctx.match[1] });
  await edit(ctx, `❌ Reject Withdrawal\n\nEnter reason:`, back('withdrawals'));
});

bot.action('disputes', async (ctx) => {
  await cb(ctx);
  const disputes = await db.getOpenDisputes();
  if (!disputes.length) return edit(ctx, `⚠️ <b>Disputes</b>\n\n✅ No open disputes.`, back('menu'));
  let msg = `⚠️ <b>Open Disputes</b> (${disputes.length})\n\n`;
  disputes.slice(0, 10).forEach((d, i) => { msg += `${i+1}. ${d.worker?.first_name} - ${fmt.short(d.submission?.task?.title, 25)}\n`; });
  const btns = disputes.slice(0, 5).map((d, i) => [Markup.button.callback(`${i+1}. ${d.worker?.first_name}`, `dispute_${d.id}`)]);
  btns.push([Markup.button.callback('🔙 BACK', 'menu')]);
  await edit(ctx, msg, Markup.inlineKeyboard(btns));
});

bot.action(/^dispute_(.+)$/, async (ctx) => {
  await cb(ctx);
  const { data: d } = await supabase.from('disputes').select('*, worker:app_users!disputes_worker_id_fkey(*), submission:submissions(*, task:tasks(*))').eq('id', ctx.match[1]).single();
  if (!d) return edit(ctx, '❌ Not found', back('disputes'));
  const ai = getDisputeSuggestion(d);
  let msg = `⚠️ <b>Dispute</b>\n\n📋 Task: ${d.submission?.task?.title}\n💰 ${fmt.vae(d.submission?.worker_payment)}\n\n`;
  msg += `👷 Worker: ${d.worker?.first_name} (Rep: ${d.worker?.reputation})\n`;
  msg += `📝 Submission: ${fmt.short(d.submission?.content, 100)}\n`;
  msg += `❌ Rejection: ${fmt.short(d.submission?.rejection_reason, 100)}\n`;
  msg += `⚠️ Dispute: ${fmt.short(d.reason, 100)}\n\n`;
  msg += `🤖 AI: ${ai.suggestion}\n📊 Recommended: ${ai.recommended.toUpperCase()}`;
  await edit(ctx, msg, Markup.inlineKeyboard([
    [Markup.button.callback('👷 Worker Wins', `resolve_${d.id}_worker`)],
    [Markup.button.callback('👔 Requester Wins', `resolve_${d.id}_requester`)],
    [Markup.button.callback('⚖️ Split 50/50', `resolve_${d.id}_split`)],
    [Markup.button.callback('🔙 BACK', 'disputes')],
  ]));
});

bot.action(/^resolve_(.+)_(worker|requester|split)$/, async (ctx) => {
  await cb(ctx);
  setState(ctx.from.id, { action: 'resolve_dispute', disputeId: ctx.match[1], winner: ctx.match[2] });
  await edit(ctx, `⚖️ Decision: ${ctx.match[2].toUpperCase()}\n\nEnter resolution notes:`, back('disputes'));
});

bot.action(/^tickets(?:_(\d+))?$/, async (ctx) => {
  await cb(ctx);
  const offset = parseInt(ctx.match?.[1] || '0');
  const tickets = await db.getOpenTickets();
  if (!tickets.length) return edit(ctx, `🎫 <b>Tickets</b>\n\n✅ No open tickets.`, back('menu'));
  const PER_PAGE = 10;
  const totalPages = Math.ceil(tickets.length / PER_PAGE);
  const page = Math.floor(offset / PER_PAGE) + 1;
  const pageTickets = tickets.slice(offset, offset + PER_PAGE);
  let msg = `🎫 <b>Open Tickets</b> (${tickets.length})\n📄 Page ${page}/${totalPages}\n\n`;
  pageTickets.forEach((t, i) => {
    const name = t.user?.first_name || t.user_id?.slice(0, 8) || 'User';
    msg += `${offset + i + 1}. ${t.ticket_id || `#${offset + i + 1}`} — ${name}: ${fmt.short(t.subject || t.message, 30)}\n`;
  });
  const btns = pageTickets.map((t, i) => [Markup.button.callback(`${offset + i + 1}. ${t.ticket_id || 'View'}`, `ticket_${t.id}`)]);
  const nav = [];
  if (offset > 0) nav.push(Markup.button.callback('◀️ Prev', `tickets_${offset - PER_PAGE}`));
  if (offset + PER_PAGE < tickets.length) nav.push(Markup.button.callback('Next ▶️', `tickets_${offset + PER_PAGE}`));
  if (nav.length) btns.push(nav);
  btns.push([Markup.button.callback('🔙 BACK', 'menu')]);
  await edit(ctx, msg, Markup.inlineKeyboard(btns));
});

bot.action(/^ticket_(.+)$/, async (ctx) => {
  await cb(ctx);
  const t = await db.getTicket(ctx.match[1]);
  if (!t) return edit(ctx, '❌ Not found', back('tickets'));
  setState(ctx.from.id, { action: 'reply_ticket', ticketId: t.id, userTgId: t.user?.telegram_id });
  await edit(ctx, `🎫 <b>${t.ticket_id}</b>\n\n👤 ${t.user?.first_name}\n📝 ${t.message}\n\nType your reply:`, back('tickets'));
});

bot.action('kyc', async (ctx) => {
  await cb(ctx);
  const pending = await db.getPendingKYC();
  if (!pending.length) return edit(ctx, `🛡️ <b>KYC</b>\n\n✅ No pending.`, back('menu'));
  const btns = pending.slice(0, 5).map((u, i) => [Markup.button.callback(`${i+1}. ${u.first_name}`, `viewkyc_${u.id}`)]);
  btns.push([Markup.button.callback('🔙 BACK', 'menu')]);
  await edit(ctx, `🛡️ <b>Pending KYC</b> (${pending.length})`, Markup.inlineKeyboard(btns));
});

bot.action(/^viewkyc_(.+)$/, async (ctx) => {
  await cb(ctx);
  const u = await db.getUserById(ctx.match[1]);
  if (!u) return edit(ctx, '❌ Not found', back('kyc'));
  if (u.id_photo) await ctx.replyWithPhoto(u.id_photo, { caption: '🆔 ID' });
  if (u.selfie_photo) await ctx.replyWithPhoto(u.selfie_photo, { caption: '🤳 Selfie' });
  await send(ctx, `KYC for ${u.first_name}`, Markup.inlineKeyboard([
    [Markup.button.callback('✅ Approve', `approvekyc_${u.id}`), Markup.button.callback('❌ Reject', `rejectkyc_${u.id}`)],
    [Markup.button.callback('🔙 BACK', 'kyc')],
  ]));
});

bot.action(/^approvekyc_(.+)$/, async (ctx) => {
  await cb(ctx);
  await supabase.from('app_users').update({ identity_tier: 2 }).eq('id', ctx.match[1]);
  const u = await db.getUserById(ctx.match[1]);
  if (u?.telegram_id) notifyUser(u.telegram_id, `✅ <b>Identity Verified!</b>\n\nYou're now Verified tier.`);
  await edit(ctx, `✅ KYC approved.`, back('kyc'));
});

bot.action(/^rejectkyc_(.+)$/, async (ctx) => {
  await cb(ctx);
  await supabase.from('app_users').update({ id_photo: null, selfie_photo: null }).eq('id', ctx.match[1]);
  await edit(ctx, `❌ KYC rejected.`, back('kyc'));
});

// ═══ REPORTS: View flagged tasks from system_alerts ═══
bot.action('reports', async (ctx) => {
  await cb(ctx);
  const { data: alerts } = await supabase
    .from('system_alerts')
    .select('*')
    .eq('type', 'task_report')
    .eq('acknowledged', false)
    .order('created_at', { ascending: false })
    .limit(15);
  
  if (!alerts?.length) return edit(ctx, `🚩 <b>Reports</b>\n\n✅ No unresolved reports.`, back('menu'));
  
  let msg = `🚩 <b>Unresolved Reports</b> (${alerts.length})\n\n`;
  alerts.slice(0, 10).forEach((a, i) => {
    const d = a.data || {};
    const taskShort = d.task_id ? d.task_id.slice(0, 8) : 'N/A';
    const reason = d.reason || a.message || 'Unknown';
    const time = new Date(a.created_at).toLocaleDateString('en-GB', { day: 'numeric', month: 'short' });
    msg += `${i + 1}. <b>${reason}</b> — Task ${taskShort}… (${time})\n`;
  });
  
  const btns = alerts.slice(0, 5).map((a, i) => [Markup.button.callback(`${i + 1}. View Report`, `viewrpt_${a.id}`)]);
  btns.push([Markup.button.callback('🔙 BACK', 'menu')]);
  await edit(ctx, msg, Markup.inlineKeyboard(btns));
});

bot.action(/^viewrpt_(.+)$/, async (ctx) => {
  await cb(ctx);
  const { data: alert } = await supabase.from('system_alerts').select('*').eq('id', ctx.match[1]).single();
  if (!alert) return edit(ctx, '❌ Report not found.', back('reports'));
  
  const d = alert.data || {};
  let msg = `🚩 <b>Report Detail</b>\n\n`;
  msg += `📋 <b>Type:</b> ${d.reason || alert.message}\n`;
  msg += `📝 <b>Details:</b> ${fmt.short(d.details, 200) || 'None'}\n`;
  msg += `⏰ <b>Time:</b> ${fmt.date(alert.created_at)}\n`;
  
  if (d.task_id) {
    const { data: task } = await supabase.from('tasks').select('id, title, description, status, requester_id').eq('id', d.task_id).single();
    if (task) {
      msg += `\n📌 <b>Task:</b> ${fmt.esc(task.title)}\n`;
      msg += `📄 <b>Desc:</b> ${fmt.short(task.description, 150)}\n`;
      msg += `🔖 <b>Status:</b> ${task.status}\n`;
      msg += `👤 <b>Poster:</b> ${task.requester_id.slice(0, 8)}…\n`;
    }
  }
  
  const btns = [];
  if (d.task_id) {
    btns.push([
      Markup.button.callback('⏸️ Pause Task', `pausetask_${alert.id}`),
      Markup.button.callback('🗑️ Remove Task', `removetask_${alert.id}`),
    ]);
    btns.push([Markup.button.callback('🚫 Ban Poster', `banposter_${alert.id}`)]);
  }
  btns.push([
    Markup.button.callback('✅ Dismiss', `dismissrpt_${alert.id}`),
    Markup.button.callback('🔙 Reports', 'reports'),
  ]);
  await edit(ctx, msg, Markup.inlineKeyboard(btns));
});

bot.action(/^pausetask_(.+)$/, async (ctx) => {
  await cb(ctx);
  const { data: alert } = await supabase.from('system_alerts').select('data').eq('id', ctx.match[1]).single();
  if (!alert?.data?.task_id) return edit(ctx, '❌ No task linked.', back('reports'));
  await supabase.from('tasks').update({ status: 'paused' }).eq('id', alert.data.task_id);
  await supabase.from('system_alerts').update({ acknowledged: true }).eq('id', ctx.match[1]);
  await edit(ctx, `⏸️ Task paused. Workers can no longer see or accept it.`, back('reports'));
});

bot.action(/^removetask_(.+)$/, async (ctx) => {
  await cb(ctx);
  const { data: alert } = await supabase.from('system_alerts').select('data').eq('id', ctx.match[1]).single();
  if (!alert?.data?.task_id) return edit(ctx, '❌ No task linked.', back('reports'));
  
  // Cancel task + refund requester
  const { data: task } = await supabase.from('tasks').select('id, budget, requester_id, status').eq('id', alert.data.task_id).single();
  if (task && (task.status === 'open' || task.status === 'active' || task.status === 'paused')) {
    await supabase.from('tasks').update({ status: 'cancelled' }).eq('id', task.id);
    // Refund requester
    const { data: bal } = await supabase.from('vae_balances').select('available').eq('user_id', task.requester_id).single();
    await supabase.from('vae_balances').update({ available: (bal?.available || 0) + task.budget }).eq('user_id', task.requester_id);
    await supabase.from('transactions').insert({
      user_id: task.requester_id, type: 'admin_refund', amount: task.budget,
      task_id: task.id, description: 'Task removed by admin (policy violation)',
    });
  }
  await supabase.from('system_alerts').update({ acknowledged: true }).eq('id', ctx.match[1]);
  await edit(ctx, `🗑️ Task removed + requester refunded ${fmt.vae(task?.budget || 0)}.`, back('reports'));
});

bot.action(/^banposter_(.+)$/, async (ctx) => {
  await cb(ctx);
  const { data: alert } = await supabase.from('system_alerts').select('data').eq('id', ctx.match[1]).single();
  if (!alert?.data?.task_id) return edit(ctx, '❌ No task linked.', back('reports'));
  const { data: task } = await supabase.from('tasks').select('requester_id').eq('id', alert.data.task_id).single();
  if (!task) return edit(ctx, '❌ Task not found.', back('reports'));
  
  await supabase.from('app_users').update({ banned: true, trust_score: 0 }).eq('id', task.requester_id);
  await supabase.from('system_alerts').update({ acknowledged: true }).eq('id', ctx.match[1]);
  await edit(ctx, `🚫 User banned + trust score set to 0.`, back('reports'));
});

bot.action(/^dismissrpt_(.+)$/, async (ctx) => {
  await cb(ctx);
  await supabase.from('system_alerts').update({ acknowledged: true }).eq('id', ctx.match[1]);
  await edit(ctx, `✅ Report dismissed.`, back('reports'));
});

bot.action('broadcast', async (ctx) => {
  await cb(ctx);
  await edit(ctx, `📢 <b>Broadcast</b>\n\nSelect audience:`, Markup.inlineKeyboard([
    [Markup.button.callback('👥 All', 'bc_all')],
    [Markup.button.callback('👷 Workers', 'bc_workers'), Markup.button.callback('💼 Requesters', 'bc_requesters')],
    [Markup.button.callback('🔙 BACK', 'menu')],
  ]));
});

bot.action(/^bc_(all|workers|requesters)$/, async (ctx) => {
  await cb(ctx);
  setState(ctx.from.id, { action: 'broadcast', audience: ctx.match[1] });
  await edit(ctx, `📢 Broadcast to ${ctx.match[1]}\n\nType your message:`, back('broadcast'));
});

bot.action(/^credit_(.+)$/, async (ctx) => {
  await cb(ctx);
  setState(ctx.from.id, { action: 'credit_user', userId: ctx.match[1] });
  await edit(ctx, `💰 <b>Credit User</b>\n\nEnter amount in Vaes:`, back('menu'));
});

bot.action(/^debit_(.+)$/, async (ctx) => {
  await cb(ctx);
  setState(ctx.from.id, { action: 'debit_user', userId: ctx.match[1] });
  await edit(ctx, `💸 <b>Debit User</b>\n\nEnter amount in Vaes:`, back('menu'));
});

bot.action(/^ban_(.+)$/, async (ctx) => {
  await cb(ctx);
  setState(ctx.from.id, { action: 'ban_user', userId: ctx.match[1] });
  await edit(ctx, `🔨 <b>Ban User</b>\n\nEnter days:`, back('menu'));
});

bot.action(/^unban_(.+)$/, async (ctx) => {
  await cb(ctx);
  await db.unbanUser(ctx.match[1]);
  const u = await db.getUserById(ctx.match[1]);
  if (u?.telegram_id) notifyUser(u.telegram_id, `✅ <b>Account Restored</b>\n\nYour ban has been lifted.`);
  await edit(ctx, `✅ User unbanned.`, back('menu'));
});

bot.on('text', async (ctx) => {
  const state = getState(ctx.from.id);
  const text = ctx.message.text.trim();
  if (!state.action) return;

  switch (state.action) {
    case 'user_search': {
      const user = await db.searchUser(text);
      clearState(ctx.from.id);
      if (!user) return send(ctx, '❌ User not found.');
      const bal = await db.getBalance(user.id);
      let msg = `👤 <b>${user.first_name}</b>\n\n`;
      msg += `ID: ${user.telegram_id}\nBalance: ${fmt.both(bal.available)}\nTasks: ${user.tasks_completed || 0}\nRep: ${user.reputation || 100}`;
      await send(ctx, msg, Markup.inlineKeyboard([
        [Markup.button.callback('💰 Credit', `credit_${user.id}`), Markup.button.callback('💸 Debit', `debit_${user.id}`)],
        [Markup.button.callback('🔨 Ban', `ban_${user.id}`), Markup.button.callback('✅ Unban', `unban_${user.id}`)],
        [Markup.button.callback('🔙 BACK', 'menu')],
      ]));
      return;
    }
    case 'reply_ticket': {
      await db.replyToTicket(state.ticketId, text);
      if (state.userTgId) notifyUser(state.userTgId, `📬 <b>Support Reply</b>\n\n${text}`);
      clearState(ctx.from.id);
      return send(ctx, `✅ Ticket resolved.`, back('tickets'));
    }
    case 'resolve_dispute': {
      const result = await db.resolveDispute(state.disputeId, state.winner, text);
      clearState(ctx.from.id);
      if (result?.error) return send(ctx, `❌ Resolution failed: ${fmt.esc(result.error)}`, back('disputes'));
      return send(ctx, `✅ Dispute resolved: ${state.winner}\n\n💰 Payments processed atomically.`, back('disputes'));
    }
    case 'broadcast': {
      const users = await db.getAllUsers();
      let targets = users;
      if (state.audience === 'workers') targets = users.filter(u => u.is_worker);
      if (state.audience === 'requesters') targets = users.filter(u => u.is_requester);
      let sent = 0;
      for (const u of targets) {
        if (u.telegram_id && await notifyUser(u.telegram_id, `📢 <b>Announcement</b>\n\n${text}`)) sent++;
      }
      clearState(ctx.from.id);
      return send(ctx, `✅ Sent to ${sent} users.`);
    }
    case 'reject_withdrawal': {
      const { data: wd } = await supabase.from('withdrawals').select('*, user:app_users(id, telegram_id)').eq('id', state.wdId).single();
      if (wd) {
        const { data: bal } = await supabase.from('vae_balances').select('available').eq('user_id', wd.user.id).single();
        await supabase.from('vae_balances').update({ available: (bal?.available || 0) + wd.amount_vae }).eq('user_id', wd.user.id);
        await supabase.from('withdrawals').update({ status: 'failed', failure_reason: text }).eq('id', state.wdId);
        if (wd.user?.telegram_id) notifyUser(wd.user.telegram_id, `❌ <b>Withdrawal Rejected</b>\n\nReason: ${text}\n\nFunds returned.`);
      }
      clearState(ctx.from.id);
      return send(ctx, `❌ Rejected. Funds returned.`, back('withdrawals'));
    }
    case 'credit_user': {
      const amount = parseInt(text, 10);
      if (isNaN(amount) || amount <= 0) return send(ctx, '❌ Invalid amount');
      await db.creditUser(state.userId, amount, 'Admin credit');
      const u = await db.getUserById(state.userId);
      if (u?.telegram_id) notifyUser(u.telegram_id, `🎁 +${fmt.vae(amount)} credited to your account!`);
      clearState(ctx.from.id);
      return send(ctx, `✅ Credited ${fmt.vae(amount)}`, back('menu'));
    }
    case 'debit_user': {
      const amount = parseInt(text, 10);
      if (isNaN(amount) || amount <= 0) return send(ctx, '❌ Invalid amount');
      const success = await db.debitUser(state.userId, amount, 'Admin debit');
      if (!success) { clearState(ctx.from.id); return send(ctx, `❌ Insufficient balance.`, back('menu')); }
      clearState(ctx.from.id);
      return send(ctx, `✅ Debited ${fmt.vae(amount)}`, back('menu'));
    }
    case 'ban_user': {
      const days = parseInt(text, 10);
      if (isNaN(days) || days <= 0) return send(ctx, '❌ Invalid days');
      await db.banUser(state.userId, days);
      const u = await db.getUserById(state.userId);
      if (u?.telegram_id) notifyUser(u.telegram_id, `⛔ Account suspended for ${days} days.`);
      clearState(ctx.from.id);
      return send(ctx, `✅ Banned for ${days} days.`, back('menu'));
    }
  }
});

bot.catch((err, ctx) => { console.error('Admin bot error:', err); });

async function sendAlert(message) {
  for (const adminId of ADMIN_IDS) {
    try { await bot.telegram.sendMessage(adminId, `🚨 ${message}`, { parse_mode: 'HTML' }); } catch {}
  }
}

async function sendDailySummary() {
  try {
    const s = await db.getStats();
    let treasury = null;
    try { treasury = await getTreasuryBalance(); } catch (e) { console.error('[Daily] Treasury check failed:', e.message); }
    let msg = `📊 <b>Daily Summary</b>\n━━━━━━━━━━━━━━━━━━━━\n\n`;
    msg += `👥 +${s.todayUsers || 0} users (${s.totalUsers} real${s.aiAgents ? ` + ${s.aiAgents} AI` : ''})\n`;
    msg += `   Workers: ${s.totalWorkers} | Requesters: ${s.totalRequesters} | WA: ${s.whatsappUsers}\n`;
    msg += `📋 ${s.openTasks || 0} open tasks / ${s.totalTasks} total\n`;
    msg += `💰 Revenue: ${fmt.both(s.platformRevenue)}\n`;
    msg += `💎 Deposits: $${s.totalDepositsUsd?.toFixed(2) || '0.00'}\n`;
    if (treasury) msg += `🏦 Treasury: $${treasury.totalUsd.toFixed(2)}\n`;
    msg += `\n⚠️ ${s.pendingWithdrawals || 0} WD | ${s.pendingDeposits || 0} deposits | ${s.openDisputes || 0} disputes | ${s.openTickets || 0} tickets`;
    for (const adminId of ADMIN_IDS) {
      try { await bot.telegram.sendMessage(adminId, msg, { parse_mode: 'HTML' }); } catch {}
    }
  } catch (err) {
    console.error('[Daily Summary] Failed:', err.message);
    for (const adminId of ADMIN_IDS) {
      try { await bot.telegram.sendMessage(adminId, `⚠️ Daily summary failed: ${err.message}`, { parse_mode: 'HTML' }); } catch {}
    }
  }
}

async function launch() {
  console.log('🔐 Loopuman Admin Bot starting...');
  try {
    await bot.launch();
    console.log('🔐 Admin Bot started successfully!');
  } catch (err) {
    console.error('❌ Admin Bot failed to start:', err.message);
    process.exit(1);
  }
}

// Only register signal handlers when running as main process
// (prevents crash when scheduler requires this module)
if (require.main === module) {
  process.once('SIGINT', () => bot.stop('SIGINT'));
  process.once('SIGTERM', () => bot.stop('SIGTERM'));
}

module.exports = { bot, launch, sendAlert, sendDailySummary };
if (require.main === module) launch();
