import mongoose from 'mongoose';
import Sale from '../models/Sale.js';
import SaleReturn from '../models/SaleReturn.js';
import Expense from '../models/Expense.js';

const { Types } = mongoose;
const oid = (id) => (id ? new Types.ObjectId(String(id)) : null);

const round2 = (n) => Math.round((Number(n) || 0) * 100) / 100;
const safePct = (num, den) => (den > 0 ? round2((num / den) * 100) : 0);

export const resolveRange = (query = {}) => {
  const preset = query.preset || 'THIS_MONTH';
  const now = new Date();
  let start;
  let end = new Date(now);
  end.setHours(23, 59, 59, 999);

  switch (preset) {
    case 'CUSTOM': {
      start = query.from ? new Date(query.from) : new Date(now.getFullYear(), 0, 1);
      start.setHours(0, 0, 0, 0);
      if (query.to) {
        end = new Date(query.to);
        end.setHours(23, 59, 59, 999);
      }
      break;
    }
    case 'TODAY':
      start = new Date(now);
      start.setHours(0, 0, 0, 0);
      break;
    case 'YESTERDAY': {
      start = new Date(now);
      start.setDate(start.getDate() - 1);
      start.setHours(0, 0, 0, 0);
      end = new Date(start);
      end.setHours(23, 59, 59, 999);
      break;
    }
    case 'THIS_WEEK': {
      start = new Date(now);
      const day = (start.getDay() + 6) % 7; // Monday-based week
      start.setDate(start.getDate() - day);
      start.setHours(0, 0, 0, 0);
      break;
    }
    case 'THIS_MONTH':
      start = new Date(now.getFullYear(), now.getMonth(), 1);
      break;
    case 'LAST_MONTH':
      start = new Date(now.getFullYear(), now.getMonth() - 1, 1);
      end = new Date(now.getFullYear(), now.getMonth(), 0, 23, 59, 59, 999);
      break;
    case 'THIS_YEAR':
      start = new Date(now.getFullYear(), 0, 1);
      break;
    default:
      start = new Date(now.getFullYear(), now.getMonth(), 1);
  }
  return { start, end };
};

const saleMatch = ({ start, end, paymentMethod, cashierId }) => {
  const m = { createdAt: { $gte: start, $lte: end } };
  if (paymentMethod) m.paymentMethod = paymentMethod;
  const id = oid(cashierId);
  if (id) m['cashier.id'] = id;
  return m;
};

const returnMatch = ({ start, end, cashierId }) => {
  const m = { createdAt: { $gte: start, $lte: end } };
  const id = oid(cashierId);
  if (id) m['processedBy.id'] = id;
  return m;
};

const expenseMatch = ({ start, end }) => ({ expenseDate: { $gte: start, $lte: end } });

const summaryAgg = async (range, filters) => {
  const [salesAgg, returnsAgg, cogsAgg, missingCostAgg, expensesAgg] = await Promise.all([
    Sale.aggregate([
      { $match: saleMatch(range, filters) },
      { $group: { _id: null, revenue: { $sum: '$grandTotal' }, discount: { $sum: '$discount' }, count: { $sum: 1 } } },
    ]),
    SaleReturn.aggregate([
      { $match: returnMatch(range, filters) },
      { $group: { _id: null, refunds: { $sum: '$totalRefund' }, count: { $sum: 1 } } },
    ]),
    Sale.aggregate([
      { $match: saleMatch(range, filters) },
      { $unwind: '$items' },
      {
        $group: {
          _id: null,
          cogs: {
            $sum: {
              $cond: [
                { $ne: ['$items.costPrice', null] },
                { $multiply: ['$items.quantity', '$items.costPrice'] },
                0,
              ],
            },
          },
        },
      },
    ]),
    Sale.aggregate([
      { $match: saleMatch(range, filters) },
      { $unwind: '$items' },
      { $match: { 'items.costPrice': { $eq: null } } },
      {
        $group: {
          _id: null,
          lines: { $sum: 1 },
          units: { $sum: '$items.quantity' },
          revenue: { $sum: '$items.total' },
        },
      },
    ]),
    Expense.aggregate([
      { $match: expenseMatch(range) },
      { $group: { _id: null, total: { $sum: '$amount' }, count: { $sum: 1 } } },
    ]),
  ]);

  return {
    revenue: round2(salesAgg[0]?.revenue || 0),
    saleCount: salesAgg[0]?.count || 0,
    discounts: round2(salesAgg[0]?.discount || 0),
    returns: round2(returnsAgg[0]?.refunds || 0),
    returnCount: returnsAgg[0]?.count || 0,
    cogsSold: round2(cogsAgg[0]?.cogs || 0),
    legacyMissingCost: {
      lines: missingCostAgg[0]?.lines || 0,
      units: missingCostAgg[0]?.units || 0,
      revenue: round2(missingCostAgg[0]?.revenue || 0),
    },
    expenses: round2(expensesAgg[0]?.total || 0),
    expenseCount: expensesAgg[0]?.count || 0,
  };
};

const returnedCogsAgg = async (range, filters) => {
  const results = await SaleReturn.aggregate([
    { $match: returnMatch(range, filters) },
    { $unwind: '$items' },
    {
      $group: {
        _id: null,
        refundAmount: { $sum: '$items.amount' },
        cogs: {
          $sum: {
            $cond: [
              { $ne: ['$items.costPrice', null] },
              { $multiply: ['$items.quantity', '$items.costPrice'] },
              0,
            ],
          },
        },
      },
    },
  ]);
  return { refundAmount: round2(results[0]?.refundAmount || 0), cogs: round2(results[0]?.cogs || 0) };
};

const getProfitSummary = async (query = {}) => {
  const range = resolveRange(query);
  const filters = {
    paymentMethod: query.paymentMethod || null,
    cashierId: query.cashierId || null,
  };

  const [s, ret] = await Promise.all([summaryAgg(range, filters), returnedCogsAgg(range, filters)]);

  // Net figures: revenue minus refunds; COGS minus the cost of returned goods
  const netSales = round2(s.revenue - s.returns);
  const cogs = round2(s.cogsSold - ret.cogs);
  const grossProfit = round2(netSales - cogs);
  const netProfit = round2(grossProfit - s.expenses);

  return {
    range: { start: range.start, end: range.end, preset: query.preset || 'THIS_MONTH' },
    ...s,
    returnedCogs: ret.cogs,
    netSales,
    cogs,
    grossProfit,
    grossMarginPct: safePct(grossProfit, netSales),
    netProfit,
    netMarginPct: safePct(netProfit, netSales),
    note:
      s.legacyMissingCost.lines > 0
        ? `${s.legacyMissingCost.lines} sale line(s) predate cost tracking — COGS unavailable for them (₹${s.legacyMissingCost.revenue.toFixed(2)} revenue).`
        : '',
  };
};

const getProductProfit = async (query = {}) => {
  const range = resolveRange(query);
  const filters = { paymentMethod: query.paymentMethod || null, cashierId: query.cashierId || null };

  const [sold, returned] = await Promise.all([
    Sale.aggregate([
      { $match: saleMatch(range, filters) },
      { $unwind: '$items' },
      {
        $group: {
          _id: {
            product: '$items.product',
            name: '$items.productName',
            variantId: '$items.variant',
            variantLabel: '$items.variantLabel',
          },
          unitsSold: { $sum: '$items.quantity' },
          revenue: { $sum: '$items.total' },
          cogsKnown: {
            $sum: {
              $cond: [
                { $ne: ['$items.costPrice', null] },
                { $multiply: ['$items.quantity', '$items.costPrice'] },
                0,
              ],
            },
          },
          unitsMissingCost: {
            $sum: { $cond: [{ $eq: ['$items.costPrice', null] }, '$items.quantity', 0] },
          },
        },
      },
    ]),
    SaleReturn.aggregate([
      { $match: returnMatch(range, filters) },
      { $unwind: '$items' },
      {
        $group: {
          _id: {
            product: '$items.product',
            variantId: '$items.variantId',
          },
          unitsReturned: { $sum: '$items.quantity' },
          refunded: { $sum: '$items.amount' },
          cogsReturned: {
            $sum: {
              $cond: [
                { $ne: ['$items.costPrice', null] },
                { $multiply: ['$items.quantity', '$items.costPrice'] },
                0,
              ],
            },
          },
        },
      },
    ]),
  ]);

  const returnMap = new Map(
    returned.map((r) => [`${r._id.product}_${r._id.variantId || ''}`, r])
  );

  const rows = sold.map((row) => {
    const key = `${row._id.product}_${row._id.variantId || ''}`;
    const ret = returnMap.get(key) || { unitsReturned: 0, refunded: 0, cogsReturned: 0 };
    const netRevenue = round2(row.revenue - ret.refunded);
    const cogs = round2(row.cogsKnown - ret.cogsReturned);
    const grossProfit = round2(netRevenue - cogs);
    return {
      productId: row._id.product,
      product: row._id.name,
      variantId: row._id.variantId,
      variant: row._id.variantLabel || '',
      unitsSold: row.unitsSold,
      unitsReturned: ret.unitsReturned,
      unitsNet: row.unitsSold - ret.unitsReturned,
      revenue: round2(row.revenue),
      returned: round2(ret.refunded),
      netRevenue,
      cogs,
      cogsUnavailable: row.unitsMissingCost > 0,
      grossProfit,
      marginPct: safePct(grossProfit, netRevenue),
    };
  });

  return rows;
};

const getCategoryProfit = async (query = {}) => {
  const range = resolveRange(query);
  const filters = { paymentMethod: query.paymentMethod || null, cashierId: query.cashierId || null };

  const [sold, returned] = await Promise.all([
    Sale.aggregate([
      { $match: saleMatch(range, filters) },
      { $unwind: '$items' },
      {
        $lookup: {
          from: 'products',
          localField: 'items.product',
          foreignField: '_id',
          as: 'productDoc',
        },
      },
      {
        $addFields: {
          category: {
            $ifNull: [{ $arrayElemAt: ['$productDoc.category', 0] }, 'Uncategorized'],
          },
        },
      },
      {
        $group: {
          _id: '$category',
          unitsSold: { $sum: '$items.quantity' },
          revenue: { $sum: '$items.total' },
          cogsKnown: {
            $sum: {
              $cond: [
                { $ne: ['$items.costPrice', null] },
                { $multiply: ['$items.quantity', '$items.costPrice'] },
                0,
              ],
            },
          },
          unitsMissingCost: {
            $sum: { $cond: [{ $eq: ['$items.costPrice', null] }, '$items.quantity', 0] },
          },
        },
      },
    ]),
    SaleReturn.aggregate([
      { $match: returnMatch(range, filters) },
      { $unwind: '$items' },
      {
        $lookup: {
          from: 'products',
          localField: 'items.product',
          foreignField: '_id',
          as: 'productDoc',
        },
      },
      {
        $addFields: {
          category: {
            $ifNull: [{ $arrayElemAt: ['$productDoc.category', 0] }, 'Uncategorized'],
          },
        },
      },
      {
        $group: {
          _id: '$category',
          unitsReturned: { $sum: '$items.quantity' },
          refunded: { $sum: '$items.amount' },
          cogsReturned: {
            $sum: {
              $cond: [
                { $ne: ['$items.costPrice', null] },
                { $multiply: ['$items.quantity', '$items.costPrice'] },
                0,
              ],
            },
          },
        },
      },
    ]),
  ]);

  const returnMap = new Map(returned.map((r) => [r._id, r]));
  return sold.map((row) => {
    const ret = returnMap.get(row._id) || { unitsReturned: 0, refunded: 0, cogsReturned: 0 };
    const netRevenue = round2(row.revenue - ret.refunded);
    const cogs = round2(row.cogsKnown - ret.cogsReturned);
    const grossProfit = round2(netRevenue - cogs);
    return {
      category: row._id,
      unitsSold: row.unitsSold,
      revenue: round2(row.revenue),
      returned: round2(ret.refunded),
      netRevenue,
      cogs,
      cogsUnavailable: row.unitsMissingCost > 0,
      grossProfit,
      marginPct: safePct(grossProfit, netRevenue),
    };
  });
};

const getExpenseBreakdown = async (query = {}) => {
  const range = resolveRange(query);
  const [byCategory, byPaymentMethod, list] = await Promise.all([
    Expense.aggregate([
      { $match: expenseMatch(range) },
      { $group: { _id: '$category', amount: { $sum: '$amount' }, count: { $sum: 1 } } },
      { $sort: { amount: -1 } },
    ]),
    Expense.aggregate([
      { $match: expenseMatch(range) },
      { $group: { _id: '$paymentMethod', amount: { $sum: '$amount' }, count: { $sum: 1 } } },
      { $sort: { amount: -1 } },
    ]),
    Expense.find(expenseMatch(range)).sort('-expenseDate').limit(200),
  ]);
  const total = round2(byCategory.reduce((s, r) => s + r.amount, 0));
  return {
    total,
    byCategory: byCategory.map((r) => ({
      category: r._id,
      amount: round2(r.amount),
      count: r.count,
      pct: safePct(r.amount, total),
    })),
    byPaymentMethod: byPaymentMethod.map((r) => ({
      method: r._id,
      amount: round2(r.amount),
      count: r.count,
      pct: safePct(r.amount, total),
    })),
    recent: list,
  };
};

const getNetProfitReport = async (query = {}) => {
  const [summary, expenses] = await Promise.all([getProfitSummary(query), getExpenseBreakdown(query)]);
  return { ...summary, expenseBreakdown: expenses };
};

export default { getProfitSummary, getProductProfit, getCategoryProfit, getExpenseBreakdown, getNetProfitReport };
