import { NextRequest, NextResponse } from "next/server";
import { prisma } from "@/server/db/client";
import { getServerSession } from "next-auth";
import { authOptions } from "@/server/auth/config";
import { getRecentActivities } from "@/server/services/activity-log.service";
import { nowWIB, startOfDayWIBtoUTC } from "@/lib/timezone";

// Disable caching for this route - always fetch fresh data
export const dynamic = 'force-dynamic';
export const revalidate = 0;



export async function GET(request: NextRequest) {
  try {
    // Check authentication
    const session = await getServerSession(authOptions);
    if (!session || !session.user) {
      return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
    }

    const userRole = (session.user as any).role;

    // Parse optional ?month=YYYY-MM param (defaults to current WIB month)
    const { searchParams } = new URL(request.url);
    const monthParam = searchParams.get('month'); // e.g. "2026-02"
    const nowLocal = nowWIB();
    let selectedYear: number;
    let selectedMonth: number; // 0-indexed
    if (monthParam && /^\d{4}-\d{2}$/.test(monthParam)) {
      [selectedYear, selectedMonth] = monthParam.split('-').map(Number);
      selectedMonth -= 1; // convert 1-indexed to 0-indexed
    } else {
      selectedYear = nowLocal.getUTCFullYear();
      selectedMonth = nowLocal.getUTCMonth();
    }
    const monthKey = `${selectedYear}-${String(selectedMonth + 1).padStart(2, '0')}`;

    const result = await (async () => {
    const now = nowWIB();
    const startOfMonth = new Date(Date.UTC(selectedYear, selectedMonth, 1));
    const startOfNextMonth = new Date(Date.UTC(selectedYear, selectedMonth + 1, 1));
    // last day of selected month (handles 28/29/30/31 days correctly)
    const endOfMonth = new Date(Date.UTC(selectedYear, selectedMonth + 1, 0, 23, 59, 59, 999));
    const MONTH_NAMES_ID = ['Januari','Februari','Maret','April','Mei','Juni','Juli','Agustus','September','Oktober','November','Desember'];
    const periodLabel = `${MONTH_NAMES_ID[selectedMonth]} ${selectedYear}`;
    const isCurrentMonth = (selectedYear === now.getUTCFullYear() && selectedMonth === now.getUTCMonth());

    // ==================== 1. Total PPPoE Users ====================
    let totalPppoeUsers = 0;
    try {
      totalPppoeUsers = await prisma.pppoeUser.count();
    } catch (e) {
      console.error('[Dashboard] Error counting pppoeUser:', e);
    }

    // ==================== 2 & 3. Active Sessions (PPPoE & Hotspot separate) ====================
    let activeSessionsPPPoE = 0;
    let activeSessionsHotspot = 0;

    try {
      // ── Sumber kebenaran: radacct ──
      // Sama dengan halaman Sesi: if username ada di pppoeUser → PPPoE, else → Hotspot.
      const normalizeUsername = (u: string) => u.includes('@') ? u.split('@')[0] : u;

      const activeRadacctSessions = await prisma.radacct.findMany({
        where: { acctstoptime: null },
        select: { username: true },
      });

      const onlineUsernames = new Set<string>(
        activeRadacctSessions.map(s => s.username).filter(Boolean) as string[]
      );

      const allUsernames = [...onlineUsernames];
      let pppoeUsernameSet = new Set<string>();

      if (allUsernames.length > 0) {
        const normalizedUsernames = [...new Set(allUsernames.map(normalizeUsername))];
        const [pppoeUsers, hotspotVouchers] = await Promise.all([
          prisma.pppoeUser.findMany({
            where: {
              OR: [
                { username: { in: allUsernames } },
                { username: { in: normalizedUsernames } },
              ],
            },
            select: { username: true },
          }),
          prisma.hotspotVoucher.findMany({
            where: { code: { in: allUsernames } },
            select: { code: true },
          }),
        ]);

        pppoeUsernameSet = new Set(pppoeUsers.map(u => u.username.toLowerCase()));
        const hotspotVoucherSet = new Set(hotspotVouchers.map(v => v.code));

        for (const username of allUsernames) {
          const raw = username.toLowerCase();
          const normalized = normalizeUsername(username).toLowerCase();
          if (pppoeUsernameSet.has(raw) || pppoeUsernameSet.has(normalized)) {
            activeSessionsPPPoE++;
          } else if (hotspotVoucherSet.has(username)) {
            // Only count as hotspot if username is a registered voucher code
            activeSessionsHotspot++;
          }
          // else: unregistered user — skip entirely (ghost session)
        }
      }

      // Supplement: count synthetic ACTIVE hotspot vouchers.
      // These are vouchers that are ACTIVE + firstLoginAt set but have NO active
      // radacct row. Matches the same logic as /api/sessions synthetic sessions:
      //   - Exclude vouchers already counted via active radacct (in onlineUsernames)
      //   - For vouchers with prior stopped records, only count if latest stop is
      //     BEFORE firstLoginAt (new login has no Accounting-Start yet).
      const nowTs = new Date();
      const activeCandidates = await prisma.hotspotVoucher.findMany({
        where: {
          status: 'ACTIVE',
          firstLoginAt: { not: null },
          // Exclude vouchers already counted via active radacct
          code: { notIn: [...onlineUsernames] },
          // Exclude already-expired vouchers whose status hasn't been updated yet
          OR: [
            { expiresAt: null },
            { expiresAt: { gt: nowTs } },
          ],
        },
        select: { code: true, firstLoginAt: true },
      });
      if (activeCandidates.length > 0) {
        const candidateCodes = activeCandidates.map(v => v.code);
        // Find the latest stopped session per voucher code
        const stoppedRows = await prisma.radacct.findMany({
          where: {
            username: { in: candidateCodes },
            acctstoptime: { not: null },
          },
          select: { username: true, acctstoptime: true },
          orderBy: { acctstoptime: 'desc' },
        });
        const latestStopMap = new Map<string, Date>();
        for (const r of stoppedRows) {
          if (r.acctstoptime && !latestStopMap.has(r.username)) {
            latestStopMap.set(r.username, new Date(r.acctstoptime));
          }
        }
        // Count vouchers where session is still "active" (not properly stopped after last login)
        const syntheticCount = activeCandidates.filter(v => {
          const latestStop = latestStopMap.get(v.code);
          if (!latestStop || !v.firstLoginAt) return true; // No prior stop → count as synthetic
          // Count if latest stop is BEFORE firstLoginAt (new login has no Accounting-Start yet)
          return latestStop.getTime() < new Date(v.firstLoginAt).getTime();
        }).length;
        activeSessionsHotspot += syntheticCount;
      }
    } catch (e) {
      console.error('[Dashboard] Error counting active sessions:', e);
    }

    // ==================== 4. Unused Hotspot Vouchers ====================
    let unusedVouchers = 0;
    try {
      unusedVouchers = await prisma.hotspotVoucher.count({
        where: {
          status: 'WAITING',
          firstLoginAt: null,
        },
      });
    } catch (e) {
      console.error('[Dashboard] Error counting unused vouchers:', e);
    }

    // ==================== 5. Isolated / Blocked Customers ====================
    let isolatedCount = 0;
    try {
      isolatedCount = await prisma.pppoeUser.count({
        where: { status: { in: ['isolated', 'ISOLATED', 'blocked', 'BLOCKED'] } },
      });
    } catch (e) {
      console.error('[Dashboard] Error counting isolated users:', e);
    }

    // ==================== 6. Suspended Customers (Stop Langganan) ====================
    let suspendedCount = 0;
    try {
      suspendedCount = await prisma.pppoeUser.count({
        where: { status: { in: ['suspended', 'SUSPENDED'] } },
      });
    } catch (e) {
      console.error('[Dashboard] Error counting suspended users:', e);
    }

    // ==================== 6b. Active PPPoE Customers ====================
    let activePppoeUsers = 0;
    try {
      activePppoeUsers = await prisma.pppoeUser.count({
        where: { status: { in: ['active', 'ACTIVE'] } },
      });
    } catch (e) {
      console.error('[Dashboard] Error counting active users:', e);
    }

    // ==================== 6c. New Online Registrations (pending review) ====================
    let newRegistrations = 0;
    try {
      newRegistrations = await prisma.registrationRequest.count({
        where: { status: { in: ['PENDING', 'REVIEWING'] } },
      });
    } catch (e) {
      console.error('[Dashboard] Error counting new registrations:', e);
    }

    // ==================== 6d. Upcoming / Overdue Invoices (H-7 window) ====================
    type UpcomingInvoice = {
      invoiceNumber: string;
      customerName: string;
      customerUsername: string;
      amount: number;
      dueDate: string;
      status: string;
      daysUntilDue: number;
    };
    let upcomingInvoices: UpcomingInvoice[] = [];
    try {
      const sevenDaysFromNow = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000);
      const raw = await prisma.invoice.findMany({
        where: {
          status: { in: ['PENDING', 'OVERDUE'] },
          dueDate: { lte: sevenDaysFromNow },
        },
        orderBy: { dueDate: 'asc' },
        take: 20,
        select: {
          invoiceNumber: true,
          customerName: true,
          customerUsername: true,
          amount: true,
          dueDate: true,
          status: true,
        },
      });
      upcomingInvoices = raw.map(inv => ({
        invoiceNumber: inv.invoiceNumber,
        customerName: inv.customerName || '-',
        customerUsername: inv.customerUsername || '-',
        amount: inv.amount,
        dueDate: inv.dueDate.toISOString(),
        status: inv.status,
        daysUntilDue: Math.ceil(
          (new Date(inv.dueDate).getTime() - now.getTime()) / (1000 * 60 * 60 * 24)
        ),
      }));
    } catch (e) {
      console.error('[Dashboard] Error loading upcoming invoices:', e);
    }

    // ==================== 7. Voucher Revenue (today + this month) ====================
    let voucherRevenue = 0;
    let voucherRevenueToday = 0;
    try {
      const startOfToday = startOfDayWIBtoUTC(now);

      // Estimate from sold vouchers — today
      const soldToday = await prisma.hotspotVoucher.findMany({
        where: {
          status: { in: ['ACTIVE', 'EXPIRED'] },
          firstLoginAt: { gte: startOfToday },
        },
        include: { profile: { select: { sellingPrice: true } } },
      });
      voucherRevenueToday = soldToday.reduce((sum, v) => sum + (v.profile?.sellingPrice || 0), 0);

      // Try from Keuangan transactions with hotspot/voucher category for this month
      const voucherCategory = await prisma.transactionCategory.findFirst({
        where: {
          OR: [
            { name: { contains: 'hotspot' } },
            { name: { contains: 'voucher' } },
          ],
          type: 'INCOME',
        },
      });

      if (voucherCategory) {
        const voucherIncome = await prisma.transaction.aggregate({
          where: {
            type: 'INCOME',
            categoryId: voucherCategory.id,
            date: { gte: startOfMonth, lt: startOfNextMonth },
          },
          _sum: { amount: true },
        });
        voucherRevenue = Number(voucherIncome._sum.amount) || 0;
      }

      // If no category found, estimate from sold vouchers this month
      if (voucherRevenue === 0) {
        const soldVouchers = await prisma.hotspotVoucher.findMany({
          where: {
            status: { in: ['ACTIVE', 'EXPIRED'] },
            firstLoginAt: { gte: startOfMonth, lt: startOfNextMonth },
          },
          include: { profile: { select: { sellingPrice: true } } },
        });
        voucherRevenue = soldVouchers.reduce((sum, v) => sum + (v.profile?.sellingPrice || 0), 0);
      }
    } catch (e) {
      console.error('[Dashboard] Error calculating voucher revenue:', e);
    }

    // ==================== 8. Invoice Revenue — today, this month, unpaid, all-time ====================
    let invoiceRevenue = 0;
    let invoiceRevenueToday = 0;
    let invoiceCountToday = 0;
    let invoiceCountMonth = 0;
    let unpaidInvoicesCount = 0;
    let totalAllTimeRevenue = 0;
    try {
      // Use local Date constructor so mysql2 sends WIB midnight string to MySQL
      // (now.getUTCDate/Month/FullYear are WIB calendar values since now=nowWIB())
      const startOfToday = new Date(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), 0, 0, 0, 0);

      const [todayAgg, monthAgg, monthCount, unpaidCount, allTimeAgg] = await Promise.all([
        prisma.invoice.aggregate({
          where: { status: 'PAID', paidAt: { gte: startOfToday } },
          _sum: { amount: true },
          _count: { id: true },
        }),
        prisma.invoice.aggregate({
          where: { status: 'PAID', paidAt: { gte: startOfMonth, lt: startOfNextMonth } },
          _sum: { amount: true },
        }),
        prisma.invoice.count({
          where: { status: 'PAID', paidAt: { gte: startOfMonth, lt: startOfNextMonth } },
        }),
        prisma.invoice.count({
          where: { status: { in: ['PENDING', 'OVERDUE'] } },
        }),
        prisma.invoice.aggregate({
          where: { status: 'PAID' },
          _sum: { amount: true },
        }),
      ]);

      invoiceRevenueToday = Number(todayAgg._sum.amount) || 0;
      invoiceCountToday = todayAgg._count.id || 0;
      invoiceRevenue = Number(monthAgg._sum.amount) || 0;
      invoiceCountMonth = monthCount;
      unpaidInvoicesCount = unpaidCount;
      totalAllTimeRevenue = Number(allTimeAgg._sum.amount) || 0;
    } catch (e) {
      console.error('[Dashboard] Error calculating invoice revenue:', e);
    }

    // ==================== 9. Agent Voucher Sales (this month) ====================
    const agentSalesData: { agentId: string; agentName: string; sold: number; revenue: number }[] = [];
    let agentSalesTotal = { count: 0, revenue: 0 };
    try {
      const soldVouchersByAgent = await prisma.hotspotVoucher.findMany({
        where: {
          agentId: { not: null },
          firstLoginAt: { gte: startOfMonth, lt: startOfNextMonth },
        },
        select: {
          agentId: true,
          agent: { select: { name: true } },
          profile: { select: { sellingPrice: true } },
        },
      });

      const agentMap = new Map<string, { name: string; sold: number; revenue: number }>();
      for (const v of soldVouchersByAgent) {
        if (!v.agentId) continue;
        const entry = agentMap.get(v.agentId) ?? { name: v.agent?.name ?? v.agentId, sold: 0, revenue: 0 };
        entry.sold += 1;
        entry.revenue += v.profile?.sellingPrice ?? 0;
        agentMap.set(v.agentId, entry);
      }

      for (const [agentId, data] of agentMap.entries()) {
        agentSalesData.push({ agentId, agentName: data.name, sold: data.sold, revenue: data.revenue });
        agentSalesTotal.count += data.sold;
        agentSalesTotal.revenue += data.revenue;
      }
      agentSalesData.sort((a, b) => b.sold - a.sold);
      agentSalesData.splice(5); // top 5 only
    } catch (e) {
      console.error('[Dashboard] Error loading agent sales:', e);
    }

    // ==================== 10. RADIUS Auth Log ====================
    let radiusAuthLog: { username: string; reply: string; authdate: Date | string }[] = [];
    let radiusAuthStats = { acceptToday: 0, rejectToday: 0 };
    try {
      const startOfToday = startOfDayWIBtoUTC(now);

      [radiusAuthLog, radiusAuthStats.acceptToday, radiusAuthStats.rejectToday] = await Promise.all([
        prisma.radpostauth.findMany({
          orderBy: { authdate: 'desc' },
          take: 15,
          select: { username: true, reply: true, authdate: true },
        }),
        prisma.radpostauth.count({
          where: { reply: 'Access-Accept', authdate: { gte: startOfToday } },
        }),
        prisma.radpostauth.count({
          where: { reply: 'Access-Reject', authdate: { gte: startOfToday } },
        }),
      ]);
    } catch (e) {
      console.error('[Dashboard] Error loading RADIUS auth log:', e);
    }

    // ==================== Recent Activities ====================
    const activities = await getRecentActivities(10);

    // ==================== System Status ====================
    let radiusStatus = false;
    const databaseStatus = true;
    const apiStatus = true;

    try {
      const recentRadacct = await prisma.radacct.findFirst({
        where: {
          acctstarttime: { gte: new Date(now.getTime() - 3600000) },
        },
      });
      radiusStatus = !!recentRadacct;
    } catch (error) {
      radiusStatus = false;
    }

    // ==================== Format currency ====================
    const formatCurrency = (amount: number) =>
      new Intl.NumberFormat("id-ID", {
        style: "currency",
        currency: "IDR",
        minimumFractionDigits: 0,
        maximumFractionDigits: 0,
      }).format(amount);

    return {
      stats: {
        totalPppoeUsers,
        activePppoeUsers,
        activeSessionsPPPoE,
        activeSessionsHotspot,
        unusedVouchers,
        isolatedCount,
        suspendedCount,
        newRegistrations,
        upcomingInvoices,
        voucherRevenue,
        voucherRevenueFormatted: formatCurrency(voucherRevenue),
        voucherRevenueToday,
        voucherRevenueTodayFormatted: formatCurrency(voucherRevenueToday),
        invoiceRevenue,
        invoiceRevenueFormatted: formatCurrency(invoiceRevenue),
        invoiceRevenueToday,
        invoiceRevenueTodayFormatted: formatCurrency(invoiceRevenueToday),
        invoiceCountToday,
        invoiceCountMonth,
        unpaidInvoicesCount,
        totalAllTimeRevenue,
        totalAllTimeRevenueFormatted: formatCurrency(totalAllTimeRevenue),
      },
      activities,
      systemStatus: {
        radius: radiusStatus,
        database: databaseStatus,
        api: apiStatus,
      },
      agentSales: agentSalesData,
      agentSalesTotal,
      radiusAuthLog,
      radiusAuthStats,
      periodLabel,
      monthKey,
      isCurrentMonth,
    };
    })(); // end async IIFE

    return NextResponse.json({ success: true, ...result });
  } catch (error: any) {
    console.error("Dashboard stats error:", error);
    return NextResponse.json(
      { success: false, error: error.message },
      { status: 500 },
    );
  }
}
