import { NextRequest, NextResponse } from "next/server";
import { prisma } from "@/lib/db";
import { requireApiAuthWithOrg, handleApiError } from "@/lib/api-auth";

const VALID_PERIODS = new Set(["7", "30", "90", "365"]);
const VALID_GROUP_BY = new Set(["day", "week", "month"]);

export async function GET(request: NextRequest) {
  try {
    const session = await requireApiAuthWithOrg();
    const orgId = session.user.orgId!;

    const params = request.nextUrl.searchParams;
    const period = VALID_PERIODS.has(params.get("period") ?? "")
      ? Number(params.get("period"))
      : 30;
    const groupBy = VALID_GROUP_BY.has(params.get("groupBy") ?? "")
      ? (params.get("groupBy") as "day" | "week" | "month")
      : "day";

    const since = new Date();
    since.setDate(since.getDate() - period);

    const [series, pipeline, teamActivity, avgReview, peopleCount, newPeople] =
      await Promise.all([
        buildTimeSeries(orgId, since, groupBy),
        buildPipeline(orgId),
        buildTeamActivity(orgId, since),
        buildAvgReviewTime(orgId, since),
        prisma.person.count({ where: { organizationId: orgId } }),
        prisma.person.count({
          where: { organizationId: orgId, createdAt: { gte: since } },
        }),
      ]);

    return NextResponse.json({
      series,
      pipeline,
      teamActivity,
      avgReviewTimeHours: avgReview,
      peopleCount,
      newPeopleThisPeriod: newPeople,
    });
  } catch (error) {
    return handleApiError(error);
  }
}

async function buildTimeSeries(
  orgId: string,
  since: Date,
  groupBy: "day" | "week" | "month"
) {
  const cards = await prisma.responseCard.findMany({
    where: { organizationId: orgId, createdAt: { gte: since } },
    select: { createdAt: true, submissionSource: true },
    orderBy: { createdAt: "asc" },
  });

  const buckets = new Map<string, { total: number; survey: number }>();

  for (const card of cards) {
    const key = bucketKey(card.createdAt, groupBy);
    const entry = buckets.get(key) ?? { total: 0, survey: 0 };
    entry.total++;
    if (card.submissionSource === "survey") entry.survey++;
    buckets.set(key, entry);
  }

  return Array.from(buckets.entries())
    .sort(([a], [b]) => a.localeCompare(b))
    .map(([date, counts]) => ({
      date,
      total: counts.total,
      scanned: counts.total - counts.survey,
      survey: counts.survey,
    }));
}

function bucketKey(date: Date, groupBy: "day" | "week" | "month"): string {
  const d = new Date(date);
  if (groupBy === "month") {
    return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-01`;
  }
  if (groupBy === "week") {
    const day = d.getDay();
    d.setDate(d.getDate() - day);
    return d.toISOString().split("T")[0];
  }
  return d.toISOString().split("T")[0];
}

async function buildPipeline(orgId: string) {
  const [byOcr, byReview] = await Promise.all([
    prisma.responseCard.groupBy({
      by: ["ocrStatus"],
      where: { organizationId: orgId },
      _count: { id: true },
    }),
    prisma.responseCard.groupBy({
      by: ["reviewStatus"],
      where: { organizationId: orgId },
      _count: { id: true },
    }),
  ]);

  const ocrMap = Object.fromEntries(
    byOcr.map((r) => [r.ocrStatus, r._count.id])
  );
  const reviewMap = Object.fromEntries(
    byReview.map((r) => [r.reviewStatus, r._count.id])
  );

  return {
    pending: ocrMap["pending"] ?? 0,
    processing: ocrMap["processing"] ?? 0,
    complete: ocrMap["complete"] ?? 0,
    error: ocrMap["error"] ?? 0,
    unreviewed: reviewMap["unreviewed"] ?? 0,
    reviewed: reviewMap["reviewed"] ?? 0,
    exported: 0,
  };
}

async function buildTeamActivity(orgId: string, since: Date) {
  const [reviewedCards, assignedCards] = await Promise.all([
    prisma.responseCard.findMany({
      where: {
        organizationId: orgId,
        reviewedById: { not: null },
        reviewedAt: { gte: since },
      },
      select: {
        reviewedById: true,
        reviewedBy: { select: { id: true, displayName: true, username: true, email: true } },
      },
    }),
    prisma.responseCard.findMany({
      where: { organizationId: orgId, assignedToId: { not: null } },
      select: {
        assignedToId: true,
        assignedTo: { select: { id: true, displayName: true, username: true, email: true } },
      },
    }),
  ]);

  const userMap = new Map<string, { name: string; reviewed: number; assigned: number }>();

  for (const card of reviewedCards) {
    const u = card.reviewedBy!;
    const entry = userMap.get(u.id) ?? {
      name: u.displayName || u.username || u.email,
      reviewed: 0,
      assigned: 0,
    };
    entry.reviewed++;
    userMap.set(u.id, entry);
  }

  for (const card of assignedCards) {
    const u = card.assignedTo!;
    const existing = userMap.get(u.id);
    if (existing) {
      existing.assigned++;
    } else {
      userMap.set(u.id, {
        name: u.displayName || u.username || u.email,
        reviewed: 0,
        assigned: 1,
      });
    }
  }

  return Array.from(userMap.entries())
    .map(([userId, data]) => ({ userId, ...data }))
    .sort((a, b) => b.reviewed - a.reviewed);
}

async function buildAvgReviewTime(orgId: string, since: Date) {
  const cards = await prisma.responseCard.findMany({
    where: {
      organizationId: orgId,
      reviewedAt: { not: null },
      createdAt: { gte: since },
    },
    select: { createdAt: true, reviewedAt: true },
  });

  if (cards.length === 0) return null;

  const totalMs = cards.reduce((sum, c) => {
    return sum + (c.reviewedAt!.getTime() - c.createdAt.getTime());
  }, 0);

  const avgHours = totalMs / cards.length / (1000 * 60 * 60);
  return Math.round(avgHours * 10) / 10;
}
