// ═══════════════════════════════════════════════════════════════════════════════
// LOOPUMAN WHATSAPP BOT v7.0 — COMPLETE FIX
// ═══════════════════════════════════════════════════════════════════════════════
// FIXES:
// - Syntax errors (getBalance/addBalance nesting, console.log)
// - New user registration (referral_code generation)
// - Draft preservation (no more lost posts!)
// - Workers capped at 2
// - Correct cost display (budget + 20%)
// - Templates merge with existing draft
// - "Other" category always visible
// - Cancel buttons during text input
// - Confirmation before clearing draft
// ═══════════════════════════════════════════════════════════════════════════════

require('dotenv').config();
const express = require('express');
const crypto = require('crypto');
const { createClient } = require('@supabase/supabase-js');

const stripeService = require('./services/stripe-service');
const cryptoService = require('./services/crypto-service');
const app = express();

// ═══ STRIPE WEBHOOK: Must be BEFORE express.json() to preserve raw body ═══
app.use('/webhook', stripeService.webhookRouter);

app.use(express.json());

// ═══════════════════════════════════════════════════════════════════════════════
// CONFIGURATION
// ═══════════════════════════════════════════════════════════════════════════════
const CONFIG = {
  PORT: process.env.WHATSAPP_PORT || process.env.PORT || 3000,
  VERIFY_TOKEN: process.env.WHATSAPP_VERIFY_TOKEN || 'loopuman_verify_token',
  ACCESS_TOKEN: process.env.WA_ACCESS_TOKEN,
  PHONE_ID: process.env.WA_PHONE_NUMBER_ID,
  TELEGRAM_BOT: 'https://t.me/Loopuman_bot',

  // Financial
  REQUESTER_FEE: 0.20, // Requester pays +20%
  WORKER_FEE: 0.20,    // Worker gets -20%
  MIN_TASK_BUDGET: 50, // Ⓥ50 minimum ($0.50)
  VAE_RATE: 100,       // 100 VAE = $1

  // Task defaults
  DEFAULT_WORKERS: 2,
  DEFAULT_DURATION: 7, // days
  MAX_WORKERS: 2,      // Cap at 2 for sample tasks
  // Smart budget suggestions per category
  SUGGESTED_BUDGETS: {
    labeling: 200, writing: 150, research: 200, verification: 100,
    survey: 75, ai_training: 150, translation: 150, moderation: 150,
    transcription: 300, micro: 100, local: 500, mystery: 800,
    photo: 150, social: 75, data_entry: 100, other: 100,
  },
};

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

// User state for multi-step flows
const userState = new Map();
const waPaginationState = new Map(); // For list pagination

// ═══════════════════════════════════════════════════════════════════════════════
// STARTUP
// ═══════════════════════════════════════════════════════════════════════════════
console.log('╔═══════════════════════════════════════════════════════════════╗');
console.log('║  LOOPUMAN WHATSAPP BOT v7.0 — ALL BUGS FIXED                 ║');
console.log(`║  Port: ${CONFIG.PORT}                                                  ║`);
console.log('╚═══════════════════════════════════════════════════════════════╝');

// ═══════════════════════════════════════════════════════════════════════════════
// UTILITY FUNCTIONS
// ═══════════════════════════════════════════════════════════════════════════════
const fmt = (vae) => `Ⓥ${Number(vae || 0).toLocaleString()}`;
const fmtUsd = (vae) => `$${(Number(vae || 0) / CONFIG.VAE_RATE).toFixed(2)}`;
const fmtBoth = (vae) => `${fmt(vae)} (${fmtUsd(vae)})`;
const shortId = (id) => id ? id.slice(-6).toUpperCase() : '';
const short = (str, len = 30) => str?.length > len ? str.slice(0, len - 3) + '...' : str;
const esc = (str) => String(str || '').replace(/[*_`]/g, '');

// ═══ CONTENT MODERATION ═══
const BLOCKED_PATTERNS = [
  /\b(drug|cocaine|heroin|meth|fentanyl|mdma|ecstasy)\s*(deal|sell|buy|traffi|smuggl|distribut)/i,
  /\b(sell|buy|traffi)\s*(drug|narcotic|controlled substance|weapon|firearm|gun|explosive)/i,
  /\b(money\s*launder|counterfeit|fraud\s*scheme|ponzi|pyramid\s*scheme)/i,
  /\b(hack|ddos|phish|malware|ransomware|exploit|breach)\s*(account|website|server|system|bank)/i,
  /\b(steal|rob|burglar|shoplift|loot|theft)\b/i,
  /\b(fake\s*(id|passport|license|document|diploma|certificate))/i,
  /\b(child|minor|underage)\s*(porn|sex|nude|exploit|abuse|traffi)/i,
  /\b(human\s*traffi|sex\s*traffi|force\s*(labor|labour|prostitut))/i,
  /\b(hitman|assassin|kill\s*for\s*(hire|money)|contract\s*kill)/i,
  /\b(bomb|explosive)\s*(make|build|assembl|instruct)/i,
  /\b(dox|doxx|stalk|harass|threaten|bully|intimidat)\s*(someone|person|target|victim)/i,
  /\b(revenge\s*porn|non.?consensual\s*(intimate|nude|sex))/i,
  /\b(advance\s*fee|nigerian\s*prince|send\s*money\s*(first|upfront|before))/i,
  /\b(guaranteed\s*(profit|income|return)|get\s*rich\s*quick|100%\s*profit)/i,
  /\b(bank\s*account|credit\s*card|ssn|social\s*security)\s*(number|detail|info).*(send|share|provide)/i,
];
const BLOCKED_KEYWORDS = [
  'child porn', 'cp links', 'jailbait', 'pedo',
  'hire hitman', 'kill someone', 'murder for hire',
  'buy guns illegally', 'illegal weapons', 'ghost gun',
  'credit card dumps', 'stolen cards', 'carding',
  'ransomware', 'malware kit', 'exploit kit',
  'human trafficking', 'sex trafficking', 'organ trafficking',
  'terrorist', 'jihad recruit', 'extremist recruit',
];
function screenContent(text) {
  if (!text) return { blocked: false };
  const lower = text.toLowerCase();
  for (const kw of BLOCKED_KEYWORDS) {
    if (lower.includes(kw)) return { blocked: true, reason: 'Content violates platform guidelines', category: 'blocked_keyword' };
  }
  for (const pattern of BLOCKED_PATTERNS) {
    if (pattern.test(text)) return { blocked: true, reason: 'Content violates platform guidelines', category: 'blocked_pattern' };
  }
  return { blocked: false };
}

async function reportTask(taskId, reporterId, reason, details = '') {
  try {
    await supabase.from('system_alerts').insert({
      type: 'task_report',
      severity: 'high',
      message: `Task reported: ${reason}`,
      data: { task_id: taskId, reporter_id: reporterId, reason, details, reported_at: new Date().toISOString() },
    });
    // Real-time admin notification via Telegram
    const adminIds = (process.env.ADMIN_TELEGRAM_IDS || '').split(',').filter(Boolean);
    const tgToken = process.env.TELEGRAM_BOT_TOKEN;
    if (tgToken) {
      for (const adminId of adminIds) {
        try {
          await fetch(`https://api.telegram.org/bot${tgToken}/sendMessage`, {
            method: 'POST', headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ chat_id: adminId, parse_mode: 'HTML',
              text: `🚩 <b>WA TASK REPORT</b>\n\nReason: ${reason}\nDetails: ${(details || 'None').slice(0, 200)}\nTask: ${taskId ? taskId.slice(0, 8) + '…' : 'auto-blocked'}\n\n<i>Open Admin Bot → 🚩 Reports to review.</i>` }),
          });
        } catch {}
      }
    }
    console.log(`[REPORT] Task ${taskId} reported by ${reporterId}: ${reason}`);
    // Auto-escalate: pause task after 3+ reports
    if (taskId) {
      const { count } = await supabase.from('system_alerts').select('*', { count: 'exact', head: true })
        .eq('type', 'task_report').filter('data->>task_id', 'eq', taskId);
      if ((count || 0) >= 3) {
        await supabase.from('tasks').update({ status: 'paused' }).eq('id', taskId).in('status', ['open', 'active']);
        console.log(`[AUTO-ESCALATE] Task ${taskId} paused after ${count} reports`);
      }
    }
  } catch (e) {
    console.error('[Report] Error:', e.message);
  }
}
// ═══ END CONTENT MODERATION ═══

// Generate unique referral code
function generateReferralCode() {
  return 'WA' + crypto.randomBytes(4).toString('hex').toUpperCase();
}

// ═══════════════════════════════════════════════════════════════════════════════
// WHATSAPP API FUNCTIONS
// ═══════════════════════════════════════════════════════════════════════════════
async function sendText(to, text) {
  console.log(`[${new Date().toISOString()}] sendText to ${to}`);
  try {
    const response = await fetch(`https://graph.facebook.com/v18.0/${CONFIG.PHONE_ID}/messages`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${CONFIG.ACCESS_TOKEN}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        messaging_product: 'whatsapp',
        to,
        type: 'text',
        text: { body: text }
      }),
    });
    const data = await response.json();
    if (data.error) console.error('WhatsApp Error:', data.error);
    return data;
  } catch (e) {
    console.error('Send error:', e.message);
    return null;
  }
}

async function sendButtons(to, text, buttons) {
  console.log(`[${new Date().toISOString()}] sendButtons to ${to}`);
  try {
    const response = await fetch(`https://graph.facebook.com/v18.0/${CONFIG.PHONE_ID}/messages`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${CONFIG.ACCESS_TOKEN}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        messaging_product: 'whatsapp',
        to,
        type: 'interactive',
        interactive: {
          type: 'button',
          body: { text },
          action: {
            buttons: buttons.slice(0, 3).map(b => ({
              type: 'reply',
              reply: { id: b.id, title: b.title.slice(0, 20) }
            }))
          }
        },
      }),
    });
    const data = await response.json();
    if (data.error) console.error('WhatsApp Error:', data.error);
    return data;
  } catch (e) {
    console.error('Buttons error:', e.message);
    return null;
  }
}

async function sendImage(to, imageUrl, caption = '') {
  console.log(`[${new Date().toISOString()}] sendImage to ${to}`);
  try {
    const response = await fetch(`https://graph.facebook.com/v18.0/${CONFIG.PHONE_ID}/messages`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${CONFIG.ACCESS_TOKEN}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        messaging_product: 'whatsapp',
        to,
        type: 'image',
        image: { link: imageUrl, caption }
      }),
    });
    const data = await response.json();
    if (data.error) console.error('WhatsApp Image Error:', data.error);
    return data;
  } catch (e) {
    console.error('Send image error:', e.message);
    return null;
  }
}

async function notifyTelegram(telegramId, message) {
  if (!telegramId || !process.env.TELEGRAM_BOT_TOKEN) return false;
  try {
    const axios = require('axios');
    await axios.post(`https://api.telegram.org/bot${process.env.TELEGRAM_BOT_TOKEN}/sendMessage`, {
      chat_id: telegramId,
      text: message,
      parse_mode: 'HTML'
    });
    return true;
  } catch (e) {
    console.error('Telegram notify error:', e.message);
    return false;
  }
}

async function sendDocument(to, docUrl, caption = '', filename = 'attachment') {
  console.log(`[${new Date().toISOString()}] sendDocument to ${to}`);
  try {
    const response = await fetch(`https://graph.facebook.com/v18.0/${CONFIG.PHONE_ID}/messages`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${CONFIG.ACCESS_TOKEN}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        messaging_product: 'whatsapp',
        to,
        type: 'document',
        document: { link: docUrl, caption, filename }
      }),
    });
    const data = await response.json();
    if (data.error) console.error('WhatsApp Doc Error:', data.error);
    return data;
  } catch (e) {
    console.error('Send doc error:', e.message);
    return null;
  }
}

async function sendList(to, text, buttonText, sections) {
  console.log(`[${new Date().toISOString()}] sendList to ${to}`);
  
  // Defensive: validate inputs
  if (!sections || !Array.isArray(sections) || sections.length === 0) {
    console.error('sendList error: sections is invalid');
    return sendText(to, text); // Fallback to text message
  }
  
  try {
    // Filter out invalid sections
    const validSections = sections
      .filter(s => s && Array.isArray(s.rows) && s.rows.length > 0)
      .map(s => ({
        title: (s.title || 'Options').slice(0, 24),
        rows: s.rows.filter(r => r && r.id).map(r => ({
          id: String(r.id).slice(0, 200),
          title: (r.title || 'Option').slice(0, 24),
          description: r.desc ? r.desc.slice(0, 72) : undefined,
        })),
      }))
      .filter(s => s.rows.length > 0);
    
    if (validSections.length === 0) {
      console.error('sendList error: no valid sections after filtering');
      return sendText(to, text);
    }
    
    const response = await fetch(`https://graph.facebook.com/v18.0/${CONFIG.PHONE_ID}/messages`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${CONFIG.ACCESS_TOKEN}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        messaging_product: 'whatsapp',
        to,
        type: 'interactive',
        interactive: {
          type: 'list',
          body: { text: text.slice(0, 1024) },
          action: {
            button: (buttonText || 'Menu').slice(0, 20),
            sections: validSections,
          },
        },
      }),
    });
    const data = await response.json();
    if (data.error) console.error('WhatsApp Error:', data.error);
    return data;
  } catch (e) {
    console.error('List error:', e.message);
    return null;
  }
}

// ═══════════════════════════════════════════════════════════════════════════════
// DATABASE FUNCTIONS
// ═══════════════════════════════════════════════════════════════════════════════
async function getOrCreateUser(phone, name = 'User') {
  console.log(`[${new Date().toISOString()}] getOrCreateUser: ${phone}`);

  // Check existing
  const { data: existing } = await supabase
    .from('app_users')
    .select('*')
    .eq('phone', phone)
    .single();

  if (existing) {
    if (!existing.first_name || existing.first_name === "User" || existing.first_name === "Requester") {
      await supabase.from("app_users").update({ first_name: name }).eq("id", existing.id);
      existing.first_name = name;
    }
    console.log(`User found: ${existing.id}`);
    return existing;
  }

  // Generate unique referral code
  const referralCode = generateReferralCode();

  // Create new user with referral_code
  const { data: newUser, error } = await supabase
    .from('app_users')
    .insert({
      phone,
      first_name: name,
      is_requester: true,
      is_worker: false,
      platform: 'whatsapp',
      referral_code: referralCode,
    })
    .select()
    .single();

  if (error) {
    console.error('Create user error:', error);
    return null;
  }

  // Create balance record
  await supabase.from('vae_balances').insert({
    user_id: newUser.id,
    total_earned: 0,
    available: 0,
  });

  console.log(`New user created: ${newUser.id} with referral code: ${referralCode}`);
  return newUser;
}

async function getBalance(userId) {
  const { data } = await supabase
    .from('vae_balances')
    .select('total_earned, available')
    .eq('user_id', userId)
    .single();
  return {
    earned: data?.total_earned || 0,
    deposit: data?.available || 0,
    total: (data?.total_earned || 0) + (data?.available || 0),
  };
}

async function addBalance(userId, amount, type, description) {
  const isRefund = type === 'refund';
  // ATOMIC: Use RPC instead of read-then-write
  const { error } = await supabase.rpc('increment_balance', {
    p_user_id: userId,
    p_amount: amount,
    p_balance_type: isRefund ? 'available_only' : 'earned',
  });
  
  if (error) {
    console.warn(`[WA addBalance] RPC failed, using fallback: ${error.message}`);
    const { data: current } = await supabase
      .from('vae_balances')
      .select('available')
      .eq('user_id', userId)
      .single();
    const newBalance = (current?.available || 0) + amount;
    await supabase.from('vae_balances').update({ available: newBalance, updated_at: new Date().toISOString() }).eq('user_id', userId);
  }

  // Record transaction
  await supabase.from('transactions').insert({
    user_id: userId,
    type: type,
    amount: amount,
    description: description
  });
}

// ═══════════════════════════════════════════════════════════════════════════════
// CATEGORIES (11 total)
// ═══════════════════════════════════════════════════════════════════════════════
const CATEGORIES = {
  survey: { name: 'Survey', icon: '📋' },
  photo: { name: 'Photo', icon: '📷' },
  writing: { name: 'Writing', icon: '✍️' },
  translation: { name: 'Translation', icon: '🌍' },
  research: { name: 'Research', icon: '🔍' },
  data_entry: { name: 'Data Entry', icon: '📊' },
  social: { name: 'Social Media', icon: '📱' },
  audio: { name: 'Audio', icon: '🎤' },
  video: { name: 'Video', icon: '🎥' },
  testing: { name: 'App Testing', icon: '🧪' },
  other: { name: 'Other', icon: '📦' },
};

// ═══════════════════════════════════════════════════════════════════════════════
// TASK TEMPLATES (showing 9 most common + back button)
// ═══════════════════════════════════════════════════════════════════════════════
const TEMPLATES = [
  { id: 'survey', title: '📋 Quick Survey', desc: '5-10 questions', budget: 50, category: 'survey' },
  { id: 'photo', title: '📷 Photo Task', desc: 'Take specific photos', budget: 100, category: 'photo' },
  { id: 'review', title: '⭐ Product Review', desc: 'Write honest review', budget: 150, category: 'writing' },
  { id: 'translate', title: '🌍 Translation', desc: 'Translate text', budget: 200, category: 'translation' },
  { id: 'social', title: '📱 Social Media', desc: 'Follow/like/share', budget: 50, category: 'social' },
  { id: 'research', title: '🔍 Research', desc: 'Find information', budget: 150, category: 'research' },
  { id: 'writing', title: '✍️ Content Writing', desc: 'Write articles/posts', budget: 250, category: 'writing' },
  { id: 'data', title: '📊 Data Entry', desc: 'Input/verify data', budget: 100, category: 'data_entry' },
  { id: 'other', title: '📝 Custom Task', desc: 'Define your own', budget: 100, category: 'other' },
];

// ═══════════════════════════════════════════════════════════════════════════════
// MAIN MENU
// ═══════════════════════════════════════════════════════════════════════════════
async function showMainMenu(phone, user, message = '') {
  const bal = await getBalance(user.id);

  // Count pending reviews
  const { count: pendingCount } = await supabase
    .from('submissions')
    .select('*', { count: 'exact', head: true })
    .eq('requester_id', user.id)
    .eq('status', 'submitted');

  // Count active tasks
  const { count: activeCount } = await supabase
    .from('tasks')
    .select('*', { count: 'exact', head: true })
    .eq('requester_id', user.id)
    .eq('status', 'open');

  const pendingBadge = pendingCount > 0 ? ` (${pendingCount} new!)` : '';

  await sendList(phone,
    (message ? `${message}\n\n` : '') +
    `🏠 *LOOPUMAN*\n` +
    `━━━━━━━━━━━━━━━━\n\n` +
    `👋 Welcome to Loopuman, ${esc(user.first_name)}!\n\n` +
    `💰 *Balance:* ${fmtBoth(bal.deposit)}\n` +
    `📋 *Active Tasks:* ${activeCount || 0}\n` +
    `📬 *Pending Reviews:* ${pendingCount || 0}\n\n` +
    `_Post tasks to get work done fast!_\n` +
    `_Workers are paid on Telegram._`,
    '📋 Menu',
    [{
      title: 'Main Menu',
      rows: [
        { id: 'post_task', title: '➕ Post Task', desc: 'Create a new task' },
        { id: 'templates', title: '⚡ Quick Templates', desc: '9 ready-to-use templates' },
        { id: 'my_tasks', title: '📊 My Tasks', desc: `${activeCount || 0} active tasks` },
        { id: 'reviews', title: `📬 Review Work${pendingBadge}`, desc: 'Approve submissions' },
        { id: 'wallet', title: '💰 Wallet', desc: `Balance: ${fmt(bal.deposit)}` },
        { id: 'deposit', title: '💳 Add Funds', desc: 'Deposit to your account' },
        { id: 'help', title: '❓ Help', desc: 'FAQ and support' },
      ]
    }]
  );
}

// ═══════════════════════════════════════════════════════════════════════════════
// ═══════════════════════════════════════════════════════════════════════════════
//                    HUB-BASED TASK POSTING (THE KEY FEATURE)
// ═══════════════════════════════════════════════════════════════════════════════
// ═══════════════════════════════════════════════════════════════════════════════

function initTaskDraft() {
  return {
    action: 'task_hub',
    title: null,
    description: null,
    category: 'other',
    budget: 100,
    maxWorkers: CONFIG.DEFAULT_WORKERS,
    durationDays: CONFIG.DEFAULT_DURATION,
    isCompetition: true,
    editing: null,
    media: [],
  };
}

// Get or preserve existing draft
async function getOrCreateDraft(phone, userId = null) {
  let state = userState.get(phone);
  // Check DB for persisted draft first
  if (userId) {
    const dbDraft = await getDraft(userId);
    if (dbDraft && !userState.get(phone)) {
      console.log(`[Draft] Restored from DB for ${userId.slice(0,8)}`);
      const state = {
        action: "task_hub",
        title: dbDraft.title || null,
        description: dbDraft.description || null,
        category: dbDraft.category || "other",
        budget: dbDraft.budget || 100,
        maxWorkers: dbDraft.maxWorkers || CONFIG.DEFAULT_WORKERS,
        durationDays: dbDraft.durationDays || CONFIG.DEFAULT_DURATION,
        isCompetition: dbDraft.isCompetition !== false,
        editing: null,
        media: dbDraft.media || [],
        restored: true,
        saved_at: dbDraft.saved_at
      };
      userState.set(phone, state);
      return state;
    }
  }
  if (!state || state.action !== 'task_hub') {
    state = initTaskDraft();
    userState.set(phone, state);
  }
  return state;
}

async function showTaskHub(phone, user, message = '') {
  const state = await getOrCreateDraft(phone, user.id);
  state.editing = null; // Clear any editing state

  const cat = CATEGORIES[state.category] || CATEGORIES.other;
  const workerPay = Math.floor(state.budget * (1 - CONFIG.WORKER_FEE));
  // FIXED: Show correct cost with 20% fee
  const totalCost = state.budget;
  const bal = await getBalance(user.id);

  // Check if ready to post (title and description set)
  const titleStatus = state.title ? `✅ "${short(state.title, 25)}"` : '❌ Not set';
  const descStatus = state.description ? `✅ "${state.description.slice(0, 50)}${state.description.length > 50 ? "..." : ""}"` : `❌ Not set`;
  const canPost = state.title && state.description;
  const canAfford = bal.deposit >= totalCost;

  let msg = '';
  // Show restored message if draft was loaded from DB
  if (state.restored && !message) {
    const age = formatDraftAge(state.saved_at);
    msg += `📋 *Draft restored!* (saved ${age})\n\n`;
    state.restored = false; // Only show once
  }
  if (message) msg += `${message}\n\n`;

  msg += `📝 *POST A TASK*\n`;
  msg += `━━━━━━━━━━━━━━━━\n\n`;

  msg += `📌 *Title:* ${titleStatus}\n`;
  msg += `📄 *Description:* ${descStatus}\n`;
  msg += `🏷️ *Category:* ${cat.icon} ${cat.name}\n`;
  msg += `💰 *Budget:* ${fmtBoth(state.budget)}\n`;
  msg += `👥 *Workers:* ${state.maxWorkers}${state.isCompetition ? ' (Competition)' : ''}\n`;
  msg += `⏰ *Duration:* ${state.durationDays} days\n`;
  if (state.media?.length > 0) {
    msg += `📎 *Media:* ${state.media.length} file(s) attached\n`;
  }
  msg += `\n`;

  msg += `━━━━━━━━━━━━━━━━\n`;
  msg += `💰 *Total Cost:* ${fmtBoth(totalCost)}\n`;
  msg += `💰 *Your balance:* ${fmtBoth(bal.deposit)}`;
  if (!canAfford) msg += ` ⚠️ Need more!`;
  msg += `\n\n`;

  if (!canPost) {
    msg += `_Set title and description to continue._`;
  } else if (!canAfford) {
    msg += `_Add funds to post this task._`;
  } else {
    msg += `✅ _Ready to post!_`;
  }

  await sendList(phone, msg, '✏️ Edit Fields', [{
    title: 'Edit Task',
    rows: [
      { id: 'edit_title', title: '📌 Set Title', desc: state.title ? 'Edit title' : 'Required!' },
      { id: 'edit_desc', title: '📄 Set Description', desc: state.description ? 'Edit description' : 'Required!' },
      { id: 'edit_category', title: '🏷️ Category', desc: `Current: ${cat.name}` },
      { id: 'edit_budget', title: '💰 Budget', desc: `Current: ${fmt(state.budget)}` },
      { id: 'edit_workers', title: '👥 Workers', desc: `Current: ${state.maxWorkers}` },
      { id: 'edit_duration', title: '⏰ Duration', desc: `Current: ${state.durationDays} days` },
      { id: 'hub_media', title: '📎 Add Media', desc: `${state.media?.length || 0} file(s) attached` },
      { id: 'hub_preview', title: '👁️ Preview & Post', desc: canPost ? 'Review and post' : 'Set title & desc first' },
      { id: 'hub_fresh_confirm', title: '🔴 Start Fresh', desc: 'Clear all fields' },
      { id: 'menu', title: '🔙 BACK', desc: 'Return to menu (draft saved)' },
    ],
  }]);
}

async function showEditTitle(phone, user) {
  const state = await getOrCreateDraft(phone, user.id);
  state.editing = 'title';
  userState.set(phone, state);

  await sendText(phone,
    `📌 *SET TASK TITLE*\n` +
    `━━━━━━━━━━━━━━━━\n\n` +
    `What's the task called?\n\n` +
    `Examples:\n` +
    `• "Write a product description"\n` +
    `• "Take 5 photos of your local store"\n` +
    `• "Translate menu to Spanish"\n\n` +
    `_Type your title now:_`
  );
  // Add cancel button
  await sendButtons(phone, 'Or cancel:', [
    { id: 'back_hub', title: '❌ Cancel' },
  ]);
}

async function showEditDescription(phone, user) {
  const state = await getOrCreateDraft(phone, user.id);
  state.editing = 'description';
  userState.set(phone, state);

  await sendText(phone,
    `📄 *SET TASK DESCRIPTION*\n` +
    `━━━━━━━━━━━━━━━━\n\n` +
    `Describe the task in detail:\n\n` +
    `• What should the worker do?\n` +
    `• Any specific requirements?\n` +
    `• Expected format/length?\n\n` +
    `Example:\n` +
    `"Write a 100-word product description for a wireless Bluetooth speaker. Include: key features, benefits, and a call to action. Tone: professional but friendly."\n\n` +
    `_Type your description now:_`
  );
  // Add cancel button
  await sendButtons(phone, 'Or cancel:', [
    { id: 'back_hub', title: '❌ Cancel' },
  ]);
}

async function showEditCategory(phone, user) {
  const state = await getOrCreateDraft(phone, user.id);

  // Build category rows - WhatsApp max 10, we have 11 categories
  // Prioritize showing "other" by putting common ones first + other
  const priorityOrder = ['survey', 'photo', 'writing', 'translation', 'research', 'data_entry', 'social', 'other'];
  const rows = priorityOrder.slice(0, 8).map(id => {
    const cat = CATEGORIES[id];
    return {
      id: `cat_${id}`,
      title: `${cat.icon} ${cat.name}`,
      desc: id === state.category ? '✓ Current' : '',
    };
  });

  // Add back button
  rows.push({ id: 'back_hub', title: '🔙 BACK', desc: 'Return to task' });

  await sendList(phone,
    `🏷️ *SELECT CATEGORY*\n` +
    `━━━━━━━━━━━━━━━━\n\n` +
    `Choose the best category for your task:\n\n` +
    `_Use "Other" for custom task types._`,
    '📁 Categories',
    [{ title: 'Categories', rows }]
  );
}

async function showEditBudget(phone, user) {
  const state = await getOrCreateDraft(phone, user.id);
  await sendList(phone,
    (() => {
      const suggested = CONFIG.SUGGESTED_BUDGETS[state.category] || 100;
      return `💰 *SET BUDGET*\n` +
        `━━━━━━━━━━━━━━━━\n\n` +
        `💡 *Suggested:* ${fmtBoth(suggested)} for ${state.category || 'this category'}\n` +
        `📊 _Tasks at suggested budget fill 3x faster_\n\n` +
        `Current: ${fmtBoth(state.budget)}\n` +
        `Minimum: ${fmtBoth(CONFIG.MIN_TASK_BUDGET)}\n\n` +
        `Select a quick amount or type custom:`;
    })(),
    '💰 Budget',
    [{
      title: 'Quick Amounts',
      rows: [
        { id: 'budget_50', title: `${fmt(50)} ($0.50)`, desc: 'Minimum' },
        { id: 'budget_100', title: `${fmt(100)} ($1.00)`, desc: 'Standard' },
        { id: 'budget_200', title: `${fmt(200)} ($2.00)`, desc: 'Good' },
        { id: 'budget_500', title: `${fmt(500)} ($5.00)`, desc: 'Premium' },
        { id: 'budget_1000', title: `${fmt(1000)} ($10.00)`, desc: 'High value' },
        { id: 'budget_custom', title: '✏️ Custom Amount', desc: 'Type any amount' },
        { id: 'back_hub', title: '🔙 BACK', desc: 'Return to task' },
      ],
    }]
  );
}

async function showEditWorkers(phone, user) {
  // FIXED: Cap workers at 2
  await sendList(phone,
    `👥 *SET NUMBER OF WORKERS*\n` +
    `━━━━━━━━━━━━━━━━\n\n` +
    `• 1 worker = First submission wins\n` +
    `• 2 workers = Competition mode (pick best)\n\n` +
    `Competition gives you more choice!\nSame cost either way.`,
    '👥 Workers',
    [{
      title: 'Worker Count',
      rows: [
        { id: 'workers_1', title: '1 Worker', desc: 'First submission wins' },
        { id: 'workers_2', title: '2 Workers', desc: 'Competition: Pick best' },
        { id: 'back_hub', title: '🔙 BACK', desc: 'Return to task' },
      ],
    }]
  );
}

async function showEditDuration(phone, user) {
  await sendList(phone,
    `⏰ *SET TASK DURATION*\n` +
    `━━━━━━━━━━━━━━━━\n\n` +
    `How long should workers have to complete this?`,
    '⏰ Duration',
    [{
      title: 'Duration',
      rows: [
        { id: 'duration_1', title: '1 Day', desc: 'Urgent tasks' },
        { id: 'duration_3', title: '3 Days', desc: 'Quick turnaround' },
        { id: 'duration_7', title: '7 Days', desc: 'Standard (recommended)' },
        { id: 'duration_14', title: '14 Days', desc: 'Flexible deadline' },
        { id: 'duration_30', title: '30 Days', desc: 'No rush' },
        { id: 'back_hub', title: '🔙 BACK', desc: 'Return to task' },
      ],
    }]
  );
}

// Confirmation before clearing draft
async function showClearConfirmation(phone) {
  await sendButtons(phone,
    `🔴 *CLEAR DRAFT?*\n\n` +
    `This will erase all your task details.\n\n` +
    `Are you sure?`,
    [
      { id: 'hub_fresh', title: '🔴 Yes, Clear' },
      { id: 'back_hub', title: '❌ No, Keep It' },
    ]
  );
}

async function showPreview(phone, user) {
  const state = await getOrCreateDraft(phone, user.id);
  if (!state || !state.title || !state.description) {
    return showTaskHub(phone, user, '⚠️ Please set title and description first.');
  }

  const cat = CATEGORIES[state.category] || CATEGORIES.other;
  const workerPay = Math.floor(state.budget * (1 - CONFIG.WORKER_FEE));
  const totalCost = state.budget;
  const bal = await getBalance(user.id);

  if (bal.deposit < totalCost) {
    return sendButtons(phone,
      `⚠️ *INSUFFICIENT BALANCE*\n` +
      `━━━━━━━━━━━━━━━━\n\n` +
      `Task cost: ${fmtBoth(totalCost)}\n` +
      `Your balance: ${fmtBoth(bal.deposit)}\n` +
      `Need: ${fmtBoth(totalCost - bal.deposit)} more\n\n` +
      `Add funds to continue.`,
      [
        { id: 'deposit', title: '💳 Add Funds' },
        { id: 'back_hub', title: '🔙 BACK' },
      ]
    );
  }

  await sendButtons(phone,
    `👁️ *PREVIEW — Ready to post?*\n` +
    `━━━━━━━━━━━━━━━━\n\n` +
    `📌 *Title:*\n${state.title}\n\n` +
    `📄 *Description:*\n${state.description.slice(0, 300)}${state.description.length > 300 ? '...' : ''}\n\n` +
    `🏷️ *Category:* ${cat.icon} ${cat.name}\n` +
    `💰 *Budget:* ${fmtBoth(state.budget)}\n` +
    `👥 *Workers:* ${state.maxWorkers}\n` +
    `⏰ *Duration:* ${state.durationDays} days\n\n` +
    `━━━━━━━━━━━━━━━━\n` +
    `💰 Total: ${fmtBoth(totalCost)}\n` +
    `💰 After posting: ${fmtBoth(bal.deposit - totalCost)}`,
    [
      { id: 'confirm_post', title: '✅ Post Task!' },
      { id: 'back_hub', title: '✏️ Edit' },
    ]
  );
}

async function postTask(phone, user) {
  const state = await getOrCreateDraft(phone, user.id);
  if (!state || !state.title || !state.description) {
    return showTaskHub(phone, user, '⚠️ Please set title and description first.');
  }

  // ═══ CONTENT MODERATION: Screen BEFORE taking money ═══
  const titleScreen = screenContent(state.title);
  const descScreen = screenContent(state.description);
  if (titleScreen.blocked || descScreen.blocked) {
    await reportTask(null, user.id, 'auto_blocked', `WA blocked. Title: "${state.title}", Desc: "${(state.description || '').slice(0, 200)}"`);
    return sendText(phone, '🚫 *Task Blocked*\n\nYour task was flagged for violating platform guidelines.\n\nLoopuman does not allow illegal, harmful, or inappropriate content.\n\nIf this is an error, contact support.');
  }
  // ═══ END CONTENT MODERATION ═══

  const totalCost = state.budget;
  const bal = await getBalance(user.id);

  if (bal.deposit < totalCost) {
    return showTaskHub(phone, user, '⚠️ Insufficient balance. Please add funds.');
  }

  // Deduct balance (atomic)
  const { data: deducted, error: deductErr } = await supabase.rpc('safe_deduct_balance', {
    p_user_id: user.id,
    p_amount: totalCost,
  });
  if (deductErr || !deducted) {
    // Fallback or insufficient
    if (deductErr) console.warn(`[WA] safe_deduct RPC failed: ${deductErr.message}`);
    const freshBal = await getBalance(user.id);
    if (freshBal.deposit < totalCost) {
      return showTaskHub(phone, user, '⚠️ Insufficient balance. Please add funds.');
    }
    await supabase.from('vae_balances').update({ available: freshBal.deposit - totalCost, updated_at: new Date().toISOString() }).eq('user_id', user.id);
  }

  // Create task
  const { data: task, error } = await supabase
    .from('tasks')
    .insert({
      requester_id: user.id,
      title: state.title,
      description: state.description,
      category: state.category,
      budget: state.budget,
      max_workers: state.maxWorkers,
      duration_days: state.durationDays,
      status: 'open',
      source: 'whatsapp',
      reference_media: state.media || [],
    })
    .select()
    .single();

  if (error) {
    console.error('Create task error:', error);
    // Refund balance
    await supabase
      .from('vae_balances')
      .update({ available: bal.deposit })
      .eq('user_id', user.id);
    return sendText(phone, '❌ Error creating task. Please try again.');
  }

  // Record transaction
  await supabase.from('transactions').insert({
    user_id: user.id,
    type: 'task_posted',
    amount: -totalCost,
    description: `Posted task: ${state.title}`,
    task_id: task.id,
  });

  // Clear state only after successful post
  await clearDraft(user.id);
  userState.delete(phone);

  await sendButtons(phone,
    `✅ *TASK POSTED!*\n` +
    `━━━━━━━━━━━━━━━━\n\n` +
    `📋 "${state.title}"\n\n` +
    `Workers on Telegram & WhatsApp will be notified!\n\n` +
    `📬 You'll be notified when someone submits work!\n\n` +
    `💰 Remaining balance: ${fmtBoth(bal.deposit - totalCost)}`,
    [
      { id: 'post_task', title: '➕ Post Another' },
      { id: 'my_tasks', title: '📊 My Tasks' },
      { id: 'menu', title: '🏠 Menu' },
    ]
  );

  // Notify matched workers on both platforms
  try {
    const { Telegraf } = require('telegraf');
    const tgBot = new Telegraf(process.env.TELEGRAM_BOT_TOKEN);

    const { data: allWorkers } = await supabase
      .from('app_users')
      .select('id, telegram_id, phone, first_name, trust_score')
      .neq('id', user.id)
      .gt('trust_score', 0);

    if (allWorkers?.length) {
      const taskCategory = state.category || 'general';
      let notifiedCount = 0;
      const earningsVae = Math.round(state.budget * 0.8);

      for (const worker of allWorkers) {
        if (notifiedCount >= 50) break;

        // Check skill match
        const { data: ws } = await supabase
          .from('worker_skills')
          .select('categories')
          .eq('user_id', worker.id)
          .maybeSingle();

        const cats = ws?.categories || [];
        if (cats.length > 0 && !cats.includes(taskCategory)) continue;

        // Notify via Telegram
        if (worker.telegram_id) {
          const tgMsg = `🔔 <b>New Task Available!</b>\n\n📋 "${state.title}"\n💰 Earn: Ⓥ${earningsVae}\n📁 ${taskCategory}\n⏰ ${state.durationDays} day${state.durationDays > 1 ? 's' : ''}\n\nTap Find Work to accept!`;
          tgBot.telegram.sendMessage(worker.telegram_id, tgMsg, { parse_mode: 'HTML' }).catch(() => {});
        }

        // Notify via WhatsApp (if different from requester)
        if (worker.phone && worker.phone !== phone) {
          await sendText(worker.phone, `🔔 *New Task Available!*\n\n📋 "${state.title}"\n💰 Earn: Ⓥ${earningsVae}\n📁 ${taskCategory}\n\nOpen Telegram bot to accept!`).catch(() => {});
        }

        notifiedCount++;
      }
      console.log('[WA-TaskPost] Notified ' + notifiedCount + ' workers for: ' + state.title);
    }
  } catch (e) {
    console.error('[WA-TaskPost] Worker notification error:', e.message);
  }
}

// ═══════════════════════════════════════════════════════════════════════════════
// QUICK TEMPLATES
// ═══════════════════════════════════════════════════════════════════════════════
async function showTemplates(phone) {
  await sendList(phone,
    `⚡ *QUICK TEMPLATES*\n` +
    `━━━━━━━━━━━━━━━━\n\n` +
    `Select a template to pre-fill your task:\n\n` +
    `_Templates set title, category & budget._\n` +
    `_You just add the description!_`,
    `📋 Templates`,
    [{
      title: `Templates`,
      rows: [
        ...TEMPLATES.map(t => ({
          id: `tpl_${t.id}`,
          title: t.title,
          desc: `${t.desc} • ${fmt(t.budget)}`
        })),
        { id: `menu`, title: `🔙 BACK`, desc: `Return to menu` }
      ],
    }]
  );
}

async function applyTemplate(phone, user, templateId) {
  const template = TEMPLATES.find(t => t.id === templateId);
  if (!template) return showTemplates(phone);

  // FIXED: Merge with existing draft instead of replacing
  const state = await getOrCreateDraft(phone, user.id);
  state.title = `${template.title.replace(/[^\w\s]/g, '')} Task`;
  state.budget = template.budget;
  state.category = template.category;
  // Keep existing description if set
  await saveDraft(user.id, state);
  userState.set(phone, state);

  return showTaskHub(phone, user, `✅ Template applied: ${template.title}\n\nNow add a description!`);
}

// ═══════════════════════════════════════════════════════════════════════════════
// MY TASKS
// ═══════════════════════════════════════════════════════════════════════════════
async function showMyTasks(phone, user, taskOffset = 0) {
  const { data: tasks } = await supabase
    .from('tasks')
    .select('id, title, status, budget, created_at')
    .eq('requester_id', user.id)
    .order('created_at', { ascending: false });

  if (!tasks || tasks.length === 0) {
    return sendButtons(phone,
      `📊 *MY TASKS*\n` +
      `━━━━━━━━━━━━━━━━\n\n` +
      `You haven't posted any tasks yet.\n\n` +
      `Post your first task to get work done!`,
      [
        { id: 'post_task', title: '➕ Post Task' },
        { id: 'menu', title: '🏠 Menu' },
      ]
    );
  }

  // Pagination for tasks
  const TASKS_PER_PAGE = 8;
  const totalTaskPages = Math.ceil(tasks.length / TASKS_PER_PAGE);
  const currentTaskPage = Math.floor(taskOffset / TASKS_PER_PAGE) + 1;
  const pageTasks = tasks.slice(taskOffset, taskOffset + TASKS_PER_PAGE);
  
  const taskRows = pageTasks.map(t => {
    const statusIcon = t.status === 'open' ? '🟢' : t.status === 'completed' ? '✅' : '⏸️';
    return {
      id: `task_${t.id}`,
      title: short(t.title, 22),
      desc: `${statusIcon} ${t.status} • ${fmt(t.budget)}`,
    };
  });
  if (taskOffset + TASKS_PER_PAGE < tasks.length) taskRows.push({ id: `tasks_next_${taskOffset + TASKS_PER_PAGE}`, title: 'Next ▶️', desc: `Page ${currentTaskPage + 1}/${totalTaskPages}` });
  if (taskOffset > 0) taskRows.push({ id: `tasks_prev_${taskOffset - TASKS_PER_PAGE}`, title: '◀️ Previous', desc: `Page ${currentTaskPage - 1}/${totalTaskPages}` });
  taskRows.push({ id: 'menu', title: '🔙 BACK', desc: 'Return to menu' });

  await sendList(phone,
    `📊 *MY TASKS* (${tasks.length})\n` +
    `📄 Page ${currentTaskPage}/${totalTaskPages}\n` +
    `━━━━━━━━━━━━━━━━`,
    '📋 Tasks',
    [{
      title: 'Your Tasks',
      rows: taskRows,
    }]
  );
}

async function showTaskDetail(phone, user, taskId) {
  const { data: task } = await supabase
    .from('tasks')
    .select('*')
    .eq('id', taskId)
    .single();

  if (!task) {
    return sendText(phone, '❌ Task not found.');
  }

  const cat = CATEGORIES[task.category] || CATEGORIES.other;
  const statusIcon = task.status === 'open' ? '🟢' : task.status === 'completed' ? '✅' : '⏸️';

  // Count submissions
  // Count all submissions for display
  const { count: subCount } = await supabase
    .from('submissions')
    .select('*', { count: 'exact', head: true })
    .eq('task_id', taskId);

  // Count only active submissions (for cancel button)
  const { count: activeSubCount } = await supabase
    .from('submissions')
    .select('*', { count: 'exact', head: true })
    .eq('task_id', taskId)
    .in('status', ['in_progress', 'submitted']);

  let msg = `📋 *TASK DETAILS*\n`;
  msg += `━━━━━━━━━━━━━━━━\n\n`;
  msg += `📌 *Title:* ${task.title}\n\n`;
  msg += `📄 *Description:*\n${task.description?.slice(0, 200) || 'No description'}${task.description?.length > 200 ? '...' : ''}\n\n`;
  msg += `🏷️ *Category:* ${cat.icon} ${cat.name}\n`;
  msg += `💰 *Budget:* ${fmtBoth(task.budget)}\n`;
  msg += `${statusIcon} *Status:* ${task.status}\n`;
  msg += `📥 *Submissions:* ${subCount || 0}\n`;
  msg += `⏰ *Posted:* ${new Date(task.created_at).toLocaleDateString()}`;
  if (task.reference_media?.length > 0) {
    msg += `\n📎 *Media:* ${task.reference_media.length} file(s)`;
  }

  const btns = [];
  // Cancel only if open/active with no active submissions
  if ((task.status === 'open' || task.status === 'active') && (activeSubCount || 0) === 0) {
    btns.push({ id: `cancel_task_${taskId}`, title: '❌ Cancel Task' });
  }
  btns.push({ id: 'my_tasks', title: '📊 My Tasks' });
  btns.push({ id: 'menu', title: '🏠 Menu' });

  await sendButtons(phone, msg, btns);

  // Send reference media files to requester
  if (task.reference_media?.length > 0) {
    for (const media of task.reference_media) {
      try {
        if (media.url) {
          if (media.type === 'image') {
            await sendImage(phone, media.url, `📎 ${media.filename || 'Reference image'}`);
          } else {
            await sendDocument(phone, media.url, `📎 ${media.filename || 'File'}`, media.filename || 'file');
          }
        }
      } catch (e) { console.error('Error sending reference media:', e.message); }
    }
  }
}

// ═══════════════════════════════════════════════════════════════════════════════
// REVIEW SUBMISSIONS
// ═══════════════════════════════════════════════════════════════════════════════
async function showPendingReviews(phone, user, reviewOffset = 0) {
  // First get user's task IDs
  const { data: userTasks } = await supabase
    .from('tasks')
    .select('id')
    .eq('requester_id', user.id);
  
  const taskIds = (userTasks || []).map(t => t.id);
  
  if (taskIds.length === 0) {
    return sendButtons(phone,
      `📬 *REVIEW SUBMISSIONS*
` +
      `━━━━━━━━━━━━━━━━

` +
      `No pending submissions to review.

` +
      `When workers submit work, you'll see it here.`,
      [
        { id: 'my_tasks', title: '📊 My Tasks' },
        { id: 'menu', title: '🏠 Menu' },
      ]
    );
  }
  
  // Get submissions for user's tasks only
  const { data: submissions } = await supabase
    .from('submissions')
    .select(`
      id, content, created_at, worker_id,
      task:task_id(id, title, budget, requester_id),
      worker:worker_id(first_name, tasks_completed, tasks_approved)
    `)
    .eq('status', 'submitted')
    .in('task_id', taskIds)
    .order('created_at', { ascending: true })
    .limit(10);
  
  const valid = submissions || [];

  if (valid.length === 0) {
    return sendButtons(phone,
      `📬 *REVIEW SUBMISSIONS*\n` +
      `━━━━━━━━━━━━━━━━\n\n` +
      `No pending submissions to review.\n\n` +
      `When workers submit work, you'll see it here.`,
      [
        { id: 'my_tasks', title: '📊 My Tasks' },
        { id: 'menu', title: '🏠 Menu' },
      ]
    );
  }

  await sendList(phone,
    `📬 *REVIEW SUBMISSIONS*\n` +
    `━━━━━━━━━━━━━━━━\n\n` +
    `${valid.length} submission(s) waiting for review:\nPage ${Math.floor(reviewOffset / 7) + 1}/${Math.ceil(valid.length / 7)}`,
    '📋 Submissions',
    [{
      title: 'Pending Reviews',
      rows: valid.slice(reviewOffset, reviewOffset + 7).map(s => ({
        id: `review_${s.id}`,
        title: short(s.task.title, 22),
        desc: `By #${shortId(s.worker_id)} • ${fmt(s.task.budget)}`,
      })).concat(
        reviewOffset + 7 < valid.length ? [{ id: `reviews_next_${reviewOffset + 7}`, title: 'Next ▶️', desc: `Page ${Math.floor(reviewOffset / 7) + 2}` }] : [],
        reviewOffset > 0 ? [{ id: `reviews_prev_${reviewOffset - 7}`, title: '◀️ Previous', desc: `Page ${Math.floor(reviewOffset / 7)}` }] : [],
        [{ id: 'menu', title: '🔙 BACK', desc: 'Return to menu' }]
      ),
    }]
  );
}

async function showSubmissionReview(phone, user, submissionId) {
  const { data: sub } = await supabase
    .from('submissions')
    .select(`
      id, content, attachments, created_at, worker_id,
      task:task_id(id, title, description, budget),
      worker:worker_id(first_name, tasks_completed, tasks_approved)
    `)
    .eq('id', submissionId)
    .single();

  if (!sub || !sub.task) {
    return sendText(phone, '❌ Submission not found.');
  }

  const approvalRate = sub.worker?.tasks_completed > 0
    ? Math.round((sub.worker.tasks_approved / sub.worker.tasks_completed) * 100)
    : 0;

  const workerPay = Math.floor(sub.task.budget * (1 - CONFIG.WORKER_FEE));

  let msg = `📬 *REVIEW SUBMISSION*\n`;
  // Show restored message if draft was loaded from DB
  msg += `━━━━━━━━━━━━━━━━\n\n`;
  msg += `📋 *Task:* ${sub.task.title}\n\n`;
  msg += `👷 *Worker:* #${shortId(sub.worker_id)}\n`;
  msg += `📊 *Stats:* ${sub.worker?.tasks_completed || 0} tasks, ${approvalRate}% approved\n\n`;
  msg += `━━━━━━━━━━━━━━━━\n`;
  msg += `📝 *SUBMITTED WORK:*\n\n`;
  msg += `${sub.content || '(No text)'}\n\n`;

  if (sub.attachments?.length > 0) {
    msg += `📎 ${sub.attachments.length} file(s) attached\n\n`;
  }

  msg += `━━━━━━━━━━━━━━━━\n`;
  msg += `💰 Approve = Pay ${fmtBoth(workerPay)}`;

  userState.set(phone, { action: 'reviewing', submissionId: sub.id, taskId: sub.task.id });

  await sendButtons(phone, msg, [
    { id: 'approve_sub', title: '✅ Approve & Pay' },
    { id: 'reject_sub', title: '❌ Reject' },
    { id: 'reviews', title: '🔙 BACK' },
  ]);

  // Send attachments if any (from Telegram file IDs)
  if (sub.attachments && sub.attachments.length > 0) {
    const axios = require('axios');
    const tgToken = process.env.TELEGRAM_BOT_TOKEN;
    
    for (const fileId of sub.attachments) {
      try {
        // Get file info from Telegram
        const fileInfoRes = await axios.get(`https://api.telegram.org/bot${tgToken}/getFile?file_id=${fileId}`);
        if (fileInfoRes.data && fileInfoRes.data.ok) {
          const filePath = fileInfoRes.data.result.file_path;
          const fileUrl = `https://api.telegram.org/file/bot${tgToken}/${filePath}`;
          
          // Check if image or document
          if (filePath.match(/\.(jpg|jpeg|png|gif|webp)$/i)) {
            await sendImage(phone, fileUrl, '📎 Worker attachment');
          } else {
            const filename = filePath.split('/').pop() || 'attachment';
            await sendDocument(phone, fileUrl, '📎 Worker attachment', filename);
          }
        }
      } catch (e) {
        console.error('Error sending attachment to WhatsApp:', e.message);
        await sendText(phone, '⚠️ Could not load one attachment');
      }
    }
  }
}

async function approveSubmission(phone, user) {
  const state = userState.get(phone);
  if (!state?.submissionId) return showPendingReviews(phone, user);

  const { data: sub } = await supabase
    .from('submissions')
    .select(`
      id, worker_id, status,
      task:task_id(id, title, budget, requester_id)
    `)
    .eq('id', state.submissionId)
    .single();

  if (!sub || !sub.task) return sendText(phone, '❌ Submission not found.');

  // Double-pay guard
  if (sub.status === 'approved') {
    userState.delete(phone);
    return sendText(phone, '✅ Already approved — worker has been paid.');
  }

  // Use atomic RPC to approve and pay (same as TG bot — prevents double-pay race)
  const { data: rpcResult, error: rpcError } = await supabase.rpc('approve_submission_safe', {
    p_submission_id: sub.id,
  });

  if (rpcError || !rpcResult?.success) {
    console.error('[WA Approve] RPC failed:', rpcError?.message || rpcResult?.error);
    return sendText(phone, `❌ Approval failed: ${rpcResult?.error || 'Please try again.'}`);
  }

  const workerPay = rpcResult.payment || Math.floor(sub.task.budget * (1 - CONFIG.WORKER_FEE));

  // Update task status ONLY if all workers completed
  const { count: approvedCount } = await supabase
    .from('submissions')
    .select('*', { count: 'exact', head: true })
    .eq('task_id', sub.task.id)
    .eq('status', 'approved');
  
  const { data: taskInfo } = await supabase.from('tasks').select('max_workers').eq('id', sub.task.id).single();
  
  if ((approvedCount || 0) >= (taskInfo?.max_workers || 1)) {
    await supabase.from('tasks').update({ status: 'completed' }).eq('id', sub.task.id);
  }

  // Update worker stats
  await supabase.rpc('increment_field', {
    p_table: 'app_users',
    p_field: 'tasks_completed',
    p_id: sub.worker_id,
  });
  await supabase.rpc('increment_field', {
    p_table: 'app_users',
    p_field: 'tasks_approved',
    p_id: sub.worker_id,
  });

  // Process referral bonus (1% from platform cut to referrer)
  await processReferralBonus(sub.worker_id, workerPay);

  // Record transaction
  await supabase.from('transactions').insert({
    user_id: sub.worker_id,
    type: 'task_payment',
    amount: workerPay,
    description: `Payment for: ${sub.task.title}`,
    task_id: sub.task.id,
  });

  userState.delete(phone);

  await sendButtons(phone,
    `✅ *APPROVED!*\n` +
    `━━━━━━━━━━━━━━━━\n\n` +
    `${fmtBoth(workerPay)} sent to worker!\n\n` +
    `Task: "${sub.task.title}"`,
    [
      { id: 'reviews', title: '📬 More Reviews' },
      { id: 'menu', title: '🏠 Menu' },
    ]
  );
}

async function startRejectSubmission(phone, user) {
  const state = userState.get(phone);
  if (!state?.submissionId) return;

  state.action = 'rejecting';
  userState.set(phone, state);

  await sendText(phone,
    `❌ *REJECT SUBMISSION*\n` +
    `━━━━━━━━━━━━━━━━\n\n` +
    `Please provide a reason for rejection:\n\n` +
    `_This helps workers improve their work._`
  );
  await sendButtons(phone, 'Or cancel:', [
    { id: 'reviews', title: '❌ Cancel' },
  ]);
}

async function rejectSubmission(phone, user, reason) {
  const state = userState.get(phone);
  if (!state?.submissionId) return showPendingReviews(phone, user);

  const { data: sub } = await supabase
    .from('submissions')
    .select('id, worker_id, status, task:task_id(id, title, budget, requester_id)')
    .eq('id', state.submissionId)
    .single();

  if (!sub) return sendText(phone, '❌ Submission not found.');

  // Guard: already processed
  if (sub.status === 'rejected' || sub.status === 'approved') {
    userState.delete(phone);
    return sendText(phone, `❌ Submission already ${sub.status}.`);
  }

  // Update submission status
  await supabase
    .from('submissions')
    .update({
      status: 'rejected',
      rejection_reason: reason,
      reviewed_at: new Date().toISOString()
    })
    .eq('id', sub.id);

  // Update worker stats (rejected, NOT completed) + trust penalty
  await supabase.rpc('increment_field', {
    p_table: 'app_users',
    p_field: 'tasks_rejected',
    p_id: sub.worker_id,
  });
  // Trust score penalty
  const { data: worker } = await supabase.from('app_users').select('trust_score, telegram_id, phone').eq('id', sub.worker_id).single();
  if (worker) {
    await supabase.from('app_users').update({
      trust_score: Math.max(0, (worker.trust_score || 30) - 5),
    }).eq('id', sub.worker_id);
  }

  // Refund requester's balance
  if (sub.task?.requester_id && sub.task?.budget) {
    await addBalance(sub.task.requester_id, sub.task.budget, 'refund', `Rejected: ${sub.task.title}`);
  }

  // Release task slot + reopen
  const { data: task } = await supabase.from('tasks').select('current_workers').eq('id', sub.task.id).single();
  await supabase.from('tasks').update({
    current_workers: Math.max(0, (task?.current_workers || 1) - 1),
    status: 'open',
  }).eq('id', sub.task.id);

  // Notify worker about rejection
  try {
    if (worker) {
      const rejectMsg = `❌ <b>Submission Rejected</b>\n\nTask: "${sub.task?.title || 'Task'}"\n\nReason: "${reason}"\n\n💡 The task is still open — you can try again or find other tasks.`;

      // Notify via Telegram
      if (worker.telegram_id) {
        const { Telegraf } = require('telegraf');
        const tgBot = new Telegraf(process.env.TELEGRAM_BOT_TOKEN);
        await tgBot.telegram.sendMessage(worker.telegram_id, rejectMsg, { parse_mode: 'HTML' }).catch(() => {});
      }

      // Notify via WhatsApp if worker has phone
      if (worker.phone && worker.phone !== phone) {
        await sendText(worker.phone, rejectMsg.replace(/<[^>]*>/g, ''));
      }
    }
  } catch (e) {
    console.error('[Reject] Worker notification error:', e.message);
  }

  userState.delete(phone);

  await sendButtons(phone,
    `❌ *REJECTED*\n` +
    `━━━━━━━━━━━━━━━━\n\n` +
    `${fmtBoth(sub.task.budget)} refunded to your balance.\n` +
    `Task reopened for other workers.\n\n` +
    `Reason: "${reason}"`,
    [
      { id: 'reviews', title: '📬 More Reviews' },
      { id: 'menu', title: '🏠 Menu' },
    ]
  );
}

// ═══════════════════════════════════════════════════════════════════════════════
// REFERRAL BONUS PROCESSING
// ═══════════════════════════════════════════════════════════════════════════════
async function processReferralBonus(workerId, workerPay) {
  // Get worker's referrer
  const { data: worker } = await supabase
    .from('app_users')
    .select('referred_by')
    .eq('id', workerId)
    .single();

  if (!worker?.referred_by) return;

  // Calculate 1% bonus (from platform's 40% cut, not worker's pay)
  const platformCut = Math.floor(workerPay * (CONFIG.REQUESTER_FEE + CONFIG.WORKER_FEE) / (1 - CONFIG.WORKER_FEE));
  const referralBonus = Math.floor(platformCut * 0.01); // 1%

  if (referralBonus < 1) return; // Too small

  // Credit referrer
  await supabase.rpc('increment_balance', {
    p_user_id: worker.referred_by,
    p_amount: referralBonus,
    p_balance_type: 'earned',
  });

  // Record transaction
  await supabase.from('transactions').insert({
    user_id: worker.referred_by,
    type: 'referral_bonus',
    amount: referralBonus,
    description: `Referral bonus (1%) from worker`,
  });

  console.log(`Referral bonus: ${referralBonus} VAE to referrer ${worker.referred_by}`);
}

// ═══════════════════════════════════════════════════════════════════════════════
// WALLET
// ═══════════════════════════════════════════════════════════════════════════════
async function showWallet(phone, user) {
  const bal = await getBalance(user.id);

  // Get recent transactions
  const { data: txs } = await supabase
    .from('transactions')
    .select('type, amount, description, created_at')
    .eq('user_id', user.id)
    .order('created_at', { ascending: false })
    .limit(5);

  let msg = `💰 *WALLET*\n`;
  msg += `━━━━━━━━━━━━━━━━\n\n`;
  msg += `💳 *Deposit Balance:* ${fmtBoth(bal.deposit)}\n`;
  msg += `━━━━━━━━━━━━━━━━\n`;
  msg += `📊 *Total:* ${fmtBoth(bal.total)}\n\n`;

  if (txs && txs.length > 0) {
    msg += `📜 *Recent Transactions:*\n`;
    for (const tx of txs) {
      const icon = (tx.amount || 0) >= 0 ? '➕' : '➖';
      msg += `${icon} ${fmt(Math.abs(tx.amount || 0))} - ${tx.description?.slice(0, 25) || tx.type}\n`;
    }
  }

  await sendButtons(phone, msg, [
    { id: 'deposit', title: '💳 Add Funds' },
    { id: 'menu', title: '🔙 BACK' },
  ]);
}

async function showDeposit(phone, user) {
  const bal = await getBalance(user.id);
  await sendList(phone,
    `💳 *BUY VAES*\n` +
    `━━━━━━━━━━━━━━━━\n\n` +
    `Current balance: ${fmtBoth(bal.deposit)}\n\n` +
    `Select payment method:`,
    `💰 Payment`,
    [{
      title: `Payment Methods`,
      rows: [
        { id: `pay_card`, title: `💳 Card (Visa/MC)`, desc: `Pay with credit/debit card` },
        { id: `pay_crypto`, title: `🪙 Crypto (Celo)`, desc: `USDC, USDT, cUSD — instant` },
        { id: `menu`, title: `🔙 BACK`, desc: `Return to menu` },
      ],
    }]
  );
}

async function showCardDeposit(phone, user) {
  await sendList(phone,
    `💳 *CARD PAYMENT*\n` +
    `━━━━━━━━━━━━━━━━\n\n` +
    `Select amount to buy:`,
    `💰 Amounts`,
    [{
      title: `Quick Amounts`,
      rows: [
        { id: `card_500`, title: `Ⓥ500 → $6`, desc: `Includes 20% fee` },
        { id: `card_1000`, title: `Ⓥ1,000 → $12`, desc: `Includes 20% fee` },
        { id: `card_2500`, title: `Ⓥ2,500 → $30`, desc: `Includes 20% fee` },
        { id: `card_5000`, title: `Ⓥ5,000 → $60`, desc: `Includes 20% fee` },
        { id: `card_custom`, title: `🔢 Custom Amount`, desc: `Enter any amount` },
        { id: `deposit`, title: `🔙 BACK`, desc: `Payment methods` },
      ],
    }]
  );
}

async function showCryptoDeposit(phone, user) {
  await sendList(phone,
    `🪙 *CRYPTO DEPOSIT*\n` +
    `━━━━━━━━━━━━━━━━\n\n` +
    `All deposits are on the *Celo* blockchain.\n\n` +
    `💡 *Need a wallet?*\n` +
    `Download _Valora_ or _MiniPay_ from your app store — they work natively on Celo.\n\n` +
    `Select token:`,
    `🪙 Tokens`,
    [{
      title: `Select Token`,
      rows: [
        { id: `crypto_USDC`, title: `💵 USDC`, desc: `USD Coin on Celo` },
        { id: `crypto_USDT`, title: `💵 USDT`, desc: `Tether on Celo` },
        { id: `crypto_cUSD`, title: `💵 cUSD`, desc: `Celo Dollar` },
        { id: `deposit`, title: `🔙 BACK`, desc: `Payment methods` },
      ],
    }]
  );
}

async function showCryptoToken(phone, user, token) {
  const treasury = process.env.TREASURY_ADDRESS;
  userState.set(phone, { action: 'crypto_verify', token: token });

  await sendText(phone,
    `💵 *DEPOSIT ${token} ON CELO*\n` +
    `━━━━━━━━━━━━━━━━\n\n` +
    `Send ${token} to this address:\n\n` +
    `\`${treasury}\`\n` +
    `_(tap to copy)_\n\n` +
    `⚠️ *IMPORTANT:*\n` +
    `• Only send *${token}* on *CELO* network\n` +
    `• Minimum deposit: *$${cryptoService.MIN_DEPOSIT_USD}*\n` +
    `• Wrong network = lost funds\n\n` +
    `💡 Use *Valora*, *MiniPay*, or *MetaMask* (Celo network) to send.\n\n` +
    `_After sending, tap the button below_`
  );
  await sendButtons(phone, `Ready?`, [
    { id: `crypto_sent_${token}`, title: `✅ Sent` },
    { id: `pay_crypto`, title: `🔙 BACK` },
  ]);
}

async function showCryptoVerify(phone, token) {
  userState.set(phone, { action: 'crypto_verify', token: token });
  await sendText(phone,
    `📝 *VERIFY YOUR DEPOSIT*\n` +
    `━━━━━━━━━━━━━━━━\n\n` +
    `Paste your transaction hash below:\n\n` +
    `_Find it in your wallet transaction history._\n` +
    `_It starts with 0x..._`
  );
  await sendButtons(phone, `Need help?`, [
    { id: `crypto_help`, title: `❓ Help` },
    { id: `crypto_${token}`, title: `🔙 BACK` },
  ]);
}

// ═══════════════════════════════════════════════════════════════════════════════
// HELP
// ═══════════════════════════════════════════════════════════════════════════════
async function showHelp(phone) {
  await sendList(phone,
    `❓ *HELP & FAQ*\n` +
    `━━━━━━━━━━━━━━━━\n\n` +
    `Select a topic:`,
    '📚 Topics',
    [{
      title: 'Help Topics',
      rows: [
        { id: 'help_post', title: '📝 Posting Tasks', desc: 'How to post tasks' },
        { id: 'help_pay', title: '💰 Payment & Fees', desc: 'Pricing explained' },
        { id: 'help_review', title: '📬 Reviewing Work', desc: 'Approve/reject submissions' },
        { id: 'help_workers', title: '👷 Finding Workers', desc: 'How workers find tasks' },
        { id: 'help_contact', title: '📞 Contact Support', desc: 'Get help from us' },
        { id: 'menu', title: '🔙 BACK', desc: 'Return to menu' },
      ],
    }]
  );
}

const HELP_CONTENT = {
  post: `📝 *POSTING TASKS*\n\n` +
    `1. Tap "Post Task" from menu\n` +
    `2. Set title, description, budget\n` +
    `3. Preview and confirm\n` +
    `4. Workers on Telegram see it immediately!\n\n` +
    `Tips:\n` +
    `• Be specific about requirements\n` +
    `• Set fair budgets for good work\n` +
    `• Use templates for quick posting`,

  pay: `💰 *PAYMENT & FEES*\n\n` +
    `• Ⓥ100 = $1.00 USD\n` +
    `• 20% platform fee on deposits\n\n` +
    `Example:\n` +
    `• Deposit $12 → Get Ⓥ1000\n` +
    `• Use VAE to post tasks\n\n` +
    `Simple and transparent!`,

  review: `📬 *REVIEWING WORK*\n\n` +
    `When workers submit:\n` +
    `1. You get notified\n` +
    `2. Go to "Review Work" in menu\n` +
    `3. Check submission quality\n` +
    `4. Approve = Worker gets paid instantly\n` +
    `5. Reject = Give reason, task reopens\n\n` +
    `Auto-approve after 48 hours if no action.`,

  workers: `👷 *FINDING WORKERS*\n\n` +
    `Workers use Telegram: ${CONFIG.TELEGRAM_BOT}\n\n` +
    `• Global workforce (180+ countries)\n` +
    `• Rated by approval history\n` +
    `• Competition mode: 2 workers submit, you pick best`,

  contact: `📞 *CONTACT SUPPORT*\n\nType your issue below and we'll create a ticket.\n\nOr reach us at:\n\n` +
    `Email: support@loopuman.com\n\n` +
    `Or message us on:\n` +
    `• Telegram: @LoopumanSupport\n` +
    `• Website: loopuman.com/support`,
};

// ═══════════════════════════════════════════════════════════════════════════════
// MESSAGE HANDLER
// ═══════════════════════════════════════════════════════════════════════════════
async function handleMessage(phone, name, messageType, content) {
  console.log(`[${new Date().toISOString()}] handleMessage: ${phone}, type: ${messageType}`);

  const user = await getOrCreateUser(phone, name);
  if (!user) {
    return sendText(phone, '❌ Error. Please try again.');
  }

  // Block banned users
  if (user.banned) {
    return sendText(phone, '⛔ Your account has been suspended for violating platform guidelines.\n\nIf you believe this is an error, email support@loopuman.com');
  }

  // Handle interactive responses (button/list selections)
  if (messageType === 'interactive') {
    const id = content.button_reply?.id || content.list_reply?.id;
    console.log(`Interactive selection: ${id}`);
    return handleAction(phone, user, id);
  }

  // Handle text input based on current state
  if (messageType === 'text') {
    const text = content.body?.trim() || '';
    
    // Check support state BEFORE getOrCreateDraft (which would overwrite it)
    const preState = userState.get(phone);
    if (preState?.action === 'support') {
      const ticketId = 'TKT' + Math.random().toString(36).substring(2, 10).toUpperCase();
      const { error } = await supabase.from('support_tickets').insert({
        ticket_id: ticketId, user_id: user.id, subject: 'WhatsApp Support Request', message: text, status: 'open',
      });
      userState.delete(phone);
      if (error) {
        console.error('Support ticket error:', error);
        return sendText(phone, '❌ Could not create ticket. Please email support@loopuman.com');
      }
      await sendText(phone, '✅ *Ticket Created!*\n\n🎫 ID: ' + ticketId + '\n\nWe will respond within 24 hours.');
      return sendButtons(phone, 'More questions?', [{ id: 'help', title: '❓ More Help' }, { id: 'menu', title: '🏠 Menu' }]);
    }
    
    // Check card_custom state BEFORE getOrCreateDraft
    if (preState?.action === 'card_custom') {
      const amount = parseInt(text.replace(/[^\d]/g, ''));
      if (isNaN(amount) || amount < 500) {
        return sendText(phone, '❌ Minimum is Ⓥ500 ($6). Try again:');
      }
      if (amount > 100000) {
        return sendText(phone, '❌ Maximum is Ⓥ100,000 ($1,200). Try again:');
      }
      const session = await stripeService.createCheckoutSession(user.id, amount, null, phone);
      if (!session.success) return sendText(phone, '❌ ' + session.error);
      userState.delete(phone);
      await sendText(phone, '💳 *COMPLETE PAYMENT*\n━━━━━━━━━━━━━━━━\n\nAmount: *Ⓥ' + amount.toLocaleString() + '*\nPrice: *$' + session.prices.totalUsd + '*\n_(Includes 20% platform fee)_\n\nClick the link below to pay securely:\n\n' + session.checkoutUrl);
      return sendButtons(phone, 'Done?', [{ id: 'deposit', title: '🔙 BACK' }]);
    }
    const state = await getOrCreateDraft(phone, user.id);

    // Reset state on common greetings/commands ONLY if not in editing mode
    const resetWords = ['hi', 'hello', 'hey', 'start', 'menu', 'home', 'help', 'cancel', 'stop', 'reset'];
    const firstWord = text.toLowerCase().split(/[\s,!?.]+/)[0];

    // If user is typing a title/description, don't reset on greetings
    if (state?.editing) {
      // Process the input for the field they're editing
      if (state.editing === 'title') {
        if (text.length < 3) {
          return sendText(phone, '⚠️ Title too short (min 3 characters). Try again:');
        }
        const screen = screenContent(text);
        if (screen.blocked) return sendText(phone, '🚫 This title contains prohibited content.\n\nIllegal, harmful, or inappropriate tasks are not allowed on Loopuman.');
        state.title = text.slice(0, 100);
        await saveDraft(user.id, state);
        state.editing = null;
        return showTaskHub(phone, user, `✅ Title set: "${short(text, 40)}"`);
      }

      if (state.editing === 'description') {
        if (text.length < 10) {
          return sendText(phone, '⚠️ Description too short (min 10 characters). Try again:');
        }
        const screen = screenContent(text);
        if (screen.blocked) return sendText(phone, '🚫 This description contains prohibited content.\n\nIllegal, harmful, or inappropriate tasks are not allowed on Loopuman.');
        state.description = text.slice(0, 2000);
        await saveDraft(user.id, state);
        state.editing = null;
        return showTaskHub(phone, user, `✅ Description set (${text.length} chars)`);
      }

      if (state.editing === 'budget_custom') {
        const budget = parseInt(text.replace(/[^\d]/g, ''));
        if (isNaN(budget) || budget < CONFIG.MIN_TASK_BUDGET) {
          return sendText(phone, `⚠️ Minimum budget is ${fmt(CONFIG.MIN_TASK_BUDGET)}. Try again:`);
        }
        state.budget = budget;
        await saveDraft(user.id, state);
        state.editing = null;
        return showTaskHub(phone, user, `✅ Budget set: ${fmtBoth(budget)}`);
      }
    }

    // Reset on greetings only if not editing
    if (resetWords.includes(firstWord)) {
      // Don't delete task draft - just go to menu
      return showMainMenu(phone, user);
    }

    // Crypto TX hash verification
    if (state?.action === "crypto_verify") {
      const txHash = text.trim();
      if (!txHash.match(/^0x[a-fA-F0-9]{64}$/)) {
        return sendText(phone, "❌ Invalid transaction hash format.\n\nIt should start with 0x and be 66 characters long.");
      }
      const token = state.token || "USDC";
      const alreadyUsed = await cryptoService.isTxHashUsed(supabase, txHash);
      if (alreadyUsed) {
        return sendText(phone, "❌ This transaction has already been credited.");
      }
      await sendText(phone, "⏳ Verifying transaction on Celo blockchain...");
      const result = await cryptoService.verifyDeposit(txHash, token);
      if (!result.success) {
        await sendText(phone, `❌ ${result.error}`);
        return sendButtons(phone, "Try again?", [{ id: `crypto_sent_${token}`, title: "🔄 Retry" }]);
      }
      const vaeAmount = Math.round(result.total * 100);
      await addBalance(user.id, vaeAmount, "crypto_deposit", `${result.token} deposit`);
      await cryptoService.recordDeposit(supabase, {
        userId: user.id,
        txHash: txHash,
        token: result.token,
        amountUsd: result.amount,
        bonusUsd: result.bonus,
        amountVae: vaeAmount,
        senderAddress: result.sender
      });
      userState.delete(phone);
      await sendText(phone,
        `✅ *DEPOSIT CONFIRMED!*\n` +
        `━━━━━━━━━━━━━━━━\n\n` +
        `Received: *$${result.amount.toFixed(2)} ${result.token}*\n` +
        `From: \`${result.sender.slice(0, 6)}...${result.sender.slice(-4)}\`\n` +
        `Credited: *Ⓥ${vaeAmount.toLocaleString()}*\n\n` +
        `🔗 View on Celoscan:\nhttps://celoscan.io/tx/${txHash}`
      );
      return sendButtons(phone, "Done!", [{ id: "menu", title: "🏠 Menu" }]);
    }

    // Custom card amount
    if (state?.action === "card_custom") {
      const amount = parseInt(text.replace(/[^\d]/g, ""));
      if (isNaN(amount) || amount < 500) {
        return sendText(phone, "❌ Minimum is Ⓥ500 ($6). Try again:");
      }
      if (amount > 100000) {
        return sendText(phone, "❌ Maximum is Ⓥ100,000 ($1,200). Try again:");
      }
      const session = await stripeService.createCheckoutSession(user.id, amount, null, phone);
      if (!session.success) return sendText(phone, `❌ ${session.error}`);
      userState.delete(phone);
      await sendText(phone,
        `💳 *COMPLETE PAYMENT*\n` +
        `━━━━━━━━━━━━━━━━\n\n` +
        `Amount: *Ⓥ${amount.toLocaleString()}*\n` +
        `Price: *$${session.prices.totalUsd}*\n` +
        `_(Includes 20% platform fee)_\n\n` +
        `Click the link below to pay securely:\n\n` +
        `${session.checkoutUrl}`
      );
      return sendButtons(phone, "Done?", [{ id: "deposit", title: "🔙 BACK" }]);
    }

    // Rejection reason
    if (state?.action === 'rejecting') {
      return rejectSubmission(phone, user, text);
    }

    // Default: show menu
    return showMainMenu(phone, user);
  }

  // Handle media messages (image, document, audio, video)
  if (['image', 'document', 'audio', 'voice', 'video'].includes(messageType)) {
    const preState = userState.get(phone);
    
    // Media during task creation (adding_media or task_hub state)
    if (preState?.action === 'adding_media' || preState?.action === 'task_hub') {
      const state = preState;
      if (!state.media) state.media = [];
      if (state.media.length >= 10) {
        return sendButtons(phone, '⚠️ Maximum 10 files reached.', [{ id: 'media_done', title: '✅ Done' }]);
      }
      
      const mediaObj = content.image || content.document || content.audio || content.video;
      const mediaId = mediaObj?.id;
      const mimeType = mediaObj?.mime_type || 'unknown';
      const filename = content.document?.filename || (messageType === 'image' ? 'photo.jpg' : messageType + '_file');
      
      if (mediaId) {
        try {
          // Download from WhatsApp, upload to Supabase Storage
          const mediaRes = await fetch(`https://graph.facebook.com/v18.0/${mediaId}`, { headers: { 'Authorization': `Bearer ${CONFIG.ACCESS_TOKEN}` } });
          const mediaData = await mediaRes.json();
          if (mediaData.url) {
            const fileRes = await fetch(mediaData.url, { headers: { 'Authorization': `Bearer ${CONFIG.ACCESS_TOKEN}` } });
            const buffer = Buffer.from(await fileRes.arrayBuffer());
            const storagePath = `tasks/${user.id}/${Date.now()}_${filename}`;
            const { error: uploadErr } = await supabase.storage.from('task-media').upload(storagePath, buffer, { contentType: mimeType });
            if (!uploadErr) {
              const { data: pubUrl } = supabase.storage.from('task-media').getPublicUrl(storagePath);
              state.media.push({ type: messageType, url: pubUrl.publicUrl, filename, mime_type: mimeType, storage_path: storagePath });
            } else {
              console.error('Upload error:', uploadErr);
              state.media.push({ type: messageType, wa_media_id: mediaId, filename, mime_type: mimeType });
            }
          } else {
            state.media.push({ type: messageType, wa_media_id: mediaId, filename, mime_type: mimeType });
          }
        } catch (e) {
          console.error('Media download error:', e.message);
          state.media.push({ type: messageType, wa_media_id: mediaId, filename, mime_type: mimeType });
        }
        
        state.action = 'adding_media';
        userState.set(phone, state);
        return sendButtons(phone, `✅ *Added:* ${filename}\n📎 *Total:* ${state.media.length} file(s)\n\n_Send more or tap Done._`, [{ id: 'media_done', title: '✅ Done' }, { id: 'media_clear', title: '🗑️ Clear All' }]);
      }
    }
    
    return sendText(phone, '📎 To attach files to a task, go to *Post Task → 📎 Add Media*.');
  }

  // Default: show menu
  return showMainMenu(phone, user);
}

async function handleAction(phone, user, action) {
  console.log(`[${new Date().toISOString()}] handleAction: ${action}`);

  // Pagination handlers
  if (action.startsWith('tasks_next_') || action.startsWith('tasks_prev_')) {
    const offset = parseInt(action.split('_').pop()) || 0;
    return showMyTasks(phone, user, offset);
  }
  if (action.startsWith('reviews_next_') || action.startsWith('reviews_prev_')) {
    const offset = parseInt(action.split('_').pop()) || 0;
    return showPendingReviews(phone, user, offset);
  }
  // Main menu actions
  switch (action) {
    case 'menu':
      // Don't delete draft - preserve it
      return showMainMenu(phone, user);

    case 'post_task':
      // Create new draft only if none exists
      await getOrCreateDraft(phone, user.id);
      return showTaskHub(phone, user);

    case 'templates':
      return showTemplates(phone);

    case 'my_tasks':
      return showMyTasks(phone, user);

    case 'reviews':
      return showPendingReviews(phone, user);

    case 'wallet':
      return showWallet(phone, user);

    case 'deposit':
      return showDeposit(phone, user);

    case 'pay_card':
      return showCardDeposit(phone, user);

    case 'pay_crypto':
      return showCryptoDeposit(phone, user);

    case 'crypto_help':
      const helpState = userState.get(phone) || {};
      const helpToken = helpState.token || 'USDC';
      await sendText(phone,
        `❓ *CRYPTO DEPOSIT HELP*\n` +
        `━━━━━━━━━━━━━━━━\n\n` +
        `*How to find your TX hash:*\n` +
        `1. Open your wallet app\n` +
        `2. Go to transaction history\n` +
        `3. Find the transfer you made\n` +
        `4. Copy the Transaction Hash (starts with 0x)\n\n` +
        `*Common issues:*\n` +
        `• _TX not found_ - Wait 1-2 minutes\n` +
        `• _Wrong network_ - Must be CELO only\n` +
        `• _Below minimum_ - Min deposit is $5\n\n` +
        `Still need help? Contact @LoopumanSupport`
      );
      return sendButtons(phone, `Go back?`, [{ id: `crypto_sent_${helpToken}`, title: `🔙 Back` }]);

    case 'help':
      return showHelp(phone);
  }

  // Help topics
  if (action.startsWith('help_')) {
    const topic = action.replace('help_', '');
    const content = HELP_CONTENT[topic];
    if (topic === 'contact') {
      const state = userState.get(phone) || {};
      state.action = 'support';
      userState.set(phone, state);
      return sendButtons(phone, HELP_CONTENT.contact, [{ id: 'menu', title: '🏠 Menu' }]);
    }
    if (content) {
      await sendText(phone, content);
      return sendButtons(phone, 'More questions?', [{ id: 'help', title: '❓ More Help' }, { id: 'menu', title: '🏠 Menu' }]);
    }
  }

  // Crypto token selection
  if (action.startsWith("crypto_")) {
    const token = action.replace("crypto_", "");
    if (["USDC", "USDT", "cUSD"].includes(token)) {
      return showCryptoToken(phone, user, token);
    }
    if (action.startsWith("crypto_sent_")) {
      const sentToken = action.replace("crypto_sent_", "");
      return showCryptoVerify(phone, sentToken);
    }
  }

  // Card payment amounts
  if (action.startsWith("card_")) {
    const amountStr = action.replace("card_", "");
    if (amountStr === "custom") {
      userState.set(phone, { action: "card_custom" });
      return sendText(phone, "💳 *CUSTOM AMOUNT*\n\nEnter amount in VAE (min Ⓥ500):\n\nExample: 2000");
    }
    const amount = parseInt(amountStr);
    if (amount) {
      const session = await stripeService.createCheckoutSession(user.id, amount, null, phone);
      if (!session.success) return sendText(phone, `❌ ${session.error}`);
      await sendText(phone,
        `💳 *COMPLETE PAYMENT*\n` +
        `━━━━━━━━━━━━━━━━\n\n` +
        `Amount: *Ⓥ${amount.toLocaleString()}*\n` +
        `Price: *$${session.prices.totalUsd}*\n` +
        `_(Includes 20% platform fee)_\n\n` +
        `Click the link below to pay securely:\n\n` +
        `${session.checkoutUrl}`
      );
      return sendButtons(phone, `Done?`, [{ id: `deposit`, title: `🔙 BACK` }]);
    }
  }

  // Template selection
  if (action.startsWith('tpl_')) {
    const templateId = action.replace('tpl_', '');
    return applyTemplate(phone, user, templateId);
  }

  // Hub actions
  if (action === 'edit_title') return showEditTitle(phone, user);
  if (action === 'edit_desc') return showEditDescription(phone, user);
  if (action === 'edit_category') return showEditCategory(phone, user);
  if (action === 'edit_budget') return showEditBudget(phone, user);
  if (action === 'edit_workers') return showEditWorkers(phone, user);
  if (action === 'edit_duration') return showEditDuration(phone, user);

  // ═══ MEDIA HANDLERS ═══
  if (action === 'hub_media') {
    const state = await getOrCreateDraft(phone, user.id);
    state.action = 'adding_media';
    userState.set(phone, state);
    const mc = state.media?.length || 0;
    let msg = `📎 *ATTACH REFERENCE MEDIA*\n━━━━━━━━━━━━━━━━\n\nSend images, documents, or files that help workers understand the task.\n\n*Current:* ${mc} file(s)\n`;
    if (mc > 0) {
      state.media.forEach((m, i) => { msg += `${i + 1}. ${m.type === 'image' ? '🖼️' : '📄'} ${m.filename || m.type}\n`; });
    }
    msg += `\n_Send photos or documents now._`;
    const btns = [{ id: 'media_done', title: '✅ Done' }];
    if (mc > 0) btns.push({ id: 'media_clear', title: '🗑️ Clear All' });
    return sendButtons(phone, msg, btns);
  }
  if (action === 'media_done') {
    const state = userState.get(phone);
    if (state) state.action = 'task_hub';
    return showTaskHub(phone, user, `📎 ${state?.media?.length || 0} file(s) attached`);
  }
  if (action === 'media_clear') {
    const state = userState.get(phone);
    if (state) { state.media = []; state.action = 'task_hub'; }
    return showTaskHub(phone, user, '🗑️ Media cleared');
  }

  // ═══ TASK CANCELLATION ═══
  if (action.startsWith('cancel_task_')) {
    const taskId = action.replace('cancel_task_', '');
    const { data: task } = await supabase.from('tasks').select('*').eq('id', taskId).eq('requester_id', user.id).single();
    if (!task) return sendText(phone, '❌ Task not found.');
    if (task.status === 'completed' || task.status === 'cancelled') return sendText(phone, `❌ Task already ${task.status}.`);
    const { count: activeSubs } = await supabase.from('submissions').select('*', { count: 'exact', head: true }).eq('task_id', taskId).in('status', ['in_progress', 'submitted']);
    if (activeSubs > 0) return sendButtons(phone, `⚠️ Cannot cancel — ${activeSubs} active worker(s).\nReject their submissions first.`, [{ id: `task_${taskId}`, title: '🔙 Back' }]);
    return sendButtons(phone, `⚠️ *Cancel Task?*\n━━━━━━━━━━━━━━━━\n\n📋 "${task.title}"\n💰 Budget: ${fmtBoth(task.budget)}\n\n*Your budget will be refunded.*`, [{ id: `cfm_cancel_${taskId}`, title: '✅ Yes, Cancel' }, { id: `task_${taskId}`, title: '🔙 No, Keep' }]);
  }
  if (action.startsWith('cfm_cancel_')) {
    const taskId = action.replace('cfm_cancel_', '');
    const { data: task } = await supabase.from('tasks').select('*').eq('id', taskId).eq('requester_id', user.id).single();
    if (!task) return sendText(phone, '❌ Task not found.');
    if (task.status === 'cancelled' || task.status === 'completed') return sendText(phone, `❌ Task already ${task.status}.`);
    await supabase.from('tasks').update({ status: 'cancelled' }).eq('id', taskId);
    // ATOMIC refund
    const { error: rpcErr } = await supabase.rpc('increment_balance', { p_user_id: user.id, p_amount: task.budget, p_balance_type: 'available_only' });
    if (rpcErr) {
      // Fallback
      const { data: bal } = await supabase.from('vae_balances').select('available').eq('user_id', user.id).single();
      await supabase.from('vae_balances').update({ available: (bal?.available || 0) + task.budget, updated_at: new Date().toISOString() }).eq('user_id', user.id);
    }
    await supabase.from('transactions').insert({ user_id: user.id, type: 'refund', amount: task.budget, description: `Cancelled: ${task.title}`, task_id: taskId });
    return sendButtons(phone, `✅ *Task Cancelled*\n\n"${task.title}"\n${fmtBoth(task.budget)} refunded.`, [{ id: 'my_tasks', title: '📊 My Tasks' }, { id: 'menu', title: '🏠 Menu' }]);
  }

  if (action === 'hub_preview') return showPreview(phone, user);

  // Confirmation before clearing
  if (action === 'hub_fresh_confirm') {
    return showClearConfirmation(phone);
  }

  if (action === 'hub_fresh') {
    await clearDraft(user.id);
    userState.set(phone, initTaskDraft());
    return showTaskHub(phone, user, '🔴 Cleared! Starting fresh.');
  }

  if (action === 'hub_back' || action === 'back_hub') {
    const state = await getOrCreateDraft(phone, user.id);
    if (state?.action === 'task_hub') {
      state.editing = null; // Clear editing state but keep draft
      return showTaskHub(phone, user);
    }
    return showMainMenu(phone, user);
  }

  if (action === 'confirm_post') return postTask(phone, user);

  // Category selection
  if (action.startsWith('cat_')) {
    const category = action.replace('cat_', '');
    const state = await getOrCreateDraft(phone, user.id);
    if (state?.action === 'task_hub') {
      state.category = category;
      await saveDraft(user.id, state);
      return showTaskHub(phone, user, `✅ Category: ${CATEGORIES[category]?.name || 'Other'}`);
    }
  }

  // Budget selection
  if (action.startsWith('budget_')) {
    const state = await getOrCreateDraft(phone, user.id);
    if (!state || state.action !== 'task_hub') return showMainMenu(phone, user);

    if (action === 'budget_custom') {
      state.editing = 'budget_custom';
      userState.set(phone, state);
      await sendText(phone, `Type your custom budget amount (minimum ${fmt(CONFIG.MIN_TASK_BUDGET)}):`);
      return sendButtons(phone, 'Or cancel:', [{ id: 'back_hub', title: '❌ Cancel' }]);
    }

    const budget = parseInt(action.replace('budget_', ''));
    if (!isNaN(budget) && budget >= CONFIG.MIN_TASK_BUDGET) {
      state.budget = budget;
      await saveDraft(user.id, state);
      return showTaskHub(phone, user, `✅ Budget: ${fmtBoth(budget)}`);
    }
  }

  // Workers selection (FIXED: max 2)
  if (action.startsWith('workers_')) {
    const state = await getOrCreateDraft(phone, user.id);
    if (!state || state.action !== 'task_hub') return showMainMenu(phone, user);

    const workers = parseInt(action.replace('workers_', ''));
    if (!isNaN(workers) && workers >= 1 && workers <= CONFIG.MAX_WORKERS) {
      state.maxWorkers = workers;
      state.isCompetition = workers > 1;
      return showTaskHub(phone, user, `✅ Workers: ${workers}${workers > 1 ? ' (Competition)' : ''}`);
    }
  }

  // Duration selection
  if (action.startsWith('duration_')) {
    const state = await getOrCreateDraft(phone, user.id);
    if (!state || state.action !== 'task_hub') return showMainMenu(phone, user);

    const days = parseInt(action.replace('duration_', ''));
    if (!isNaN(days) && days >= 1) {
      state.durationDays = days;
      return showTaskHub(phone, user, `✅ Duration: ${days} days`);
    }
  }

  // Task detail
  if (action.startsWith('task_')) {
    const taskId = action.replace('task_', '');
    return showTaskDetail(phone, user, taskId);
  }

  // Submission review
  if (action.startsWith('review_')) {
    const subId = action.replace('review_', '');
    return showSubmissionReview(phone, user, subId);
  }
  if (action === 'approve_sub') return approveSubmission(phone, user);
  if (action === 'reject_sub') return startRejectSubmission(phone, user);

  // Default
  return showMainMenu(phone, user);
}

// ═══════════════════════════════════════════════════════════════════════════════
// RATE LIMITING
// ═══════════════════════════════════════════════════════════════════════════════
const rateLimits = new Map();
const RATE_LIMIT = { messages: 30, window: 60000 }; // 30 messages per minute

function checkRateLimit(phone) {
  const now = Date.now();
  const data = rateLimits.get(phone) || { count: 0, windowStart: now };
  if (now - data.windowStart > RATE_LIMIT.window) { data.count = 0; data.windowStart = now; }
  data.count++;
  rateLimits.set(phone, data);
  return data.count <= RATE_LIMIT.messages;
}

// Clean rate limit data periodically
setInterval(() => {
  const now = Date.now();
  for (const [key, data] of rateLimits.entries()) {
    if (now - data.windowStart > RATE_LIMIT.window * 2) rateLimits.delete(key);
  }
}, 120000);

// ═══════════════════════════════════════════════════════════════════════════════
// WEBHOOK ROUTES
// ═══════════════════════════════════════════════════════════════════════════════
app.get('/webhook', (req, res) => {
  const mode = req.query['hub.mode'];
  const token = req.query['hub.verify_token'];
  const challenge = req.query['hub.challenge'];

  if (mode === 'subscribe' && token === CONFIG.VERIFY_TOKEN) {
    console.log('✅ Webhook verified');
    return res.status(200).send(challenge);
  }
  res.sendStatus(403);
});

app.post('/webhook', async (req, res) => {
  res.sendStatus(200); // Acknowledge immediately

  try {
    const body = req.body;
    console.log('WEBHOOK:', JSON.stringify(body, null, 2));

    const entry = body.entry?.[0];
    const change = entry?.changes?.[0];
    const value = change?.value;

    // Skip status updates
    if (value?.statuses) return;

    const message = value?.messages?.[0];
    if (!message) return;

    const phone = message.from;
    const name = value.contacts?.[0]?.profile?.name || 'User';
    const type = message.type;

    // Rate limit check
    if (!checkRateLimit(phone)) {
      console.log(`[RATE LIMIT] ${phone} exceeded ${RATE_LIMIT.messages}/min`);
      return;
    }

    let content = {};
    if (type === 'text') {
      content = { body: message.text?.body };
    } else if (type === 'interactive') {
      content = message.interactive;
    } else if (type === 'button') {
      content = { button_reply: { id: message.button?.payload } };
    } else if (type === 'image') {
      content = { image: message.image };
    } else if (type === 'document') {
      content = { document: message.document };
    } else if (type === 'audio' || type === 'voice') {
      content = { audio: message.audio || message.voice };
    } else if (type === 'video') {
      content = { video: message.video };
    }

    await handleMessage(phone, name, type, content);
  } catch (e) {
    console.error('Webhook error:', e);
  }
});

app.get('/health', (req, res) => res.json({ status: 'ok', version: '7.0-fixed' }));

app.listen(CONFIG.PORT, () => {
  console.log(`WhatsApp Bot listening on port ${CONFIG.PORT}`);
});

// ═══════════════════════════════════════════════════════════════════════════════
// PROACTIVE NOTIFICATIONS - Notify requesters instantly when workers submit
// ═══════════════════════════════════════════════════════════════════════════════
async function notifyRequesterWhatsApp(requesterId, taskTitle) {
  try {
    const { data: user } = await supabase.from('app_users').select('phone, first_name').eq('id', requesterId).single();
    if (!user?.phone) return false;
    await sendText(user.phone, `📥 *NEW SUBMISSION!*\n━━━━━━━━━━━━━━━━\n\nTask: *${taskTitle}*\n\nA worker completed your task! Review now.`);
    await sendButtons(user.phone, 'Actions:', [{ id: 'reviews', title: '📋 Review Now' }, { id: 'menu', title: '🏠 Menu' }]);
    console.log('[WhatsApp] Notified requester ' + user.phone);
    return true;
  } catch (e) {
    console.error('[WhatsApp] Notify error:', e.message);
    return false;
  }
}

// Export for use by other services
module.exports = { notifyRequesterWhatsApp };

// ═══════════════════════════════════════════════════════════════════════════════
// DRAFT PERSISTENCE - Survives bot restarts and user sessions
// ═══════════════════════════════════════════════════════════════════════════════

async function saveDraft(userId, draft) {
  try {
    draft.saved_at = new Date().toISOString();
    await supabase
      .from('app_users')
      .update({ task_draft: draft })
      .eq('id', userId);
    console.log(`[Draft] Saved for ${userId.slice(0,8)}`);
  } catch (e) {
    console.error('[Draft] Save error:', e.message);
  }
}

async function clearDraft(userId) {
  try {
    await supabase
      .from('app_users')
      .update({ task_draft: null })
      .eq('id', userId);
    console.log(`[Draft] Cleared for ${userId.slice(0,8)}`);
  } catch (e) {
    console.error('[Draft] Clear error:', e.message);
  }
}

async function getDraft(userId) {
  try {
    const { data } = await supabase
      .from('app_users')
      .select('task_draft')
      .eq('id', userId)
      .single();
    return data?.task_draft || null;
  } catch (e) {
    return null;
  }
}

function formatDraftAge(savedAt) {
  if (!savedAt) return '';
  const mins = Math.floor((Date.now() - new Date(savedAt).getTime()) / 60000);
  if (mins < 60) return `${mins} min ago`;
  const hours = Math.floor(mins / 60);
  if (hours < 24) return `${hours} hour${hours > 1 ? 's' : ''} ago`;
  const days = Math.floor(hours / 24);
  return `${days} day${days > 1 ? 's' : ''} ago`;
}
