import express from 'express';
import { DateTime } from 'luxon';
import { getDbConnection, getPoolStatus } from '../db/connection.js';
import {
  getTimeRangeConditions,
  customRange,
  previousRange,
  parseMySql,
  BUSINESS_TZ,
} from '../../../shared/business-time/index.js';

const router = express.Router();

const TIMEZONE = 'America/New_York';

// timeclock.TimeStamp stores Central wall-clock literals; the driver returns
// them as strings (dateStrings: true). Parse explicitly in business time.
const punchTime = (punch) => {
  const dt = parseMySql(punch.TimeStamp);
  return dt ? dt.toJSDate() : new Date(NaN);
};

// Punch types from the database
const PUNCH_TYPES = {
  OUT: 0,
  IN: 1,
  BREAK_START: 2,
  BREAK_END: 3,
};

// Standard hours for FTE calculation (40 hours per week)
const STANDARD_WEEKLY_HOURS = 40;

/**
 * Calculate working hours from timeclock entries
 * Groups punches by employee and date, pairs in/out punches
 * Returns both total hours (with breaks, for FTE) and productive hours (without breaks, for productivity)
 */
function calculateHoursFromPunches(punches) {
  // Group by employee
  const byEmployee = new Map();

  punches.forEach(punch => {
    if (!byEmployee.has(punch.EmployeeID)) {
      byEmployee.set(punch.EmployeeID, []);
    }
    byEmployee.get(punch.EmployeeID).push(punch);
  });

  const employeeHours = [];
  let totalHours = 0;
  let totalBreakHours = 0;

  byEmployee.forEach((employeePunches, employeeId) => {
    // Sort by timestamp
    employeePunches.sort((a, b) => punchTime(a) - punchTime(b));

    let hours = 0;
    let breakHours = 0;
    let currentIn = null;
    let breakStart = null;

    employeePunches.forEach(punch => {
      const punchAt = punchTime(punch);

      switch (punch.PunchType) {
        case PUNCH_TYPES.IN:
          currentIn = punchAt;
          break;
        case PUNCH_TYPES.OUT:
          if (currentIn) {
            hours += (punchAt - currentIn) / (1000 * 60 * 60); // Convert ms to hours
            currentIn = null;
          }
          break;
        case PUNCH_TYPES.BREAK_START:
          breakStart = punchAt;
          break;
        case PUNCH_TYPES.BREAK_END:
          if (breakStart) {
            breakHours += (punchAt - breakStart) / (1000 * 60 * 60);
            breakStart = null;
          }
          break;
      }
    });

    totalHours += hours;
    totalBreakHours += breakHours;

    employeeHours.push({
      employeeId,
      hours,
      breakHours,
      productiveHours: hours - breakHours,
    });
  });

  return {
    employeeHours,
    totalHours,
    totalBreakHours,
    totalProductiveHours: totalHours - totalBreakHours
  };
}

/**
 * Calculate FTE (Full Time Equivalents) for a period
 * @param {number} totalHours - Total hours worked
 * @param {Date} startDate - Period start
 * @param {Date} endDate - Period end
 */
function calculateFTE(totalHours, startDate, endDate) {
  const start = new Date(startDate);
  const end = new Date(endDate);
  const days = Math.max(1, (end - start) / (1000 * 60 * 60 * 24));
  const weeks = days / 7;
  const expectedHours = weeks * STANDARD_WEEKLY_HOURS;

  return expectedHours > 0 ? totalHours / expectedHours : 0;
}

// Main employee metrics endpoint
router.get('/', async (req, res) => {
  const startTime = Date.now();
  console.log(`[EMPLOYEE-METRICS] Starting request for timeRange: ${req.query.timeRange}`);

  const timeoutPromise = new Promise((_, reject) => {
    setTimeout(() => reject(new Error('Request timeout after 30 seconds')), 30000);
  });

  try {
    const mainOperation = async () => {
      const { timeRange, startDate, endDate } = req.query;
      console.log(`[EMPLOYEE-METRICS] Getting DB connection...`);
      const { connection, release } = await getDbConnection();
      console.log(`[EMPLOYEE-METRICS] DB connection obtained in ${Date.now() - startTime}ms`);

      const { whereClause, params, dateRange } = getTimeRangeConditions(timeRange, startDate, endDate);

      // Adapt where clause for timeclock table (uses TimeStamp instead of date_placed)
      const timeclockWhere = whereClause.replace(/date_placed/g, 'tc.TimeStamp');

      // Query for timeclock data with employee names
      const timeclockQuery = `
        SELECT
          tc.EmployeeID,
          tc.TimeStamp,
          tc.PunchType,
          e.firstname,
          e.lastname
        FROM timeclock tc
        LEFT JOIN employees e ON tc.EmployeeID = e.employeeid
        WHERE ${timeclockWhere}
          AND e.hidden = 0
          AND e.disabled = 0
        ORDER BY tc.EmployeeID, tc.TimeStamp
      `;

      const [timeclockRows] = await connection.execute(timeclockQuery, params);

      // Calculate hours (includes both total hours for FTE and productive hours for productivity)
      const { employeeHours, totalHours, totalBreakHours, totalProductiveHours } = calculateHoursFromPunches(timeclockRows);

      // Get employee names for the results
      const employeeNames = new Map();
      timeclockRows.forEach(row => {
        if (!employeeNames.has(row.EmployeeID)) {
          employeeNames.set(row.EmployeeID, {
            firstname: row.firstname || '',
            lastname: row.lastname || '',
          });
        }
      });

      // Enrich employee hours with names
      const enrichedEmployeeHours = employeeHours.map(eh => ({
        ...eh,
        name: employeeNames.has(eh.employeeId)
          ? `${employeeNames.get(eh.employeeId).firstname} ${employeeNames.get(eh.employeeId).lastname}`.trim()
          : `Employee ${eh.employeeId}`,
      })).sort((a, b) => b.hours - a.hours);

      // Query for picking tickets - using subquery to avoid duplication from bucket join
      // Ship-together orders: only count main orders (is_sub = 0 or NULL), not sub-orders
      const pickingWhere = whereClause.replace(/date_placed/g, 'pt.createddate');

      // First get picking ticket stats without the bucket join (to avoid duplication)
      const pickingStatsQuery = `
        SELECT
          pt.createdby as employeeId,
          e.firstname,
          e.lastname,
          COUNT(DISTINCT pt.pickingid) as ticketCount,
          SUM(pt.totalpieces_picked) as piecesPicked,
          SUM(TIMESTAMPDIFF(SECOND, pt.createddate, pt.closeddate)) as pickingTimeSeconds,
          AVG(NULLIF(pt.picking_speed, 0)) as avgPickingSpeed
        FROM picking_ticket pt
        LEFT JOIN employees e ON pt.createdby = e.employeeid
        WHERE ${pickingWhere}
          AND pt.closeddate IS NOT NULL
        GROUP BY pt.createdby, e.firstname, e.lastname
      `;

      // Separate query for order counts (needs bucket join for ship-together handling)
      const orderCountQuery = `
        SELECT
          pt.createdby as employeeId,
          COUNT(DISTINCT CASE WHEN ptb.is_sub = 0 OR ptb.is_sub IS NULL THEN ptb.orderid END) as ordersPicked
        FROM picking_ticket pt
        LEFT JOIN picking_ticket_buckets ptb ON pt.pickingid = ptb.pickingid
        WHERE ${pickingWhere}
          AND pt.closeddate IS NOT NULL
        GROUP BY pt.createdby
      `;

      const [[pickingStatsRows], [orderCountRows]] = await Promise.all([
        connection.execute(pickingStatsQuery, params),
        connection.execute(orderCountQuery, params)
      ]);

      // Merge the results
      const orderCountMap = new Map();
      orderCountRows.forEach(row => {
        orderCountMap.set(row.employeeId, parseInt(row.ordersPicked || 0));
      });

      // Aggregate picking totals
      let totalOrdersPicked = 0;
      let totalPiecesPicked = 0;
      let totalTickets = 0;
      let totalPickingTimeSeconds = 0;
      let pickingSpeedSum = 0;
      let pickingSpeedCount = 0;

      const pickingByEmployee = pickingStatsRows.map(row => {
        const ordersPicked = orderCountMap.get(row.employeeId) || 0;
        totalOrdersPicked += ordersPicked;
        totalPiecesPicked += parseInt(row.piecesPicked || 0);
        totalTickets += parseInt(row.ticketCount || 0);
        totalPickingTimeSeconds += parseInt(row.pickingTimeSeconds || 0);
        if (row.avgPickingSpeed && row.avgPickingSpeed > 0) {
          pickingSpeedSum += parseFloat(row.avgPickingSpeed);
          pickingSpeedCount++;
        }

        const empPickingHours = parseInt(row.pickingTimeSeconds || 0) / 3600;

        return {
          employeeId: row.employeeId,
          name: `${row.firstname || ''} ${row.lastname || ''}`.trim() || `Employee ${row.employeeId}`,
          ticketCount: parseInt(row.ticketCount || 0),
          ordersPicked,
          piecesPicked: parseInt(row.piecesPicked || 0),
          pickingHours: empPickingHours,
          avgPickingSpeed: row.avgPickingSpeed ? parseFloat(row.avgPickingSpeed) : null,
        };
      });

      const totalPickingHours = totalPickingTimeSeconds / 3600;
      const avgPickingSpeed = pickingSpeedCount > 0 ? pickingSpeedSum / pickingSpeedCount : 0;

      // Query for shipped orders - totals
      // Ship-together orders: only count main orders (order_type != 8 for sub-orders, or use parent tracking)
      const shippingWhere = whereClause.replace(/date_placed/g, 'o.date_shipped');

      const shippingQuery = `
        SELECT
          COUNT(DISTINCT CASE WHEN o.order_type != 8 OR o.order_type IS NULL THEN o.order_id END) as ordersShipped,
          COALESCE(SUM(o.stats_prod_pieces), 0) as piecesShipped
        FROM _order o
        WHERE ${shippingWhere}
          AND o.order_status IN (100, 92)
      `;

      const [shippingRows] = await connection.execute(shippingQuery, params);
      const shipping = shippingRows[0] || { ordersShipped: 0, piecesShipped: 0 };

      // Query for shipped orders by employee
      const shippingByEmployeeQuery = `
        SELECT
          e.employeeid,
          e.firstname,
          e.lastname,
          COUNT(DISTINCT CASE WHEN o.order_type != 8 OR o.order_type IS NULL THEN o.order_id END) as ordersShipped,
          COALESCE(SUM(o.stats_prod_pieces), 0) as piecesShipped
        FROM _order o
        JOIN employees e ON o.stats_cid_shipped = e.cid
        WHERE ${shippingWhere}
          AND o.order_status IN (100, 92)
          AND e.hidden = 0
          AND e.disabled = 0
        GROUP BY e.employeeid, e.firstname, e.lastname
        ORDER BY ordersShipped DESC
      `;

      const [shippingByEmployeeRows] = await connection.execute(shippingByEmployeeQuery, params);
      const shippingByEmployee = shippingByEmployeeRows.map(row => ({
        employeeId: row.employeeid,
        name: `${row.firstname || ''} ${row.lastname || ''}`.trim() || `Employee ${row.employeeid}`,
        ordersShipped: parseInt(row.ordersShipped || 0),
        piecesShipped: parseInt(row.piecesShipped || 0),
      }));

      // Calculate period dates for FTE calculation
      let periodStart, periodEnd;
      if (dateRange?.start) {
        periodStart = new Date(dateRange.start);
      } else if (params[0]) {
        periodStart = new Date(params[0]);
      } else {
        periodStart = new Date();
        periodStart.setDate(periodStart.getDate() - 30);
      }

      if (dateRange?.end) {
        periodEnd = new Date(dateRange.end);
      } else if (params[1]) {
        periodEnd = new Date(params[1]);
      } else {
        periodEnd = new Date();
      }

      const fte = calculateFTE(totalHours, periodStart, periodEnd);
      const activeEmployees = enrichedEmployeeHours.filter(e => e.hours > 0).length;

      // Calculate weeks in period for weekly averages
      const periodDays = Math.max(1, (periodEnd - periodStart) / (1000 * 60 * 60 * 24));
      const weeksInPeriod = periodDays / 7;

      // Get daily trend data for hours.
      // TimeStamp stores Central wall-clock literals; business date = the
      // Central calendar date, so DATE_FORMAT(col) needs no hour shift.
      const trendWhere = whereClause.replace(/date_placed/g, 'tc.TimeStamp');
      const trendQuery = `
        SELECT
          DATE_FORMAT(tc.TimeStamp, '%Y-%m-%d') as date,
          tc.EmployeeID,
          tc.TimeStamp,
          tc.PunchType
        FROM timeclock tc
        LEFT JOIN employees e ON tc.EmployeeID = e.employeeid
        WHERE ${trendWhere}
          AND e.hidden = 0
          AND e.disabled = 0
        ORDER BY date, tc.EmployeeID, tc.TimeStamp
      `;

      const [trendRows] = await connection.execute(trendQuery, params);

      // Get daily picking data for trend
      // Ship-together orders: only count main orders (is_sub = 0 or NULL)
      // createddate is a Central wall-clock literal — DATE_FORMAT(col) is the
      // business date with no hour shift.
      const pickingTrendWhere = whereClause.replace(/date_placed/g, 'pt.createddate');
      const pickingTrendQuery = `
        SELECT
          DATE_FORMAT(pt.createddate, '%Y-%m-%d') as date,
          COUNT(DISTINCT CASE WHEN ptb.is_sub = 0 OR ptb.is_sub IS NULL THEN ptb.orderid END) as ordersPicked,
          COALESCE(SUM(pt.totalpieces_picked), 0) as piecesPicked
        FROM picking_ticket pt
        LEFT JOIN picking_ticket_buckets ptb ON pt.pickingid = ptb.pickingid
        WHERE ${pickingTrendWhere}
          AND pt.closeddate IS NOT NULL
        GROUP BY DATE_FORMAT(pt.createddate, '%Y-%m-%d')
        ORDER BY date
      `;

      const [pickingTrendRows] = await connection.execute(pickingTrendQuery, params);

      // Create a map of picking data by date
      const pickingByDate = new Map();
      pickingTrendRows.forEach(row => {
        // Date is already a string in YYYY-MM-DD format from DATE_FORMAT
        const date = String(row.date);
        pickingByDate.set(date, {
          ordersPicked: parseInt(row.ordersPicked || 0),
          piecesPicked: parseInt(row.piecesPicked || 0),
        });
      });

      // Group timeclock by date for trend
      const byDate = new Map();
      trendRows.forEach(row => {
        // Date is already a string in YYYY-MM-DD format from DATE_FORMAT
        const date = String(row.date);
        if (!byDate.has(date)) {
          byDate.set(date, []);
        }
        byDate.get(date).push(row);
      });

      // Generate all business dates in the half-open period [start, end) for
      // complete trend data. Business date = Chicago calendar date.
      const allDatesInRange = [];
      const startDt = DateTime.fromJSDate(periodStart).setZone(BUSINESS_TZ).startOf('day');
      const endDt = DateTime.fromJSDate(periodEnd).setZone(BUSINESS_TZ);

      let currentDt = startDt;
      while (currentDt < endDt) {
        allDatesInRange.push(currentDt.toFormat('yyyy-MM-dd'));
        currentDt = currentDt.plus({ days: 1 });
      }

      // Build trend data for all dates in range, filling zeros for missing days
      const trend = allDatesInRange.map(date => {
        const punches = byDate.get(date) || [];
        const { totalHours: dayHours, employeeHours: dayEmployeeHours } = calculateHoursFromPunches(punches);
        const picking = pickingByDate.get(date) || { ordersPicked: 0, piecesPicked: 0 };

        // The string is already the business date; the zone here is only for
        // the display timestamp label (office time).
        const dateDt = DateTime.fromFormat(date, 'yyyy-MM-dd', { zone: TIMEZONE });

        return {
          date,
          timestamp: dateDt.toISO(),
          hours: dayHours,
          activeEmployees: dayEmployeeHours.filter(e => e.hours > 0).length,
          ordersPicked: picking.ordersPicked,
          piecesPicked: picking.piecesPicked,
        };
      });

      // Get previous period data for comparison
      const previousRange = getPreviousPeriodRange(timeRange, startDate, endDate);
      let comparison = null;
      let previousTotals = null;

      if (previousRange) {
        const prevTimeclockWhere = previousRange.whereClause.replace(/date_placed/g, 'tc.TimeStamp');

        const [prevTimeclockRows] = await connection.execute(
          `SELECT tc.EmployeeID, tc.TimeStamp, tc.PunchType
           FROM timeclock tc
           LEFT JOIN employees e ON tc.EmployeeID = e.employeeid
           WHERE ${prevTimeclockWhere}
             AND e.hidden = 0
             AND e.disabled = 0
           ORDER BY tc.EmployeeID, tc.TimeStamp`,
          previousRange.params
        );

        const {
          totalHours: prevTotalHours,
          totalProductiveHours: prevProductiveHours,
          employeeHours: prevEmployeeHours
        } = calculateHoursFromPunches(prevTimeclockRows);
        const prevActiveEmployees = prevEmployeeHours.filter(e => e.hours > 0).length;

        // Previous picking data (ship-together fix applied)
        // Use separate queries to avoid duplication from bucket join
        const prevPickingWhere = previousRange.whereClause.replace(/date_placed/g, 'pt.createddate');

        const [[prevPickingStatsRows], [prevOrderCountRows]] = await Promise.all([
          connection.execute(
            `SELECT
               SUM(pt.totalpieces_picked) as piecesPicked,
               SUM(TIMESTAMPDIFF(SECOND, pt.createddate, pt.closeddate)) as pickingTimeSeconds
             FROM picking_ticket pt
             WHERE ${prevPickingWhere}
               AND pt.closeddate IS NOT NULL`,
            previousRange.params
          ),
          connection.execute(
            `SELECT
               COUNT(DISTINCT CASE WHEN ptb.is_sub = 0 OR ptb.is_sub IS NULL THEN ptb.orderid END) as ordersPicked
             FROM picking_ticket pt
             LEFT JOIN picking_ticket_buckets ptb ON pt.pickingid = ptb.pickingid
             WHERE ${prevPickingWhere}
               AND pt.closeddate IS NOT NULL`,
            previousRange.params
          )
        ]);

        const prevPickingStats = prevPickingStatsRows[0] || { piecesPicked: 0, pickingTimeSeconds: 0 };
        const prevOrderCount = prevOrderCountRows[0] || { ordersPicked: 0 };
        const prevPicking = {
          ordersPicked: parseInt(prevOrderCount.ordersPicked || 0),
          piecesPicked: parseInt(prevPickingStats.piecesPicked || 0),
          pickingTimeSeconds: parseInt(prevPickingStats.pickingTimeSeconds || 0)
        };
        const prevPickingHours = prevPicking.pickingTimeSeconds / 3600;

        // Previous shipping data
        const prevShippingWhere = previousRange.whereClause.replace(/date_placed/g, 'o.date_shipped');
        const [prevShippingRows] = await connection.execute(
          `SELECT
             COUNT(DISTINCT CASE WHEN o.order_type != 8 OR o.order_type IS NULL THEN o.order_id END) as ordersShipped,
             COALESCE(SUM(o.stats_prod_pieces), 0) as piecesShipped
           FROM _order o
           WHERE ${prevShippingWhere}
             AND o.order_status IN (100, 92)`,
          previousRange.params
        );
        const prevShipping = prevShippingRows[0] || { ordersShipped: 0, piecesShipped: 0 };

        // Calculate previous period FTE and productivity
        const prevFte = calculateFTE(prevTotalHours, previousRange.start || periodStart, previousRange.end || periodEnd);
        const prevOrdersPerHour = prevProductiveHours > 0 ? parseInt(prevPicking.ordersPicked || 0) / prevProductiveHours : 0;
        const prevPiecesPerHour = prevProductiveHours > 0 ? parseInt(prevPicking.piecesPicked || 0) / prevProductiveHours : 0;

        previousTotals = {
          hours: prevTotalHours,
          productiveHours: prevProductiveHours,
          activeEmployees: prevActiveEmployees,
          fte: prevFte,
          ordersPicked: parseInt(prevPicking.ordersPicked || 0),
          piecesPicked: parseInt(prevPicking.piecesPicked || 0),
          pickingHours: prevPickingHours,
          ordersShipped: parseInt(prevShipping.ordersShipped || 0),
          piecesShipped: parseInt(prevShipping.piecesShipped || 0),
          ordersPerHour: prevOrdersPerHour,
          piecesPerHour: prevPiecesPerHour,
        };

        // Calculate productivity metrics for comparison
        const currentOrdersPerHour = totalProductiveHours > 0 ? totalOrdersPicked / totalProductiveHours : 0;
        const currentPiecesPerHour = totalProductiveHours > 0 ? totalPiecesPicked / totalProductiveHours : 0;

        comparison = {
          hours: calculateComparison(totalHours, prevTotalHours),
          productiveHours: calculateComparison(totalProductiveHours, prevProductiveHours),
          activeEmployees: calculateComparison(activeEmployees, prevActiveEmployees),
          fte: calculateComparison(fte, prevFte),
          ordersPicked: calculateComparison(totalOrdersPicked, parseInt(prevPicking.ordersPicked || 0)),
          piecesPicked: calculateComparison(totalPiecesPicked, parseInt(prevPicking.piecesPicked || 0)),
          ordersShipped: calculateComparison(parseInt(shipping.ordersShipped || 0), parseInt(prevShipping.ordersShipped || 0)),
          piecesShipped: calculateComparison(parseInt(shipping.piecesShipped || 0), parseInt(prevShipping.piecesShipped || 0)),
          ordersPerHour: calculateComparison(currentOrdersPerHour, prevOrdersPerHour),
          piecesPerHour: calculateComparison(currentPiecesPerHour, prevPiecesPerHour),
        };
      }

      // Calculate efficiency (picking time vs productive hours)
      const pickingEfficiency = totalProductiveHours > 0 ? (totalPickingHours / totalProductiveHours) * 100 : 0;

      const response = {
        dateRange,
        totals: {
          // Time metrics
          hours: totalHours,
          breakHours: totalBreakHours,
          productiveHours: totalProductiveHours,
          pickingHours: totalPickingHours,

          // Employee metrics
          activeEmployees,
          fte,
          weeksInPeriod,

          // Picking metrics
          ordersPicked: totalOrdersPicked,
          piecesPicked: totalPiecesPicked,
          ticketCount: totalTickets,

          // Shipping metrics
          ordersShipped: parseInt(shipping.ordersShipped || 0),
          piecesShipped: parseInt(shipping.piecesShipped || 0),

          // Calculated metrics - standardized to weekly
          hoursPerWeek: weeksInPeriod > 0 ? totalHours / weeksInPeriod : 0,
          hoursPerEmployeePerWeek: activeEmployees > 0 && weeksInPeriod > 0
            ? (totalHours / activeEmployees) / weeksInPeriod
            : 0,

          // Productivity metrics (uses productive hours - excludes breaks)
          ordersPerHour: totalProductiveHours > 0 ? totalOrdersPicked / totalProductiveHours : 0,
          piecesPerHour: totalProductiveHours > 0 ? totalPiecesPicked / totalProductiveHours : 0,

          // Picking speed from database (more accurate, only counts picking time)
          avgPickingSpeed,

          // Efficiency metrics
          pickingEfficiency,
        },
        previousTotals,
        comparison,
        byEmployee: {
          hours: enrichedEmployeeHours,
          picking: pickingByEmployee,
          shipping: shippingByEmployee,
        },
        trend,
      };

      return { response, release };
    };

    let result;
    try {
      result = await Promise.race([mainOperation(), timeoutPromise]);
    } catch (error) {
      if (error.message.includes('timeout')) {
        console.log(`[EMPLOYEE-METRICS] Request timed out in ${Date.now() - startTime}ms`);
        throw error;
      }
      throw error;
    }

    const { response, release } = result;

    if (release) release();

    console.log(`[EMPLOYEE-METRICS] Request completed in ${Date.now() - startTime}ms`);
    res.json(response);

  } catch (error) {
    console.error('Error in /employee-metrics:', error);
    console.log(`[EMPLOYEE-METRICS] Request failed in ${Date.now() - startTime}ms`);
    res.status(500).json({ error: error.message });
  }
});

// Health check
router.get('/health', async (req, res) => {
  try {
    const { connection, release } = await getDbConnection();
    await connection.execute('SELECT 1 as test');
    release();

    res.json({
      status: 'healthy',
      timestamp: new Date().toISOString(),
      pool: getPoolStatus(),
    });
  } catch (error) {
    res.status(500).json({
      status: 'unhealthy',
      timestamp: new Date().toISOString(),
      error: error.message,
    });
  }
});

// Helper functions
function calculateComparison(currentValue, previousValue) {
  if (typeof previousValue !== 'number') {
    return { absolute: null, percentage: null };
  }

  const absolute = typeof currentValue === 'number' ? currentValue - previousValue : null;
  const percentage =
    absolute !== null && previousValue !== 0
      ? (absolute / Math.abs(previousValue)) * 100
      : null;

  return { absolute, percentage };
}

function getPreviousPeriodRange(timeRange, startDate, endDate) {
  if (timeRange && timeRange !== 'custom') {
    const prevTimeRange = getPreviousTimeRange(timeRange);
    if (!prevTimeRange || prevTimeRange === timeRange) {
      return null;
    }
    return getTimeRangeConditions(prevTimeRange);
  }

  const hasCustomDates = (timeRange === 'custom' || !timeRange) && startDate && endDate;
  if (!hasCustomDates) {
    return null;
  }

  try {
    const prev = previousRange(customRange(startDate, endDate));
    if (!prev) return null;
    return getTimeRangeConditions('custom', prev.start.toISO(), prev.end.toISO());
  } catch {
    return null;
  }
}

function getPreviousTimeRange(timeRange) {
  const map = {
    today: 'yesterday',
    thisWeek: 'lastWeek',
    thisMonth: 'lastMonth',
    last7days: 'previous7days',
    last30days: 'previous30days',
    last90days: 'previous90days',
    yesterday: 'twoDaysAgo'
  };
  return map[timeRange] || timeRange;
}

export default router;
