/**
 * creators.js – API pública del creador (sin guard de admin).
 *
 * Endpoints:
 *   POST /api/creator/click        → cuenta un clic de link ?ref= (público)
 *   POST /api/creator/login        → código + PIN → token HMAC 7d
 *   GET  /api/creator/me           → stats reales (requiere token)
 *   GET  /api/creator/sales        → últimas ventas atribuidas (token)
 *   GET  /api/creator/withdrawals  → historial de retiros propio (token)
 *   POST /api/creator/withdrawals  → solicita retiro y reserva saldo (token)
 *
 * Reglas de negocio de retiros:
 *   - Mínimo $1.00 USD. Máximo = saldo disponible redondeado hacia abajo a centavos.
 *   - Al solicitar se RESERVA el monto (balance -= amount) atómicamente bajo
 *     el mutex de withDB → imposible gastar el mismo saldo dos veces.
 *   - Rechazo admin → reembolso exacto del monto reservado.
 */

import { Router } from 'express';
import crypto from 'crypto';
import { withDB, readDBOnce } from '../db.js';
import {
  hashPin,
  signToken,
  verifyToken,
  checkLoginAllowed,
  registerLoginFail,
  clearLoginFails,
} from '../services/creatorAuth.js';

const router = Router();

const CODE_RE = /^[A-Z0-9]{3,20}$/;
const PIN_RE = /^\d{4,8}$/;
const MIN_WITHDRAWAL = 1;
const NOTE_MAX = 200;

const roundMoney = (v) => Math.round((Number(v) + Number.EPSILON) * 100) / 100;
const floorCents = (v) => Math.floor(Number(v) * 100) / 100;

function clientKey(req, code) {
  const ip = req.ip || req.socket?.remoteAddress || 'unknown';
  return `${ip}:${String(code).toUpperCase()}`;
}

function authCreator(req) {
  const header = req.headers.authorization || '';
  const token = header.startsWith('Bearer ') ? header.slice(7) : '';
  return verifyToken(token);
}

// ─── Público: contador de clics ────────────────────────────────────────────
router.post('/api/creator/click', async (req, res) => {
  const codeUpper = String(req.body?.code || '').trim().toUpperCase();
  if (!CODE_RE.test(codeUpper)) {
    return res.status(400).json({ error: 'invalid_code', message: 'Código inválido.' });
  }

  let result;
  try {
    result = await withDB((db) => {
      db.creators ??= {};
      const creator = db.creators[codeUpper];
      if (!creator || creator.status !== 'active') {
        return { status: 404, body: { error: 'creator_not_found', message: 'El código no existe o está inactivo.' } };
      }
      creator.clicks = Number(creator.clicks || 0) + 1;
      const clicks = creator.clicks;
      const referrals = Number(creator.totalReferrals || 0);
      return {
        status: 200,
        body: { success: true, clicks, totalReferrals: referrals },
      };
    });
  } catch (_e) {
    return res.status(500).json({ error: 'db_error', message: 'No se pudo registrar el clic.' });
  }
  res.status(result.status).json(result.body);
});

// ─── Público: login código + PIN ───────────────────────────────────────────
router.post('/api/creator/login', async (req, res) => {
  const code = String(req.body?.code || '').trim().toUpperCase();
  const pin = String(req.body?.pin || '').trim();
  if (!CODE_RE.test(code) || !PIN_RE.test(pin)) {
    return res.status(400).json({ error: 'invalid_input', message: 'Formato de código o PIN inválido.' });
  }

  const key = clientKey(req, code);
  const gate = checkLoginAllowed(key);
  if (!gate.allowed) {
    return res.status(429).json({ error: 'too_many_attempts', message: `Demasiados intentos. Espera ${gate.retryInSeconds}s.` });
  }

  try {
    const db = await readDBOnce();
    const creator = db.creators?.[code];
    if (!creator) {
      registerLoginFail(key);
      return res.status(401).json({ error: 'bad_credentials', message: 'Código o PIN incorrecto.' });
    }
    if (!creator.pinHash) {
      return res.status(403).json({ error: 'pin_not_set', message: 'Tu PIN aún no fue configurado. Pídeselo al administrador.' });
    }
    if (hashPin(pin) !== creator.pinHash) {
      registerLoginFail(key);
      return res.status(401).json({ error: 'bad_credentials', message: 'Código o PIN incorrecto.' });
    }
    if (creator.status !== 'active') {
      return res.status(403).json({ error: 'inactive', message: 'Tu cuenta está desactivada. Contacta al administrador.' });
    }

    clearLoginFails(key);
    return res.json({
      success: true,
      token: signToken(code),
      profile: { code, name: creator.name || '' },
    });
  } catch (_e) {
    return res.status(500).json({ error: 'db_error', message: 'Error interno de login.' });
  }
});

// ─── Privado (Bearer): stats + ventas + retiros ────────────────────────────
function requireCreator(req, res) {
  const auth = authCreator(req);
  if (!auth) {
    res.status(401).json({ error: 'unauthorized', message: 'Sesión inválida o expirada.' });
    return null;
  }
  return auth.code;
}

router.get('/api/creator/me', async (req, res) => {
  const code = requireCreator(req, res);
  if (!code) return;

  try {
    const db = await readDBOnce();
    const creator = db.creators?.[code];
    if (!creator) return res.status(404).json({ error: 'not_found', message: 'Creador no encontrado.' });

    // Métricas derivadas de órdenes reales
    let lifetimeEarned = 0;
    let pendingEarned = 0;
    for (const order of db.orders || []) {
      if (order.affiliate?.code !== code) continue;
      const commission = Number(order.affiliate.commission || 0);
      if (order.status === 'completed') lifetimeEarned += commission;
      else if (order.status === 'pending') pendingEarned += commission;
    }
    lifetimeEarned = roundMoney(lifetimeEarned);
    pendingEarned = roundMoney(pendingEarned);

    const myWithdrawals = (db.withdrawals || []).filter((w) => w.code === code);
    const reservedPending = roundMoney(
      myWithdrawals.filter((w) => w.status === 'pending').reduce((s, w) => s + Number(w.amount || 0), 0),
    );

    const balanceRaw = roundMoney(creator.balance || 0);
    const clicks = Number(creator.clicks || 0);
    const referrals = Number(creator.totalReferrals || 0);

    // Serie diaria de comisiones cobradas (últimos 14 días) para la gráfica.
    const byDay = new Map();
    for (const order of db.orders || []) {
      if (order.affiliate?.code !== code || order.status !== 'completed') continue;
      const day = String(order.completedAt || order.createdAt || '').slice(0, 10);
      if (!day) continue;
      byDay.set(day, roundMoney((byDay.get(day) || 0) + Number(order.affiliate.commission || 0)));
    }
    const dailyEarnings = [];
    for (let i = 13; i >= 0; i--) {
      const d = new Date();
      d.setDate(d.getDate() - i);
      const key = d.toISOString().slice(0, 10);
      dailyEarnings.push({ date: key, amount: byDay.get(key) || 0 });
    }

    return res.json({
      success: true,
      profile: { code, name: creator.name || '', status: creator.status },
      // El saldo YA está neto: las solicitudes pendientes descontaron al crearse.
      balance: balanceRaw,
      availableForWithdrawal: floorCents(balanceRaw),
      clicks,
      totalReferrals: referrals,
      conversionRate: clicks > 0 ? `${((referrals / clicks) * 100).toFixed(1)}%` : null,
      commissionBonusPercent: Number(creator.commissionBonusPercent ?? 7),
      commissionNormalPercent: Number(creator.commissionNormalPercent ?? 5),
      commissionComboPercent: Number(creator.commissionComboPercent ?? 3),
      lifetimeEarned,
      pendingEarned,
      pendingWithdrawalSum: reservedPending,
      dailyEarnings,
    });
  } catch (_e) {
    return res.status(500).json({ error: 'db_error', message: 'Error al leer tus datos.' });
  }
});

router.get('/api/creator/sales', async (req, res) => {
  const code = requireCreator(req, res);
  if (!code) return;

  try {
    const db = await readDBOnce();
    const sales = (db.orders || [])
      .filter((o) => o.affiliate?.code === code)
      .slice(0, 30)
      .map((o) => ({
        id: o.id,
        createdAt: o.createdAt,
        status: o.status,
        priceInfo: o.priceInfo || '',
        itemsSummary: Array.isArray(o.items) && o.items.length > 0
          ? o.items
              .map((it) => `${it.packageSize || String(it.packageId).replace('pkg-', '')}💎${it.isDiscounted ? '(B)' : ''}`)
              .join(' + ')
          : `${o.packageSize || ''}💎`.trim(),
        commission: roundMoney(o.affiliate.commission || 0),
      }));
    return res.json({ success: true, sales });
  } catch (_e) {
    return res.status(500).json({ error: 'db_error', message: 'Error al leer tus ventas.' });
  }
});

router.get('/api/creator/withdrawals', async (req, res) => {
  const code = requireCreator(req, res);
  if (!code) return;

  try {
    const db = await readDBOnce();
    const withdrawals = (db.withdrawals || [])
      .filter((w) => w.code === code)
      .sort((a, b) => String(b.createdAt).localeCompare(String(a.createdAt)));
    return res.json({ success: true, withdrawals });
  } catch (_e) {
    return res.status(500).json({ error: 'db_error', message: 'Error al leer tus retiros.' });
  }
});

router.post('/api/creator/withdrawals', async (req, res) => {
  const code = requireCreator(req, res);
  if (!code) return;

  const amount = Math.round(Number(req.body?.amount) * 100) / 100;
  const note = String(req.body?.note || '').trim().slice(0, NOTE_MAX);

  if (!Number.isFinite(amount) || amount < MIN_WITHDRAWAL) {
    return res.status(400).json({ error: 'invalid_amount', message: `El monto mínimo es $${MIN_WITHDRAWAL.toFixed(2)} USD.` });
  }
  if (Math.round(amount * 100) !== Math.round(Number(req.body?.amount) * 100)) {
    return res.status(400).json({ error: 'invalid_amount', message: 'Monto con demasiados decimales.' });
  }

  let result;
  try {
    result = await withDB((db) => {
      db.creators ??= {};
      db.withdrawals ??= [];
      const creator = db.creators[code];
      if (!creator) return { status: 404, body: { error: 'not_found', message: 'Creador no encontrado.' } };
      if (creator.status !== 'active') {
        return { status: 403, body: { error: 'inactive', message: 'Tu cuenta está desactivada.' } };
      }

      // El saldo ya está neto de reservas (se descuenta al crear cada solicitud).
      const available = floorCents(roundMoney(creator.balance || 0));
      if (available <= 0) {
        return { status: 400, body: { error: 'no_funds', message: 'No tienes saldo disponible para retirar.' } };
      }
      if (amount > available) {
        return { status: 400, body: { error: 'insufficient_funds', message: `Solo tienes $${available.toFixed(2)} disponibles.` } };
      }

      // Reserva atómica
      creator.balance = roundMoney(Number(creator.balance || 0) - amount);

      const withdrawal = {
        id: `WD-${Date.now().toString(36)}-${crypto.randomUUID().slice(0, 8)}`,
        code,
        amount: roundMoney(amount),
        note,
        status: 'pending',
        createdAt: new Date().toISOString(),
        processedAt: null,
      };
      db.withdrawals.push(withdrawal);

      return { status: 200, body: { success: true, withdrawal, newBalance: creator.balance } };
    });
  } catch (_e) {
    return res.status(500).json({ error: 'db_error', message: 'No se pudo registrar el retiro.' });
  }
  res.status(result.status).json(result.body);
});

export default router;
