import { NextResponse } from 'next/server';
import { and, eq, isNotNull, isNull, gte, sql } from 'drizzle-orm';

import { withAuth, withPermission } from '@/lib/api/helpers';
import { db } from '@/lib/db';
import { interests } from '@/lib/db/schema/interests';
import { berths } from '@/lib/db/schema/berths';
import { clients } from '@/lib/db/schema/clients';
import { websiteSubmissions } from '@/lib/db/schema/website-submissions';
import { errorResponse } from '@/lib/errors';
import { PIPELINE_STAGES } from '@/lib/constants';

export const GET = withAuth(
  withPermission('reports', 'view_dashboard', async (_req, ctx) => {
    try {
      const portId = ctx.portId;
      const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
      const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);

      const [pipelineRows, berthStatusRows, totals, recent] = await Promise.all([
        // Only active interests count toward per-stage breakdowns;
        // terminal (outcome-set) interests are tracked separately via
        // `closedTotal` below. Pre-2026-05-14 cleanup, terminal rows
        // were grouped under the sentinel pipeline_stage='completed'
        // bucket; the new convention leaves the stage where it was
        // when the outcome was set, so we must filter by `outcome IS
        // NULL` explicitly to keep the kanban breakdown honest.
        db
          .select({
            stage: interests.pipelineStage,
            count: sql<number>`count(*)::int`,
          })
          .from(interests)
          .where(
            and(
              eq(interests.portId, portId),
              isNull(interests.archivedAt),
              isNull(interests.outcome),
            ),
          )
          .groupBy(interests.pipelineStage),

        db
          .select({
            status: berths.status,
            count: sql<number>`count(*)::int`,
          })
          .from(berths)
          .where(eq(berths.portId, portId))
          .groupBy(berths.status),

        Promise.all([
          db
            .select({ count: sql<number>`count(*)::int` })
            .from(clients)
            .where(and(eq(clients.portId, portId), isNull(clients.archivedAt))),
          db
            .select({ count: sql<number>`count(*)::int` })
            .from(interests)
            .where(and(eq(interests.portId, portId), isNull(interests.archivedAt))),
          db
            .select({ count: sql<number>`count(*)::int` })
            .from(berths)
            .where(eq(berths.portId, portId)),
          // closedTotal: all-time count of terminal (outcome-set)
          // non-archived interests. Drives the conversion-rate tile.
          db
            .select({ count: sql<number>`count(*)::int` })
            .from(interests)
            .where(
              and(
                eq(interests.portId, portId),
                isNull(interests.archivedAt),
                isNotNull(interests.outcome),
              ),
            ),
        ]),

        Promise.all([
          db
            .select({ count: sql<number>`count(*)::int` })
            .from(websiteSubmissions)
            .where(
              and(
                eq(websiteSubmissions.portId, portId),
                gte(websiteSubmissions.receivedAt, sevenDaysAgo),
              ),
            ),
          // "completed30d" = interests that hit a terminal outcome in
          // the last 30 days (any outcome - won, lost, or cancelled).
          // Use `outcome_at` not `updated_at` so unrelated edits to a
          // long-closed deal don't drag it back into the window.
          db
            .select({ count: sql<number>`count(*)::int` })
            .from(interests)
            .where(
              and(
                eq(interests.portId, portId),
                isNotNull(interests.outcome),
                gte(interests.outcomeAt, thirtyDaysAgo),
              ),
            ),
        ]),
      ]);

      const pipeline = Object.fromEntries(PIPELINE_STAGES.map((s) => [s, 0])) as Record<
        string,
        number
      >;
      for (const row of pipelineRows) pipeline[row.stage] = row.count;

      const berthStatus: Record<string, number> = {
        available: 0,
        under_offer: 0,
        sold: 0,
      };
      for (const row of berthStatusRows) berthStatus[row.status] = row.count;

      const totalClients = totals[0][0]?.count ?? 0;
      const totalInterests = totals[1][0]?.count ?? 0;
      const totalBerths = totals[2][0]?.count ?? 0;
      const closedTotal = totals[3][0]?.count ?? 0;
      const newInquiries7d = recent[0][0]?.count ?? 0;
      const completed30d = recent[1][0]?.count ?? 0;

      const openTotal = totalInterests - closedTotal;
      const conversionPct =
        totalInterests > 0 ? Math.round((closedTotal / totalInterests) * 100) : 0;

      return NextResponse.json({
        data: {
          totals: { totalClients, totalInterests, totalBerths },
          recent: { newInquiries7d, completed30d },
          pipeline,
          berthStatus,
          conversion: { closedTotal, openTotal, conversionPct },
        },
      });
    } catch (error) {
      return errorResponse(error);
    }
  }),
);
