import { NextRequest, NextResponse } from 'next/server';
import {
  getVariantStats,
  getTopClickedLinks,
  getEventTimeSeries,
  getVariantUniqueStats,
  getVariantDeliveryStats,
  getVariantConversionStats,
  getCampaignConversions,
  getRecipientTouches,
} from '@/lib/db/campaign-repo';
import { db } from '@/lib/db/client';
import { campaigns, workspaces } from '@/lib/db/schema';
import { eq } from 'drizzle-orm';
import { attributeConversion, aggregateAttribution, type AttributionModel } from '@/modules/stats/lib/attribution-index';
import { withAuth } from '@/core/auth';

export const runtime = 'nodejs';
export const dynamic = 'force-dynamic';

/**
 * Liefert Aggregat-Stats für eine Campaign — Variant-Counter, Top-Links, Time-Series.
 *
 * Query-Params:
 *   - bucketMinutes (optional, default 60): Bucket-Größe für die Time-Series
 *   - topLinksLimit (optional, default 10): Anzahl der Top-Clicked-Links
 *
 * Wird vom Stats-Tab im Frontend per Refresh-Button gepollt — keine Subscription,
 * manueller Pull reicht für v1.
 */
export const GET = withAuth<{ params: Promise<{ id: string }> }>(async (req: NextRequest, _ctx, { params }: { params: Promise<{ id: string }> }) => {
  const { id } = await params;
  if (!id) return NextResponse.json({ error: 'Missing campaign id' }, { status: 400 });

  const url = req.nextUrl;
  const bucketMinutes = Math.max(5, Math.min(1440, Number(url.searchParams.get('bucketMinutes')) || 60));
  const topLinksLimit = Math.max(1, Math.min(50, Number(url.searchParams.get('topLinksLimit')) || 10));

  try {
    const variants = getVariantStats(id);
    const uniqueByVariant = getVariantUniqueStats(id);
    const deliveryByVariant = getVariantDeliveryStats(id);
    const conversionByVariant = getVariantConversionStats(id);
    // Zip alle Aggregate auf die Variants per ID
    const uniqueMap = new Map(uniqueByVariant.map(v => [v.variantId, v]));
    const deliveryMap = new Map(deliveryByVariant.map(v => [v.variantId, v]));
    const conversionMap = new Map(conversionByVariant.map(v => [v.variantId, v]));
    // Phase 3: Multi-Touch-Attribution. Workspace der Campaign laden → Settings
    // (attributionModel, attributionLookbackDays) und Multi-Touch über alle
    // Conversions der Campaign anwenden. Wenn keine Conversions / Workspace fehlt,
    // bleiben attributedCount/Value bei 0.
    const camp = db.select({ workspaceId: campaigns.workspaceId }).from(campaigns).where(eq(campaigns.id, id)).get();
    const workspace = camp ? db.select({
      attributionModel: workspaces.attributionModel,
      attributionLookbackDays: workspaces.attributionLookbackDays,
    }).from(workspaces).where(eq(workspaces.id, camp.workspaceId)).get() : null;
    const model: AttributionModel = (workspace?.attributionModel as AttributionModel) ?? 'last-click';
    const lookbackDays = workspace?.attributionLookbackDays ?? 30;

    const conversions = getCampaignConversions(id);
    // Pro Variant: Summe attribuierter Gewichte (für eigene Variants der Campaign)
    // und für externe Variants (andere Campaigns, die Credit bekamen).
    const attributedByVariant = new Map<string, { count: number; value: number }>();
    const attributedExternalCount = { value: 0 };
    for (const conv of conversions) {
      const fromMs = new Date(conv.createdAt.getTime() - lookbackDays * 86400_000);
      const touches = getRecipientTouches(conv.recipientId, fromMs, conv.createdAt);
      const attributed = attributeConversion({
        touches,
        conversionAt: conv.createdAt,
        model,
        lookbackDays,
      });
      const agg = aggregateAttribution(attributed);
      for (const g of agg) {
        if (g.campaignId !== id) {
          // Andere Campaigns dieser Workspace bekommen Credit — wir zählen das hier
          // nicht zur Variant-Stats dazu, behalten es aber als "externer Anteil"
          // für mögliche zukünftige Workspace-übergreifende Dashboards.
          attributedExternalCount.value += g.weight * (conv.value ?? 0);
          continue;
        }
        const key = g.variantId ?? '';
        const cur = attributedByVariant.get(key) ?? { count: 0, value: 0 };
        cur.count += g.weight;
        cur.value += g.weight * (conv.value ?? 0);
        attributedByVariant.set(key, cur);
      }
    }

    const stats = variants.map(v => {
      const attributed = attributedByVariant.get(v.variantId) ?? { count: 0, value: 0 };
      const conv = conversionMap.get(v.variantId);
      return {
        ...v,
        uniqueOpens: uniqueMap.get(v.variantId)?.uniqueOpens ?? 0,
        uniqueOpensVerified: uniqueMap.get(v.variantId)?.uniqueOpensVerified ?? 0,
        uniqueClicks: uniqueMap.get(v.variantId)?.uniqueClicks ?? 0,
        deliveredCount: deliveryMap.get(v.variantId)?.deliveredCount ?? 0,
        bounceCount: deliveryMap.get(v.variantId)?.bounceCount ?? 0,
        conversionCount: conv?.conversionCount ?? 0,
        conversionValueTotal: conv?.conversionValueTotal ?? 0,
        conversionsBySource: conv?.bySource ?? {},
        conversionValueBySource: conv?.valueBySource ?? {},
        attributedConversionCount: Math.round(attributed.count * 100) / 100,
        attributedConversionValue: Math.round(attributed.value),
      };
    });

    const topLinks = getTopClickedLinks(id, topLinksLimit);
    const timeSeries = getEventTimeSeries(id, bucketMinutes);

    return NextResponse.json({
      ok: true,
      stats,
      topLinks,
      timeSeries,
      bucketMinutes,
      attribution: { model, lookbackDays },
    });
  } catch (e) {
    return NextResponse.json(
      { error: e instanceof Error ? e.message : 'Stats lookup failed' },
      { status: 500 },
    );
  }
});
