import { NextRequest, NextResponse } from 'next/server';
import { z } from 'zod';

import { withAuth, withPermission } from '@/lib/api/helpers';
import { errorResponse } from '@/lib/errors';
import { previousPeriodBounds } from '@/lib/analytics/range';
import { computeSalesKpiComparison } from '@/lib/services/reports/sales-comparison';
import { parseSalesFilters } from '@/lib/services/reports/sales-filters';
import {
  getSalesKpis,
  getPipelineFunnel,
  getStageVelocity,
  getWinRateOverTime,
  getSourceConversion,
  getRepLeaderboard,
  getRepFilterOptions,
  getDealHeat,
  getRepPerformanceDetail,
  getStalledDeals,
  getClosingThisMonth,
  getRecentWins,
  getLostReasonBreakdown,
  salesHasData,
} from '@/lib/services/reports/sales.service';

/**
 * GET /api/v1/reports/sales?from=&to=
 *
 * Returns the Sales Performance report payload for the active port:
 * the 7 KPI tiles + the pipeline funnel (chart 1). Further charts +
 * tables ship on this same endpoint as they're built; the response
 * shape grows additively under a single `data` envelope.
 *
 * Permission: `reports.view_dashboard` (same gate as the existing
 * dashboard report endpoints; the Sales report is the canonical "for
 * leadership" surface).
 */

const querySchema = z.object({
  from: z.string().datetime().optional(),
  to: z.string().datetime().optional(),
  // "1" / "true" enables the prior-period comparison (adds per-KPI deltas).
  compare: z.string().optional(),
});

function resolveRange(from?: string, to?: string): { from: Date; to: Date } {
  const now = new Date();
  // Defaults: trailing 30 days. Matches the "Last 30 days" preset on
  // the date-range picker so a no-argument GET returns the same thing
  // the default UI state shows.
  const defaultFrom = new Date(now);
  defaultFrom.setDate(defaultFrom.getDate() - 30);
  return {
    from: from ? new Date(from) : defaultFrom,
    to: to ? new Date(to) : now,
  };
}

export const GET = withAuth(
  withPermission('reports', 'view_dashboard', async (req: NextRequest, ctx) => {
    try {
      const params = req.nextUrl.searchParams;
      const { from, to, compare } = querySchema.parse({
        from: params.get('from') ?? undefined,
        to: params.get('to') ?? undefined,
        compare: params.get('compare') ?? undefined,
      });
      const range = resolveRange(from, to);
      const compareEnabled = compare === '1' || compare === 'true';
      const priorBounds = compareEnabled ? previousPeriodBounds(range) : null;

      // Detail-table filters: stage / leadCategory / outcome / source /
      // assignedTo (rep). Parsed + allowlisted in one pure helper.
      const filters = parseSalesFilters(params);

      const [
        kpis,
        funnel,
        stageVelocity,
        winRateOverTime,
        sourceConversion,
        repLeaderboard,
        repOptions,
        dealHeat,
        repPerformanceDetail,
        stalledDeals,
        closingThisMonth,
        recentWins,
        lostReasonBreakdown,
        priorKpis,
        hasData,
      ] = await Promise.all([
        getSalesKpis(ctx.portId, range),
        getPipelineFunnel(ctx.portId),
        getStageVelocity(ctx.portId),
        getWinRateOverTime(ctx.portId, range),
        getSourceConversion(ctx.portId),
        getRepLeaderboard(ctx.portId, range),
        getRepFilterOptions(ctx.portId),
        getDealHeat(ctx.portId),
        getRepPerformanceDetail(ctx.portId, range, filters),
        getStalledDeals(ctx.portId, filters),
        getClosingThisMonth(ctx.portId, filters),
        getRecentWins(ctx.portId, filters),
        getLostReasonBreakdown(ctx.portId, range, filters),
        // Prior-window KPIs for the comparison toggle. Runs in parallel
        // with the main batch (depends only on the derived priorBounds);
        // resolves to null when the toggle is off so we pay nothing.
        priorBounds ? getSalesKpis(ctx.portId, priorBounds) : Promise.resolve(null),
        salesHasData(ctx.portId),
      ]);

      const comparison =
        priorKpis && priorBounds
          ? {
              deltas: computeSalesKpiComparison(kpis, priorKpis),
              priorRange: {
                from: priorBounds.from.toISOString(),
                to: priorBounds.to.toISOString(),
              },
            }
          : null;

      return NextResponse.json({
        data: {
          kpis,
          comparison,
          funnel,
          stageVelocity,
          winRateOverTime,
          sourceConversion,
          repLeaderboard,
          repOptions,
          dealHeat,
          repPerformanceDetail,
          stalledDeals,
          closingThisMonth,
          recentWins,
          lostReasonBreakdown,
          hasData,
          range: {
            from: range.from.toISOString(),
            to: range.to.toISOString(),
          },
        },
      });
    } catch (error) {
      return errorResponse(error);
    }
  }),
);
