import { NextRequest, NextResponse } from "next/server" import { getSessionUser } from "@/lib/auth" import { query } from "@/lib/db" import { avatarSvgUrl } from "@/lib/avatar" function getPeriodDateRange(period: string): { start: Date; end: Date } { const end = new Date() let start: Date switch (period) { case "7days": start = new Date(end); start.setDate(start.getDate() - 7); break case "30days": start = new Date(end); start.setDate(start.getDate() - 30); break case "12months": start = new Date(end); start.setFullYear(start.getFullYear() - 12); break case "6months": default: start = new Date(end); start.setMonth(start.getMonth() - 6); break } return { start, end } } function getPreviousPeriodRange(period: string, currentStart: Date): { start: Date; end: Date } { const end = new Date(currentStart) const diff = end.getTime() - currentStart.getTime() const start = new Date(end.getTime() - diff) return { start, end } } const periodLabels: Record = { "7days": "Last 7 days", "30days": "Last 30 days", "6months": "Last 6 months", "12months": "Last 12 months", } function stageToStatus(name: string): string { switch (name) { case "New": return "open" case "Contacted": return "contacted" case "Qualified": case "Interested": case "Demo Scheduled": case "Negotiation": return "pending" case "Closed Won": return "closed" case "Closed Lost": return "ignored" default: return "open" } } async function fetchLeadsInRange(start: Date, end: Date, userId?: string, isAdmin?: boolean) { const result = await query( `SELECT l.id, l.created_at, l.company_name, l.contact_name, l.email, l.phone, l.notes, l.assigned_to, l.score, ls.name AS stage_name, u.id AS user_id, u.first_name, u.last_name, u.email AS user_email, u.avatar_url FROM leads l JOIN lead_stages ls ON ls.id = l.stage_id LEFT JOIN users u ON u.id = l.assigned_to WHERE l.deleted_at IS NULL AND l.created_at >= $1 AND l.created_at <= $2 ${isAdmin ? "" : "AND l.assigned_to = $3"} ORDER BY l.created_at DESC`, isAdmin ? [start.toISOString(), end.toISOString()] : [start.toISOString(), end.toISOString(), userId] ) return result.rows.map((r: any) => ({ ...r, status: stageToStatus(r.stage_name), })) } function countStatuses(leads: any[]) { const counts = { open: 0, contacted: 0, pending: 0, closed: 0, ignored: 0 } leads.forEach((l: any) => { const s = l.status as keyof typeof counts if (s in counts) counts[s]++ }) return counts } function buildMonthlyBreakdown(leads: any[], period: string) { const { start, end } = getPeriodDateRange(period) const result: { label: string; total: number; open: number; contacted: number; pending: number; closed: number; ignored: number }[] = [] const current = new Date(start) const isMonthly = period === "6months" || period === "12months" while (current <= end) { const label = isMonthly ? current.toLocaleDateString("en-US", { month: "short", year: "2-digit" }) : current.toLocaleDateString("en-US", { month: "short", day: "numeric" }) const ps = new Date(current) const pe = isMonthly ? new Date(current.getFullYear(), current.getMonth() + 1, 0, 23, 59, 59) : (() => { const d = new Date(current); d.setHours(23, 59, 59, 999); return d })() const inPeriod = leads.filter((l: any) => { const d = new Date(l.created_at) return d >= ps && d <= pe }) const counts = countStatuses(inPeriod) result.push({ label, total: inPeriod.length, ...counts }) if (isMonthly) current.setMonth(current.getMonth() + 1) else current.setDate(current.getDate() + 1) } return result } function computeTrend(current: number, previous: number): { pct: number; up: boolean } { if (previous === 0) return { pct: current > 0 ? 100 : 0, up: current > 0 } const pct = Math.round(((current - previous) / previous) * 100) return { pct: Math.abs(pct), up: pct >= 0 } } export async function GET(request: NextRequest) { try { const user = await getSessionUser() if (!user) return NextResponse.json({ error: "Unauthorized" }, { status: 401 }) const isAdmin = user.role === "admin" || user.role === "super_admin" const { searchParams } = new URL(request.url) const period = searchParams.get("period") || "6months" const yearParam = searchParams.get("year") let start: Date, end: Date, prevRange: { start: Date; end: Date } if (yearParam) { const y = parseInt(yearParam) start = new Date(y, 0, 1) end = new Date(y, 11, 31, 23, 59, 59) prevRange = { start: new Date(y - 1, 0, 1), end: new Date(y - 1, 11, 31, 23, 59, 59) } } else { const r = getPeriodDateRange(period) start = r.start; end = r.end prevRange = getPreviousPeriodRange(period, start) } const [currentLeads, prevLeads] = await Promise.all([ fetchLeadsInRange(start, end, user.id, isAdmin), fetchLeadsInRange(prevRange.start, prevRange.end, user.id, isAdmin), ]) const currentCounts = countStatuses(currentLeads) const prevCounts = countStatuses(prevLeads) const totalLeads = currentLeads.length const closedLeads = currentCounts.closed const conversionRate = totalLeads > 0 ? Math.round((closedLeads / totalLeads) * 100) : 0 const mappedLeads = currentLeads.map((r: any) => ({ id: r.id, companyName: r.company_name || "", contactName: r.contact_name, email: r.email || "", phone: r.phone || "", source: "", description: r.notes || "", status: r.status, assignedUserId: r.assigned_to, assignedUser: r.assigned_to ? { id: r.user_id, name: `${r.first_name} ${r.last_name}`, email: r.user_email, avatar: avatarSvgUrl(`${r.first_name} ${r.last_name}`), } : null, createdAt: r.created_at, updatedAt: r.updated_at, })) const monthlyBreakdown = buildMonthlyBreakdown(currentLeads, period) const trends = { totalLeads: computeTrend(currentCounts.open + currentCounts.contacted + currentCounts.pending + currentCounts.closed + currentCounts.ignored, prevCounts.open + prevCounts.contacted + prevCounts.pending + prevCounts.closed + prevCounts.ignored), openLeads: computeTrend(currentCounts.open, prevCounts.open), contactedLeads: computeTrend(currentCounts.contacted, prevCounts.contacted), pendingLeads: computeTrend(currentCounts.pending, prevCounts.pending), closedLeads: computeTrend(currentCounts.closed, prevCounts.closed), conversionRate: computeTrend(conversionRate, prevLeads.length > 0 ? Math.round((prevCounts.closed / prevLeads.length) * 100) : 0), } const stats = { totalLeads, openLeads: currentCounts.open, contactedLeads: currentCounts.contacted, pendingLeads: currentCounts.pending, closedLeads, ignoredLeads: currentCounts.ignored, conversionRate, monthlyBreakdown, leadsPerMonth: monthlyBreakdown.map((m: any) => ({ label: m.label, leads: m.total, closed: m.closed })), trends, recentLeads: mappedLeads.slice(0, 10), statusDistribution: [ { name: "Open", value: currentCounts.open, color: "#3b82f6" }, { name: "Contacted", value: currentCounts.contacted, color: "#f59e0b" }, { name: "Pending", value: currentCounts.pending, color: "#8b5cf6" }, { name: "Closed", value: currentCounts.closed, color: "#10b981" }, { name: "Ignored", value: currentCounts.ignored, color: "#6B7280" }, ], periodLabel: periodLabels[period] ?? "Selected period", } return NextResponse.json(stats) } catch (error) { console.error("Dashboard API error:", error) return NextResponse.json({ error: "Failed to load dashboard stats" }, { status: 500 }) } }