import { NextRequest, NextResponse } from 'next/server'; import postgresClient from '@/lib/services/postgres-client'; export async function GET(req: NextRequest) { const { searchParams } = new URL(req.url); const page = Math.max(1, parseInt(searchParams.get('page') ?? '1')); const limit = 50; const offset = (page - 1) * limit; const category = searchParams.get('category'); const company = searchParams.get('company'); // Build filter for ticket list only (aggregations always show all data) const filterWhere: string[] = []; const filterParams: any[] = []; if (category) { filterParams.push(category); filterWhere.push(`problem_category = $${filterParams.length}`); } if (company) { filterParams.push(`%${company}%`); filterWhere.push(`company_name ILIKE $${filterParams.length}`); } const baseWhere = filterWhere.length ? `WHERE ticket_created_at >= DATE_TRUNC('year', NOW()) AND ${filterWhere.join(' AND ')}` : `WHERE ticket_created_at >= DATE_TRUNC('year', NOW())`; const [statsRes, categoryRes, resolutionRes, skillsRes, complexityRes, ticketsRes, totalRes, ytdRes] = await Promise.all([ // Overall stats (unfiltered) postgresClient.query(` SELECT COUNT(*) AS total_analyzed, ROUND(AVG(hours_worked)::numeric, 2) AS avg_hours, ROUND(100.0 * COUNT(*) FILTER (WHERE same_day_close) / NULLIF(COUNT(*), 0), 1) AS same_day_pct, ROUND(100.0 * COUNT(*) FILTER (WHERE preventable = true) / NULLIF(COUNT(*) FILTER (WHERE preventable IS NOT NULL), 0), 1) AS preventable_pct, ROUND(100.0 * COUNT(*) FILTER (WHERE device_was_offline = true) / NULLIF(COUNT(*) FILTER (WHERE device_was_offline IS NOT NULL), 0), 1) AS offline_pct, ROUND(100.0 * COUNT(*) FILTER (WHERE backup_completed_before_tech = true) / NULLIF(COUNT(*) FILTER (WHERE backup_completed_before_tech IS NOT NULL), 0), 1) AS auto_resolved_pct FROM veeam_ticket_analysis WHERE ticket_created_at >= DATE_TRUNC('year', NOW()) `), // By problem category (unfiltered) postgresClient.query(` SELECT problem_category, COUNT(*) AS count, ROUND(AVG(hours_worked)::numeric, 2) AS avg_hours, ROUND(100.0 * COUNT(*) FILTER (WHERE same_day_close) / NULLIF(COUNT(*), 0), 1) AS same_day_pct FROM veeam_ticket_analysis WHERE ticket_created_at >= DATE_TRUNC('year', NOW()) GROUP BY problem_category ORDER BY count DESC `), // By resolution type (unfiltered) postgresClient.query(` SELECT resolution_type, COUNT(*) AS count FROM veeam_ticket_analysis WHERE ticket_created_at >= DATE_TRUNC('year', NOW()) GROUP BY resolution_type ORDER BY count DESC `), // Skills frequency (unfiltered) postgresClient.query(` SELECT skill, COUNT(*) AS count FROM veeam_ticket_analysis, unnest(skills_required) AS skill WHERE ticket_created_at >= DATE_TRUNC('year', NOW()) GROUP BY skill ORDER BY count DESC LIMIT 15 `), // Complexity breakdown (unfiltered) postgresClient.query(` SELECT complexity, COUNT(*) AS count FROM veeam_ticket_analysis WHERE ticket_created_at >= DATE_TRUNC('year', NOW()) GROUP BY complexity ORDER BY CASE complexity WHEN 'trivial' THEN 1 WHEN 'low' THEN 2 WHEN 'medium' THEN 3 WHEN 'high' THEN 4 ELSE 5 END `), // Filtered ticket list postgresClient.query(` SELECT ticket_number, company_name, device_hostname, ticket_created_at, ticket_closed_at, same_day_close, hours_worked, problem_category, resolution_type, complexity, device_was_offline, backup_completed_before_tech, preventable, work_summary, recommended_procedure FROM veeam_ticket_analysis ${baseWhere} ORDER BY ticket_created_at DESC LIMIT $${filterParams.length + 1} OFFSET $${filterParams.length + 2} `, [...filterParams, limit, offset]), // Filtered total postgresClient.query( `SELECT COUNT(*) FROM veeam_ticket_analysis ${baseWhere}`, filterParams ), // YTD total all backup tickets postgresClient.query(` SELECT COUNT(*) AS total FROM tickets WHERE title ILIKE 'Veeam:%' AND title NOT ILIKE '[Veeam RPO]%' AND create_date >= DATE_TRUNC('year', NOW()) `), ]); return NextResponse.json({ stats: { ...statsRes.rows[0], total_ytd: parseInt(ytdRes.rows[0]?.total ?? 0), }, by_category: categoryRes.rows, by_resolution: resolutionRes.rows, skills: skillsRes.rows, by_complexity: complexityRes.rows, tickets: ticketsRes.rows, total: parseInt(totalRes.rows[0]?.count ?? 0), page, limit, }); }