// Customer lookup for the phone app (acot-phone-server).
//
// All queries hit the MySQL `sg` database via the shared SSH-tunneled pool in
// db/connection.js. The stats/orders logic mirrors the freescout
// ACOTCustomerData module so both apps display the same numbers for a given
// customer — the difference is that we key by phone, not email.
//
// NOTE: `users.phone` is not yet indexed in production. Admin will add
// `idx_phone (phone)` — queries here assume that exists for acceptable latency.

import express from 'express';
import { getDbConnection, getCachedQuery } from '../db/connection.js';
import { requirePhoneApiKey } from '../utils/phoneAuth.js';

const router = express.Router();

// Order status labels mirror ACOTCustomerDataServiceProvider.php.
const ORDER_STATUS_LABEL = {
  0: 'Created', 10: 'Incomplete', 15: 'Cancelled', 16: 'Combined',
  20: 'Placed', 22: 'Placed (Incomplete)', 40: 'Awaiting Payment',
  45: 'Payment Pending', 50: 'Awaiting Products', 55: 'Shipping Later',
  56: 'Shipping Together', 60: 'Ready', 61: 'Flagged', 62: 'Fix Before Pick',
  65: 'Manual Picking', 67: 'Remote Send', 70: 'In PT', 80: 'Picked',
  90: 'Awaiting Shipment', 91: 'Remote Wait', 92: 'Awaiting Pickup',
  93: 'Fix Before Ship', 95: 'Shipped (Confirmed)', 100: 'Shipped',
};
const ORDER_STATUS_SHORT = {
  0: 'Created', 10: 'Incomplete', 15: 'Cancelled', 16: 'Combined',
  20: 'Placed', 22: 'Plcd Incomp', 40: 'Await Payment', 45: 'Pymt Pending',
  50: 'Await Products', 55: 'Ship Later', 56: 'Ship Togethr', 60: 'Ready',
  61: 'Flagged', 62: 'Fix Bfr Pick', 65: 'Manual Pick', 67: 'Remote Send',
  70: 'In PT', 80: 'Picked', 90: 'Await Ship', 91: 'Remote Wait',
  92: 'Await Pickup', 93: 'Fix Bfr Ship', 95: 'Shpd Confirm', 100: 'Shipped',
};

function statusLabel(s) { return ORDER_STATUS_LABEL[s] ?? `Unknown (${s})`; }
function statusShort(s) { return ORDER_STATUS_SHORT[s] ?? `Unknown (${s})`; }

// SIP trunks and historical CRM imports all disagree on phone format. Rather
// than normalize everything upstream, we search across the most common
// variations for US/Canada numbers. Falls through to the raw input for
// international numbers we can't safely reformat.
function phoneVariations(input) {
  const raw = String(input || '').trim();
  if (!raw) return [];
  const digits = raw.replace(/\D/g, '');
  const out = new Set([raw, digits]);
  if (digits.length === 10) {
    out.add(`+1${digits}`);
    out.add(`1${digits}`);
  } else if (digits.length === 11 && digits.startsWith('1')) {
    out.add(`+${digits}`);
    out.add(digits.slice(1)); // 10-digit form
    out.add(`+1${digits.slice(1)}`);
  }
  return Array.from(out).filter(Boolean);
}

function trackingLink(method, tracking) {
  if (!tracking) return '';
  if (typeof method === 'string') {
    if (method.startsWith('usps_') || method === 'fedex_smartpost') {
      return `https://tools.usps.com/go/TrackConfirmAction?qtc_tLabels1=${tracking}`;
    }
    if (method.startsWith('fedex_')) {
      return `https://www.fedex.com/fedextrack/?trknbr=${tracking}`;
    }
  }
  return '';
}

// Matches ACOTCustomerDataServiceProvider::imageUrl — sbing.com/i/products/<dir1>/<dir2>/<pid>-t-<iid>.jpg
function imageUrl(pid, iid = 1) {
  const padded = String(pid).padStart(10, '0');
  const dir1 = padded.slice(0, 4);
  const dir2 = padded.slice(4, 7);
  return `https://sbing.com/i/products/${dir1}/${dir2}/${pid}-t-${iid}.jpg`;
}

router.use(requirePhoneApiKey);

// ── GET /by-phone ──────────────────────────────────────────────────────────
// Returns top-line customer info for the incoming-call overlay.
router.get('/by-phone', async (req, res) => {
  const phone = String(req.query.phone || '').trim();
  if (!phone) return res.status(400).json({ success: false, error: 'phone required' });

  const variations = phoneVariations(phone);
  if (variations.length === 0) return res.json({ success: true, customer: null });

  try {
    const data = await getCachedQuery(
      `customer-by-phone:${variations.join('|')}`,
      'default',
      async () => {
        const { connection, release } = await getDbConnection();
        try {
          const placeholders = variations.map(() => '?').join(',');
          // Tie-break by highest LTV per user instructions: subquery computes LTV
          // for every matching user, then we pick the biggest.
          const [users] = await connection.execute(
            `SELECT u.cid, u.uid, u.firstname, u.lastname, u.email, u.phone, u.points,
                    COALESCE((
                      SELECT SUM(summary_total)
                      FROM _order
                      WHERE order_cid = u.cid AND order_status >= 50
                    ), 0) AS lifetime_value,
                    COALESCE((
                      SELECT COUNT(*)
                      FROM _order
                      WHERE order_cid = u.cid AND order_status >= 20
                    ), 0) AS num_orders,
                    (
                      SELECT AVG(summary_total)
                      FROM _order
                      WHERE order_cid = u.cid AND order_status >= 20
                    ) AS avg_order
             FROM users u
             WHERE u.phone IN (${placeholders})
             ORDER BY lifetime_value DESC
             LIMIT 1`,
            variations
          );
          return users[0] ?? null;
        } finally {
          release();
        }
      }
    );

    if (!data) return res.json({ success: true, customer: null });
    res.json({
      success: true,
      customer: {
        cid: Number(data.cid),
        uid: data.uid,
        firstName: data.firstname || null,
        lastName: data.lastname || null,
        email: data.email || null,
        phone: data.phone,
        points: Number(data.points) || 0,
        lifetimeValue: Number(data.lifetime_value) || 0,
        orderCount: Number(data.num_orders) || 0,
        avgOrderValue: data.avg_order != null ? Number(data.avg_order) : 0,
      },
    });
  } catch (err) {
    console.error('customers/by-phone failed:', err);
    res.status(500).json({ success: false, error: 'query_failed' });
  }
});

// ── GET /search ────────────────────────────────────────────────────────────
// Name search for the dialer. Accepts a free-text query; splits on whitespace.
//  - 1 token: LIKE against firstname OR lastname (prefix).
//  - 2+ tokens: firstname LIKE A% AND lastname LIKE B% (order-sensitive on purpose).
router.get('/search', async (req, res) => {
  const q = String(req.query.q || '').trim();
  const limit = Math.min(Math.max(parseInt(req.query.limit || '10', 10) || 10, 1), 25);
  if (q.length < 2) return res.json({ success: true, results: [] });

  try {
    const data = await getCachedQuery(
      `customer-search:${q}:${limit}`,
      'default',
      async () => {
        const { connection, release } = await getDbConnection();
        try {
          const tokens = q.split(/\s+/).filter(Boolean);
          let sql;
          let params;
          if (tokens.length === 1) {
            const pattern = `${tokens[0]}%`;
            sql = `SELECT cid, firstname, lastname, email, phone
                   FROM users
                   WHERE (firstname LIKE ? OR lastname LIKE ?)
                     AND phone <> ''
                   ORDER BY lastname, firstname
                   LIMIT ?`;
            params = [pattern, pattern, limit];
          } else {
            const firstPat = `${tokens[0]}%`;
            const lastPat = `${tokens.slice(1).join(' ')}%`;
            sql = `SELECT cid, firstname, lastname, email, phone
                   FROM users
                   WHERE firstname LIKE ? AND lastname LIKE ?
                     AND phone <> ''
                   ORDER BY lastname, firstname
                   LIMIT ?`;
            params = [firstPat, lastPat, limit];
          }
          const [rows] = await connection.execute(sql, params);
          return rows;
        } finally {
          release();
        }
      }
    );

    res.json({
      success: true,
      results: data.map((r) => ({
        cid: Number(r.cid),
        firstName: r.firstname || null,
        lastName: r.lastname || null,
        email: r.email || null,
        phone: r.phone,
      })),
    });
  } catch (err) {
    console.error('customers/search failed:', err);
    res.status(500).json({ success: false, error: 'query_failed' });
  }
});

// ── GET /:cid/orders ───────────────────────────────────────────────────────
// Recent orders for the active-call screen — mirrors the freescout sidebar.
router.get('/:cid/orders', async (req, res) => {
  const cid = Number(req.params.cid);
  if (!Number.isFinite(cid) || cid <= 0) {
    return res.status(400).json({ success: false, error: 'bad_cid' });
  }

  try {
    const data = await getCachedQuery(
      `customer-orders:${cid}`,
      'orders',
      async () => {
        const { connection, release } = await getDbConnection();
        try {
          // MySQL-safe equivalent of the Laravel query in the freescout module.
          // Active = placed OR shipped within the last 3 months.
          // NOW() here is Central (server TZ) — intended: this is a rough
          // trailing window, not a business-day boundary.
          const [ordersRaw] = await connection.execute(
            `SELECT order_id, order_status, order_type, summary_total,
                    date_placed, ship_method_type, ship_method_tracking,
                    CASE
                      WHEN (order_status BETWEEN 20 AND 92
                            OR date_shipped > DATE_SUB(NOW(), INTERVAL 3 MONTH))
                      THEN 1 ELSE 0
                    END AS _is_active
             FROM _order
             WHERE order_cid = ?
               AND (order_status >= 20
                    OR date_shipped > DATE_SUB(NOW(), INTERVAL 3 MONTH))
             ORDER BY _is_active DESC, date_placed DESC`,
            [cid]
          );

          const active = ordersRaw.filter((o) => o._is_active === 1);
          const inactive = ordersRaw.filter((o) => o._is_active === 0);
          const orders = active.concat(inactive.slice(0, Math.max(0, 10 - active.length)));

          if (orders.length === 0) return [];

          const orderIds = orders.map((o) => o.order_id);
          const idPlaceholders = orderIds.map(() => '?').join(',');

          const [items] = await connection.execute(
            `SELECT order_id, prod_pid, prod_itemnumber, prod_description, prod_price, qty_ordered
             FROM order_items
             WHERE order_id IN (${idPlaceholders})`,
            orderIds
          );

          // Main-image lookup: per-pid highest \`order\` at type=3 (matches the
          // freescout module's raw SQL).
          const pids = [...new Set(items.map((i) => Number(i.prod_pid)).filter(Boolean))];
          const mainImagesByPid = new Map();
          if (pids.length > 0) {
            const pidList = pids.join(',');
            const [imgRows] = await connection.execute(
              `SELECT pi.pid, pi.iid
               FROM product_images pi
               INNER JOIN (
                 SELECT pid, MAX(\`order\`) AS max_order
                 FROM product_images
                 WHERE pid IN (${pidList}) AND type = 3
                 GROUP BY pid
               ) pm ON pi.pid = pm.pid AND pi.\`order\` = pm.max_order AND pi.type = 3`
            );
            for (const r of imgRows) mainImagesByPid.set(Number(r.pid), Number(r.iid));
          }

          const itemsByOrder = new Map();
          for (const it of items) {
            const oid = Number(it.order_id);
            if (!itemsByOrder.has(oid)) itemsByOrder.set(oid, []);
            const iid = mainImagesByPid.get(Number(it.prod_pid)) ?? 1;
            itemsByOrder.get(oid).push({
              pid: Number(it.prod_pid),
              sku: it.prod_itemnumber || null,
              name: it.prod_description || null,
              price: Number(it.prod_price) || 0,
              quantity: Number(it.qty_ordered) || 0,
              imageUrl: imageUrl(it.prod_pid, iid),
            });
          }

          return orders.map((o) => ({
            orderId: Number(o.order_id),
            datePlaced: o.date_placed,
            total: Number(o.summary_total) || 0,
            status: Number(o.order_status),
            statusLabel: statusLabel(Number(o.order_status)),
            statusShort: statusShort(Number(o.order_status)),
            trackingNumber: o.ship_method_tracking || '',
            trackingUrl: trackingLink(o.ship_method_type, o.ship_method_tracking),
            items: itemsByOrder.get(Number(o.order_id)) || [],
          }));
        } finally {
          release();
        }
      }
    );

    res.json({ success: true, orders: data });
  } catch (err) {
    console.error('customers/:cid/orders failed:', err);
    res.status(500).json({ success: false, error: 'query_failed' });
  }
});

export default router;
