import { Router } from 'express';
import { readDB, withDB } from '../db.js';
import { computeOrderPrice, formatAmount, normalizePackageId, computeLevelOrderItem } from '../services/pricing.js';
import { isAdminAuthenticated, requireAdmin } from '../middleware/adminAuth.js';

const router = Router();
const MAX_CART_ITEMS = 50;
const MAX_OPEN_ORDERS = 5;

// Una orden esta "abierta" mientras no se marque como pagada/completada ni se cancele.
const isOpen = (o) => o.status === 'pending';

function roundMoney(value) {
  return Math.round((Number(value) + Number.EPSILON) * 100) / 100;
}

function getOrderItems(order) {
  if (Array.isArray(order.items) && order.items.length > 0) return order.items;
  if (!order.packageId) return [];
  return [{
    lineIndex: 0,
    packageId: normalizePackageId(order.packageId),
    packageSize: order.packageSize,
    expectedAmount: order.expectedAmount,
    expectedCurrency: order.expectedCurrency,
    priceInfo: order.priceInfo,
    priceField: order.priceField,
    priceSource: order.priceSource,
    baseUsd: order.baseUsd,
    discountPercent: order.discountPercent,
    profitPercent: order.profitPercent,
    isDiscounted: Boolean(order.isDiscounted),
    garenaDiscountAvailable: Boolean(order.garenaDiscountAvailable),
    purchasedThroughUs: Boolean(order.purchasedThroughUs),
    normalPriceReason: order.normalPriceReason ?? null,
  }];
}

function getRequestedItems(reqBody) {
  if (Array.isArray(reqBody.items)) {
    return reqBody.items
      .map(item => normalizePackageId(item?.packageId || item?.id || item))
      .filter(Boolean);
  }
  if (reqBody.packageId) return [normalizePackageId(reqBody.packageId)];
  return [];
}

function buildPackageSummary(items) {
  return items.map(item => `${item.packageSize || String(item.packageId).replace('pkg-', '')}`).join(' + ');
}

function getReservedDiscountPackages(openOrders) {
  const reserved = new Set();
  for (const order of openOrders) {
    for (const item of getOrderItems(order)) {
      if (item.isDiscounted) reserved.add(normalizePackageId(item.packageId));
    }
  }
  return reserved;
}

function applyItemPricing(order, item, isDiscounted, normalPriceReason) {
  const computedPrice = computeOrderPrice({ packageId: item.packageId, paymentMethod: order.paymentMethod, isDiscounted });
  if (computedPrice.error) return computedPrice;

  Object.assign(item, {
    packageId: computedPrice.packageId,
    packageSize: computedPrice.packageSize,
    expectedAmount: computedPrice.expectedAmount,
    expectedCurrency: computedPrice.expectedCurrency,
    priceInfo: computedPrice.priceInfo,
    priceField: computedPrice.priceField,
    priceSource: computedPrice.priceSource,
    baseUsd: computedPrice.baseUsd,
    discountPercent: computedPrice.discountPercent,
    profitPercent: computedPrice.profitPercent,
    isDiscounted,
    bonusUsed: !isDiscounted,
    normalPriceReason,
  });

  return null;
}

function syncOrderTotals(order) {
  const items = getOrderItems(order);
  const expectedAmount = roundMoney(items.reduce((sum, item) => sum + Number(item.expectedAmount || 0), 0));
  const expectedCurrency = items[0]?.expectedCurrency;
  const packageSummary = buildPackageSummary(items);
  const firstItem = items[0] || {};

  order.items = items;
  order.itemCount = items.length;
  order.packageSummary = packageSummary ? `${packageSummary} diamantes` : '';
  order.priceInfo = `${formatAmount(expectedAmount)} ${expectedCurrency}`;
  order.expectedAmount = expectedAmount;
  order.expectedCurrency = expectedCurrency;
  order.packageId = firstItem.packageId;
  order.packageSize = items.reduce((sum, item) => sum + Number(item.packageSize || 0), 0);
  order.baseUsd = roundMoney(items.reduce((sum, item) => sum + Number(item.baseUsd || 0), 0));
  order.discountPercent = firstItem.discountPercent;
  order.profitPercent = firstItem.profitPercent;
  order.isDiscounted = items.some(item => item.isDiscounted);
  order.bonusUsed = !order.isDiscounted;
  order.garenaDiscountAvailable = items.some(item => item.garenaDiscountAvailable);
  order.purchasedThroughUs = items.length > 0 && items.every(item => item.purchasedThroughUs);
  order.normalPriceReason = items.find(item => item.normalPriceReason)?.normalPriceReason || null;
}

function recalculatePlayerOpenOrderDiscounts(db, playerId) {
  const user = db.users[playerId] || { discountedPackages: [], purchasedPackages: [] };
  const discountedPackages = (user.discountedPackages || []).map(normalizePackageId);
  const purchasedPackages = (user.purchasedPackages || []).map(normalizePackageId);
  const usedDiscountPackages = new Set();

  const openOrders = db.orders
    .map((order, orderIndex) => ({ order, orderIndex }))
    .filter(({ order }) => order.playerId === playerId && isOpen(order))
    .sort((a, b) => {
      const aTime = new Date(a.order.createdAt || 0).getTime();
      const bTime = new Date(b.order.createdAt || 0).getTime();
      return (aTime - bTime) || (a.orderIndex - b.orderIndex);
    });

  for (const { order } of openOrders) {
    const discountedInThisOrder = new Set();
    const items = getOrderItems(order).map((item, lineIndex) => ({ ...item, lineIndex }));
    order.items = items;

    for (const item of items) {
      // Los paquetes de nivel tienen precio fijo: no participan del recálculo de bonos.
      if (String(item.packageId).startsWith('lvl-')) continue;
      const packageId = normalizePackageId(item.packageId);
      const hasGarenaDiscount = discountedPackages.includes(packageId);
      const purchasedThroughUs = purchasedPackages.includes(packageId);
      const alreadyUsedInOpenOrders = usedDiscountPackages.has(packageId);
      const isDiscounted = hasGarenaDiscount && !purchasedThroughUs && !alreadyUsedInOpenOrders;
      const normalPriceReason = !hasGarenaDiscount
        ? 'garena_unavailable'
        : purchasedThroughUs
          ? 'purchased_through_us'
          : alreadyUsedInOpenOrders
            ? discountedInThisOrder.has(packageId) ? 'duplicate_in_cart' : 'reserved_open_order'
            : null;

      const error = applyItemPricing(order, item, isDiscounted, normalPriceReason);
      if (error) return error;

      item.garenaDiscountAvailable = hasGarenaDiscount;
      item.purchasedThroughUs = purchasedThroughUs;
      if (isDiscounted) {
        usedDiscountPackages.add(packageId);
        discountedInThisOrder.add(packageId);
      }
    }

    syncOrderTotals(order);
  }

  return null;
}

// Crear una nueva orden multi-producto.
// ⚠️ Toda la lógica (incluidas las validaciones de límites) va dentro de withDB
// para que dos POST concurrentes no superen MAX_OPEN_ORDERS: si las
// validaciones se hicieran fuera del lock, ambos verían "tengo <2 órdenes"
// y ambos entrarían.
router.post('/api/orders', async (req, res) => {
  let result;
  try {
    result = await withDB(db => {
      const { playerId, paymentMethod } = req.body;
      const requestedItems = getRequestedItems(req.body);

      if (!playerId || !paymentMethod || requestedItems.length === 0) {
        return { status: 400, body: { error: 'missing_fields', message: 'Faltan datos de la orden.' } };
      }

      if (requestedItems.length > MAX_CART_ITEMS) {
        return { status: 400, body: { error: 'cart_limit', message: `El carrito permite maximo ${MAX_CART_ITEMS} productos.` } };
      }

      const openOrders = db.orders.filter(order => order.playerId === playerId && isOpen(order));
      if (openOrders.length >= MAX_OPEN_ORDERS) {
        return { status: 409, body: {
          error: 'open_order_limit',
          message: `Solo puedes tener hasta ${MAX_OPEN_ORDERS} ordenes abiertas. Completa o cancela una antes de crear otra.`,
        } };
      }

      const user = db.users[playerId] || { discountedPackages: [], purchasedPackages: [] };
      const discountedPackages = (user.discountedPackages || []).map(normalizePackageId);
      const purchasedPackages = (user.purchasedPackages || []).map(normalizePackageId);
      const reservedDiscountPackages = getReservedDiscountPackages(openOrders);
      const cartDiscountPackages = new Set();
      const items = [];

      for (const [lineIndex, packageId] of requestedItems.entries()) {
        // ── Paquete de Aumento de Nivel: precio FIJO, sin bonos ni descuentos ──
        if (String(packageId).startsWith('lvl-')) {
          const levelItem = computeLevelOrderItem(packageId);
          if (levelItem.error) {
            return { status: 400, body: { error: 'invalid_package', message: 'Paquete de nivel invalido.' } };
          }
          items.push({ lineIndex, ...levelItem });
          continue;
        }

        const hasGarenaDiscount = discountedPackages.includes(packageId);
        const purchasedThroughUs = purchasedPackages.includes(packageId);
        const reservedOpenOrder = reservedDiscountPackages.has(packageId);
        const duplicateInCart = cartDiscountPackages.has(packageId);
        const isDiscounted = hasGarenaDiscount && !purchasedThroughUs && !reservedOpenOrder && !duplicateInCart;

        const computedPrice = computeOrderPrice({ packageId, paymentMethod, isDiscounted });
        if (computedPrice.error === 'invalid_package') {
          return { status: 400, body: { error: 'invalid_package', message: 'Paquete invalido.' } };
        }
        if (computedPrice.error === 'invalid_payment_method') {
          return { status: 400, body: { error: 'invalid_payment_method', message: 'Metodo de pago invalido.' } };
        }
        if (computedPrice.error === 'invalid_price_config') {
          return { status: 500, body: { error: 'invalid_price_config', message: 'Precio mal configurado.' } };
        }

        if (isDiscounted) cartDiscountPackages.add(packageId);

        const normalPriceReason = !hasGarenaDiscount
          ? 'garena_unavailable'
          : purchasedThroughUs
            ? 'purchased_through_us'
            : reservedOpenOrder
              ? 'reserved_open_order'
              : duplicateInCart
                ? 'duplicate_in_cart'
                : null;

        items.push({
          lineIndex,
          packageId: computedPrice.packageId,
          packageSize: computedPrice.packageSize,
          expectedAmount: computedPrice.expectedAmount,
          expectedCurrency: computedPrice.expectedCurrency,
          priceInfo: computedPrice.priceInfo,
          priceField: computedPrice.priceField,
          priceSource: computedPrice.priceSource,
          baseUsd: computedPrice.baseUsd,
          discountPercent: computedPrice.discountPercent,
          profitPercent: computedPrice.profitPercent,
          isDiscounted,
          bonusUsed: !isDiscounted,
          garenaDiscountAvailable: hasGarenaDiscount,
          purchasedThroughUs,
          normalPriceReason,
        });
      }

      const currencies = new Set(items.map(item => item.expectedCurrency));
      if (currencies.size !== 1) {
        return { status: 400, body: { error: 'mixed_currency', message: 'La orden no puede mezclar monedas.' } };
      }

      const expectedAmount = roundMoney(items.reduce((sum, item) => sum + Number(item.expectedAmount), 0));
      const expectedCurrency = items[0].expectedCurrency;
      const packageSummary = buildPackageSummary(items);
      const firstItem = items[0];

      const order = {
        id: 'ORD-' + Math.floor(1000 + Math.random() * 9000),
        playerId,
        paymentMethod,
        items,
        itemCount: items.length,
        packageSummary: `${packageSummary} diamantes`,
        priceInfo: `${formatAmount(expectedAmount)} ${expectedCurrency}`,
        expectedAmount,
        expectedCurrency,
        packageId: firstItem.packageId,
        packageSize: items.reduce((sum, item) => sum + Number(item.packageSize || 0), 0),
        baseUsd: roundMoney(items.reduce((sum, item) => sum + Number(item.baseUsd || 0), 0)),
        discountPercent: firstItem.discountPercent,
        profitPercent: firstItem.profitPercent,
        isDiscounted: items.some(item => item.isDiscounted),
        bonusUsed: !items.some(item => item.isDiscounted),
        garenaDiscountAvailable: items.some(item => item.garenaDiscountAvailable),
        purchasedThroughUs: items.every(item => item.purchasedThroughUs),
        normalPriceReason: items.find(item => item.normalPriceReason)?.normalPriceReason || null,
        status: 'pending',
        createdAt: new Date().toISOString(),
      };

      // Affiliate logic (comisiones configurables por creador)
      const { referralCode } = req.body;
      if (referralCode) {
        const codeUpper = String(referralCode).trim().toUpperCase();
        const creator = db.creators?.[codeUpper];
        if (creator && creator.status === 'active') {
          const bonusRate = Number(creator.commissionBonusPercent ?? 7) / 100;
          const normalRate = Number(creator.commissionNormalPercent ?? 5) / 100;
          const comboRate = Number(creator.commissionComboPercent ?? 3) / 100;
          const commission = roundMoney(items.reduce((sum, item) => {
            const size = Number(item.packageSize || String(item.packageId).replace('pkg-', ''));
            const isCombo = ![110, 341, 572, 1166, 2398, 6160].includes(size);
            const rate = isCombo ? comboRate : (item.isDiscounted ? bonusRate : normalRate);
            return sum + (Number(item.expectedAmount) * rate);
          }, 0));

          order.affiliate = {
            code: codeUpper,
            commission
          };
        }
      }

      db.orders.unshift(order);
      const recalculationError = recalculatePlayerOpenOrderDiscounts(db, playerId);
      if (recalculationError?.error === 'invalid_price_config') {
        return { status: 500, body: { error: 'invalid_price_config', message: 'Precio mal configurado.' } };
      }
      return { status: 200, body: { success: true, order } };
    });
  } catch (e) {
    return res.status(500).json({ error: 'db_error', message: 'No se pudo crear la orden.' });
  }
  res.status(result.status).json(result.body);
});

// Listar ordenes (las canceladas se eliminan, asi que no aparecen).
router.get('/api/orders', (req, res) => {
  const orders = readDB().orders;
  const playerId = String(req.query.playerId || '').trim();
  if (playerId) return res.json(orders.filter(order => order.playerId === playerId));
  if (!isAdminAuthenticated(req)) {
    return res.status(401).json({ error: 'admin_auth_required', message: 'Debes iniciar sesion como admin.' });
  }
  res.json(orders);
});

// El usuario solo notifica que enviara comprobante; no cambia estado de pago.
router.patch('/api/orders/:id/pay', async (req, res) => {
  let result;
  try {
    result = await withDB(db => {
      const order = db.orders.find(o => o.id === req.params.id);
      if (!order) return { status: 404, body: { error: 'Order not found' } };
      if (order.status === 'pending') {
        order.userPaymentNotifiedAt = new Date().toISOString();
        order.userPaymentNoticeCount = Number(order.userPaymentNoticeCount || 0) + 1;
        recalculatePlayerOpenOrderDiscounts(db, order.playerId);
      }
      return { status: 200, body: { success: true, order } };
    });
  } catch (e) {
    return res.status(500).json({ error: 'db_error', message: 'No se pudo actualizar la orden.' });
  }
  res.status(result.status).json(result.body);
});

// El admin valida la orden -> completada y consume solo los bonos usados con descuento.
router.patch('/api/orders/:id/complete', requireAdmin, async (req, res) => {
  let result;
  try {
    result = await withDB(db => {
      const order = db.orders.find(o => o.id === req.params.id);
      if (!order) return { status: 404, body: { error: 'Order not found' } };

      if (order.status !== 'completed') {
        const recalculationError = recalculatePlayerOpenOrderDiscounts(db, order.playerId);
        if (recalculationError?.error) {
          return { status: 500, body: { error: 'invalid_price_config', message: 'Precio mal configurado.' } };
        }

        const completedAt = new Date().toISOString();
        order.status = 'completed';
        order.completedAt = completedAt;

        const user = db.users[order.playerId] || (db.users[order.playerId] = { nickname: null, discountedPackages: [], purchasedPackages: [] });
        user.purchasedPackages = (user.purchasedPackages || []).map(normalizePackageId);

        for (const item of getOrderItems(order)) {
          if (!item.isDiscounted) continue;
          const canonicalPackageId = normalizePackageId(item.packageId);
          if (!user.purchasedPackages.includes(canonicalPackageId)) user.purchasedPackages.push(canonicalPackageId);
          item.discountConsumedAt = completedAt;
        }

        // Aplicar comisión al creador si existe
        if (order.affiliate && order.affiliate.code && db.creators && db.creators[order.affiliate.code]) {
          const creator = db.creators[order.affiliate.code];
          creator.balance = (creator.balance || 0) + (order.affiliate.commission || 0);
          creator.totalReferrals = (creator.totalReferrals || 0) + 1;
        }

        recalculatePlayerOpenOrderDiscounts(db, order.playerId);
      }
      return { status: 200, body: { success: true, order } };
    });
  } catch (e) {
    return res.status(500).json({ error: 'db_error', message: 'No se pudo completar la orden.' });
  }
  res.status(result.status).json(result.body);
});

// Cancelar = eliminar la orden. Si no fue completada, no consume descuentos.
// SOLO el admin puede cancelar/eliminar órdenes (el usuario no tiene esa vía).
router.delete('/api/orders/:id', requireAdmin, async (req, res) => {
  let result;
  try {
    result = await withDB(db => {
      const order = db.orders.find(o => o.id === req.params.id);
      if (!order) return { status: 404, body: { error: 'Order not found' } };
      if (order.status === 'completed') {
        return { status: 409, body: { error: 'completed', message: 'No se puede cancelar una orden ya completada.' } };
      }
      const playerId = order.playerId;
      db.orders = db.orders.filter(o => o.id !== req.params.id);
      recalculatePlayerOpenOrderDiscounts(db, playerId);
      return { status: 200, body: { success: true } };
    });
  } catch (e) {
    return res.status(500).json({ error: 'db_error', message: 'No se pudo cancelar la orden.' });
  }
  res.status(result.status).json(result.body);
});

export default router;
