import { NextRequest, NextResponse } from 'next/server';
import { cookies } from 'next/headers';
import { db } from '@/lib/db';
import { rateLimit, getClientIdentifier, RateLimits } from '@/lib/rateLimit';
import { TrendData } from '@/types/analytics';

export const dynamic = 'force-dynamic';

// Helper function to calculate trend with proper edge case handling
function calculateTrend(current: number, previous: number): TrendData {
  // Handle edge case: no data in either period
  if (previous === 0 && current === 0) {
    return { trend: 'flat', percentage: 0 };
  }

  // Handle new growth from zero - mark as "new" to distinguish from actual 100% growth
  if (previous === 0 && current > 0) {
    return { trend: 'up', percentage: 100, isNew: true };
  }

  // Calculate actual percentage change
  const change = ((current - previous) / previous) * 100;
  const roundedChange = Math.round(change);

  // Determine trend direction (use threshold of 5% to filter noise)
  let trend: 'up' | 'down' | 'flat';
  if (roundedChange > 5) {
    trend = 'up';
  } else if (roundedChange < -5) {
    trend = 'down';
  } else {
    trend = 'flat';
  }

  return {
    trend,
    percentage: Math.abs(roundedChange),
    isNegative: roundedChange < 0
  };
}

export async function GET(request: NextRequest) {
  try {
    const userId = cookies().get('userId')?.value;

    // Rate Limiting (user-based)
    const clientId = userId || getClientIdentifier(request);
    const rateLimitResult = rateLimit(clientId, RateLimits.ANALYTICS);

    if (!rateLimitResult.success) {
      return NextResponse.json(
        {
          error: 'Too many requests. Please try again later.',
          retryAfter: Math.ceil((rateLimitResult.reset - Date.now()) / 1000)
        },
        {
          status: 429,
          headers: {
            'X-RateLimit-Limit': rateLimitResult.limit.toString(),
            'X-RateLimit-Remaining': rateLimitResult.remaining.toString(),
            'X-RateLimit-Reset': rateLimitResult.reset.toString(),
          }
        }
      );
    }
    if (!userId) {
      return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
    }

    // Get date range from query params (default: last 30 days)
    const { searchParams } = request.nextUrl;
    const range = searchParams.get('range') || '30';
    const daysInRange = parseInt(range, 10);

    // Standardize to week (7 days) or month (30 days) for clear comparison labels
    const comparisonDays = daysInRange <= 7 ? 7 : 30;
    const comparisonPeriod: 'week' | 'month' = comparisonDays === 7 ? 'week' : 'month';

    // Calculate current and previous period dates
    const now = new Date();
    const currentPeriodStart = new Date();
    currentPeriodStart.setDate(now.getDate() - comparisonDays);

    const previousPeriodEnd = new Date(currentPeriodStart);
    const previousPeriodStart = new Date(previousPeriodEnd);
    previousPeriodStart.setDate(previousPeriodEnd.getDate() - comparisonDays);

    // Get user's QR codes with scans filtered by period
    const qrCodes = await db.qRCode.findMany({
      where: { userId },
      include: {
        scans: {
          where: {
            ts: {
              gte: currentPeriodStart,
            },
          },
        },
      },
    });

    // Get previous period scans for comparison
    const qrCodesWithPreviousScans = await db.qRCode.findMany({
      where: { userId },
      include: {
        scans: {
          where: {
            ts: {
              gte: previousPeriodStart,
              lt: previousPeriodEnd,
            },
          },
        },
      },
    });

    // Calculate current period stats
    const totalScans = qrCodes.reduce((sum, qr) => sum + qr.scans.length, 0);
    const uniqueScans = qrCodes.reduce((sum, qr) =>
      sum + qr.scans.filter(s => s.isUnique).length, 0
    );

    // Calculate previous period stats for comparison
    const previousTotalScans = qrCodesWithPreviousScans.reduce((sum, qr) => sum + qr.scans.length, 0);
    const previousUniqueScans = qrCodesWithPreviousScans.reduce((sum, qr) =>
      sum + qr.scans.filter(s => s.isUnique).length, 0
    );

    // Calculate average scans per QR code (only count QR codes with scans)
    const qrCodesWithScans = qrCodes.filter(qr => qr.scans.length > 0).length;
    const avgScansPerQR = qrCodesWithScans > 0
      ? Math.round(totalScans / qrCodesWithScans)
      : 0;

    // Calculate previous period average scans per QR
    const previousQrCodesWithScans = qrCodesWithPreviousScans.filter(qr => qr.scans.length > 0).length;
    const previousAvgScansPerQR = previousQrCodesWithScans > 0
      ? Math.round(previousTotalScans / previousQrCodesWithScans)
      : 0;

    // Calculate trends
    const scansTrend = calculateTrend(totalScans, previousTotalScans);

    // New Conversion Rate Logic: (Unique Scans / Total Scans) * 100
    // This represents "Engagement Efficiency" - how many scans are from fresh users
    const currentConversion = totalScans > 0 ? Math.round((uniqueScans / totalScans) * 100) : 0;

    const previousConversion = previousTotalScans > 0
      ? Math.round((previousUniqueScans / previousTotalScans) * 100)
      : 0;

    const avgScansTrend = calculateTrend(avgScansPerQR, previousAvgScansPerQR);

    // Device stats
    const deviceStats = qrCodes.flatMap(qr => qr.scans)
      .reduce((acc, scan) => {
        const device = scan.device || 'unknown';
        acc[device] = (acc[device] || 0) + 1;
        return acc;
      }, {} as Record<string, number>);

    const mobileScans = (deviceStats.mobile || 0) + (deviceStats.tablet || 0);
    const mobilePercentage = totalScans > 0
      ? Math.round((mobileScans / totalScans) * 100)
      : 0;

    // Country stats (current period)
    const countryStats = qrCodes.flatMap(qr => qr.scans)
      .reduce((acc, scan) => {
        const country = scan.country ?? 'Unknown Location';
        acc[country] = (acc[country] || 0) + 1;
        return acc;
      }, {} as Record<string, number>);

    // Country stats (previous period)
    const previousCountryStats = qrCodesWithPreviousScans.flatMap(qr => qr.scans)
      .reduce((acc, scan) => {
        const country = scan.country ?? 'Unknown Location';
        acc[country] = (acc[country] || 0) + 1;
        return acc;
      }, {} as Record<string, number>);

    const topCountry = Object.entries(countryStats)
      .sort(([, a], [, b]) => b - a)[0];

    // Daily scan counts for chart (current period)
    const dailyScans = qrCodes.flatMap(qr => qr.scans).reduce((acc, scan) => {
      const date = new Date(scan.ts).toISOString().split('T')[0];
      acc[date] = (acc[date] || 0) + 1;
      return acc;
    }, {} as Record<string, number>);

    // Generate last 7 days for sparkline
    const last7Days = Array.from({ length: 7 }, (_, i) => {
      const date = new Date();
      date.setDate(date.getDate() - (6 - i));
      return date.toISOString().split('T')[0];
    });

    // QR performance (only show DYNAMIC QR codes since STATIC don't track scans)
    const qrPerformance = qrCodes
      .filter(qr => qr.type === 'DYNAMIC')
      .map(qr => {
        const currentTotal = qr.scans.length;
        const currentUnique = qr.scans.filter(s => s.isUnique).length;

        // Find previous period data for this QR code
        const previousQR = qrCodesWithPreviousScans.find(prev => prev.id === qr.id);
        const previousTotal = previousQR ? previousQR.scans.length : 0;

        // Calculate trend
        const trendData = calculateTrend(currentTotal, previousTotal);

        // Calculate sparkline data (scans per day for last 7 days)
        const sparklineData = last7Days.map(date => {
          return qr.scans.filter(s =>
            new Date(s.ts).toISOString().split('T')[0] === date
          ).length;
        });

        // Find last scanned date
        const lastScanned = qr.scans.length > 0
          ? new Date(Math.max(...qr.scans.map(s => new Date(s.ts).getTime())))
          : null;

        return {
          id: qr.id,
          title: qr.title,
          type: qr.type,
          totalScans: currentTotal,
          uniqueScans: currentUnique,
          conversion: currentTotal > 0
            ? Math.round((currentUnique / currentTotal) * 100)
            : 0,
          trend: trendData.trend,
          trendPercentage: trendData.percentage,
          sparkline: sparklineData,
          lastScanned: lastScanned?.toISOString() || null,
          ...(trendData.isNew && { isNew: true }),
        };
      })
      .sort((a, b) => b.totalScans - a.totalScans);

    return NextResponse.json({
      summary: {
        totalScans,
        uniqueScans,
        avgScansPerQR,
        mobilePercentage,
        topCountry: topCountry ? topCountry[0] : 'N/A',
        topCountryPercentage: topCountry && totalScans > 0
          ? Math.round((topCountry[1] / totalScans) * 100)
          : 0,
        scansTrend,
        avgScansTrend,
        comparisonPeriod,
        comparisonDays,
      },
      deviceStats,
      countryStats: Object.entries(countryStats)
        .sort(([, a], [, b]) => b - a)
        .slice(0, 10)
        .map(([country, count]) => {
          const previousCount = previousCountryStats[country] || 0;
          const trendData = calculateTrend(count, previousCount);

          return {
            country,
            count,
            percentage: totalScans > 0
              ? Math.round((count / totalScans) * 100)
              : 0,
            trend: trendData.trend,
            trendPercentage: trendData.percentage,
            ...(trendData.isNew && { isNew: true }),
          };
        }),
      dailyScans,
      qrPerformance: qrPerformance.slice(0, 10),
    });
  } catch (error) {
    console.error('Error fetching analytics:', error);
    return NextResponse.json(
      { error: 'Internal server error' },
      { status: 500 }
    );
  }
}