'use client';

/**
 * Stats-Tab für eine versendete Campaign.
 *
 * Liest /api/campaigns/[id]/stats und rendert:
 *  - Aggregat-Tiles pro Variante: sent, open-rate, click-rate, unique opens/clicks, conversions
 *  - Top-Clicked-Links als Tabelle
 *  - Time-Series der ersten 24-72h als CSS-only Bar-Chart (3 Stacks: opens, clicks, conversions)
 *
 * Keine Subscription — manueller Refresh-Button reicht für v1. Bei aktivem Send wird automatisch
 * alle 30s gepollt (Tab im Vordergrund).
 */

import { useCallback, useEffect, useState, type ReactNode } from 'react';
import { BarChart3, ExternalLink, Loader2, RefreshCw, AlertTriangle, Eye, MousePointerClick, Target as TargetIcon, MailCheck, AlertOctagon, ShieldCheck, BadgeEuro, Users } from 'lucide-react';
import { cn } from '@/lib/utils';
import { RecipientsTable } from './RecipientsTable';
import { RecipientTimelineDrawer } from './RecipientTimelineDrawer';

type StatsSubTab = 'overview' | 'recipients';

interface VariantStat {
  variantId: string;
  label: string;
  sentCount: number;
  openCount: number;
  clickCount: number;
  uniqueOpens: number;
  /** MPP-bereinigt — nur Events mit is_mpp=0|NULL. */
  uniqueOpensVerified: number;
  uniqueClicks: number;
  /** Anzahl 'delivered'-Events (Microsoft-Accept-Bestätigung). */
  deliveredCount: number;
  /** Anzahl 'bounce'-Events (kommt erst mit Inbox-Scanner in Phase 3). */
  bounceCount: number;
  /** Anzahl 'conversion'-Events (Pixel + API-Poll kombiniert). */
  conversionCount: number;
  /** Summierter conversion.value (Cent-Werte oder ganze EUR — siehe recordConversion). */
  conversionValueTotal: number;
  /** Phase 3: Conversion-Counts pro Source (pixel/webhook/api_poll/manual/system). */
  conversionsBySource?: Record<string, number>;
  conversionValueBySource?: Record<string, number>;
  /** Phase 3: Multi-Touch-attribuierte Anzahl (kann Bruchteil sein bei linear/time-decay). */
  attributedConversionCount?: number;
  /** Phase 3: Multi-Touch-attribuierter Wert (Summe weight × value). */
  attributedConversionValue?: number;
  assignedRecipients: number;
}

interface TopLink {
  url: string;
  clicks: number;
  uniqueRecipients: number;
}

interface TimeBucket {
  bucketStart: number;
  opens: number;
  clicks: number;
  conversions: number;
}

interface StatsResponse {
  ok: boolean;
  stats: VariantStat[];
  topLinks: TopLink[];
  timeSeries: TimeBucket[];
  bucketMinutes: number;
  /** Phase 3: Workspace-Setting der Campaign — bestimmt wie attribuiert wird. */
  attribution?: {
    model: 'last-click' | 'first-click' | 'linear' | 'time-decay';
    lookbackDays: number;
  };
}

export interface CampaignStatsPanelProps {
  campaignId: string;
  /** Wenn true → automatischer Refresh alle 30s (Campaign aktiv im Send). */
  autoRefresh?: boolean;
  className?: string;
}

export function CampaignStatsPanel({ campaignId, autoRefresh = false, className }: CampaignStatsPanelProps) {
  const [data, setData] = useState<StatsResponse | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [bucketMinutes, setBucketMinutes] = useState(60);
  const [subTab, setSubTab] = useState<StatsSubTab>('overview');
  const [openRecipientId, setOpenRecipientId] = useState<string | null>(null);

  const fetchStats = useCallback(async () => {
    setLoading(true);
    setError(null);
    try {
      const res = await fetch(`/api/campaigns/${campaignId}/stats?bucketMinutes=${bucketMinutes}&topLinksLimit=10`);
      if (!res.ok) throw new Error(`Stats lookup failed (${res.status})`);
      const json: StatsResponse = await res.json();
      setData(json);
    } catch (e) {
      setError(e instanceof Error ? e.message : 'Stats lookup failed');
    } finally {
      setLoading(false);
    }
  }, [campaignId, bucketMinutes]);

  // Initial-Load + Re-Load bei Bucket-Größe-Änderung
  useEffect(() => { void fetchStats(); }, [fetchStats]);

  // Optional Auto-Refresh — nur wenn explizit angefordert
  useEffect(() => {
    if (!autoRefresh) return;
    const id = setInterval(() => void fetchStats(), 30_000);
    return () => clearInterval(id);
  }, [autoRefresh, fetchStats]);

  return (
    <div className={cn('flex flex-col gap-6', className)}>
      {/* ─── Header ─────────────────────────────────────────────── */}
      <div className="flex items-center justify-between">
        <div className="flex items-center gap-2">
          <BarChart3 className="h-5 w-5 text-zinc-500" />
          <h2 className="text-base font-semibold text-zinc-900 dark:text-zinc-100">Kampagnen-Stats</h2>
          {autoRefresh && (
            <span className="text-[11px] text-zinc-500">· auto-refresh 30s</span>
          )}
        </div>
        <div className="flex items-center gap-2">
          <select
            value={bucketMinutes}
            onChange={(e) => setBucketMinutes(Number(e.target.value))}
            className="rounded-md border border-zinc-200 bg-white px-2 py-1 text-xs text-zinc-700 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-300"
          >
            <option value={15}>15-Min-Buckets</option>
            <option value={60}>Stündlich</option>
            <option value={360}>6-Stunden</option>
            <option value={1440}>Täglich</option>
          </select>
          <button
            type="button"
            onClick={() => void fetchStats()}
            disabled={loading}
            className="inline-flex items-center gap-1.5 rounded-md border border-zinc-200 bg-white px-2.5 py-1 text-xs text-zinc-700 transition hover:bg-zinc-50 disabled:opacity-50 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-300 dark:hover:bg-zinc-800"
          >
            {loading ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <RefreshCw className="h-3.5 w-3.5" />}
            Refresh
          </button>
        </div>
      </div>

      {error && (
        <div className="flex items-start gap-2 rounded-md border border-red-200 bg-red-50 px-3 py-2 text-xs text-red-800 dark:border-red-900/50 dark:bg-red-950/30">
          <AlertTriangle className="h-4 w-4 mt-0.5" />
          <span className="flex-1">{error}</span>
        </div>
      )}

      {!data && !error && loading && (
        <div className="flex items-center justify-center py-12 text-sm text-zinc-500">
          <Loader2 className="mr-2 h-4 w-4 animate-spin" /> Lade Stats…
        </div>
      )}

      {data && (
        <>
          {/* ─── Sub-Tab-Switcher ───────────────────────────────── */}
          <div className="flex items-center gap-1 border-b border-zinc-200 dark:border-zinc-800">
            <SubTabButton
              active={subTab === 'overview'}
              onClick={() => setSubTab('overview')}
              icon={<BarChart3 className="h-3.5 w-3.5" />}
              label="Übersicht"
            />
            <SubTabButton
              active={subTab === 'recipients'}
              onClick={() => setSubTab('recipients')}
              icon={<Users className="h-3.5 w-3.5" />}
              label="Empfänger"
            />
          </div>

          {subTab === 'overview' && (
            <>
              {/* ─── Variant-Tiles ──────────────────────────────────── */}
              <VariantTilesGrid stats={data.stats} />

              {/* ─── Time-Series ────────────────────────────────────── */}
              <TimeSeriesChart buckets={data.timeSeries} bucketMinutes={data.bucketMinutes} />

              {/* ─── Top-Clicked-Links ──────────────────────────────── */}
              <TopLinksTable links={data.topLinks} />
            </>
          )}

          {subTab === 'recipients' && (
            <RecipientsTable
              campaignId={campaignId}
              onOpenRecipient={setOpenRecipientId}
            />
          )}
        </>
      )}

      <RecipientTimelineDrawer
        recipientId={openRecipientId}
        onClose={() => setOpenRecipientId(null)}
      />
    </div>
  );
}

function SubTabButton({
  active,
  onClick,
  icon,
  label,
}: {
  active: boolean;
  onClick: () => void;
  icon: ReactNode;
  label: string;
}) {
  return (
    <button
      type="button"
      onClick={onClick}
      className={cn(
        'inline-flex items-center gap-1.5 border-b-2 px-3 py-2 text-xs font-medium transition',
        active
          ? 'border-zinc-900 text-zinc-900 dark:border-zinc-100 dark:text-zinc-100'
          : 'border-transparent text-zinc-500 hover:text-zinc-700 dark:hover:text-zinc-300',
      )}
    >
      {icon}
      {label}
    </button>
  );
}

// ─── Variant Tiles ──────────────────────────────────────────────────────────

function VariantTilesGrid({ stats }: { stats: VariantStat[] }) {
  if (stats.length === 0) {
    return (
      <div className="rounded-lg border border-dashed border-zinc-300 px-4 py-8 text-center text-sm text-zinc-500 dark:border-zinc-700">
        Noch keine Variants vorhanden — wurde die Campaign schon versendet?
      </div>
    );
  }
  return (
    <div className={cn(
      'grid gap-3',
      stats.length === 1 ? 'grid-cols-1' : stats.length === 2 ? 'grid-cols-2' : 'grid-cols-2 lg:grid-cols-3',
    )}>
      {stats.map(v => <VariantTile key={v.variantId} stat={v} />)}
    </div>
  );
}

function VariantTile({ stat }: { stat: VariantStat }) {
  // Verified-Open-Rate (MPP-bereinigt) ist die Hauptmetrik — Apple öffnet sonst alles
  // automatisch und bläst die Open-Rate auf. Raw bleibt als Sub-Info sichtbar.
  const verifiedOpenRate = stat.sentCount > 0 ? (stat.uniqueOpensVerified / stat.sentCount) * 100 : 0;
  const rawOpenRate = stat.sentCount > 0 ? (stat.uniqueOpens / stat.sentCount) * 100 : 0;
  const clickRate = stat.sentCount > 0 ? (stat.uniqueClicks / stat.sentCount) * 100 : 0;
  // CTOR auf Basis verified opens — sonst künstlich gedrückt durch MPP-Inflation
  const ctor = stat.uniqueOpensVerified > 0 ? (stat.uniqueClicks / stat.uniqueOpensVerified) * 100 : 0;
  const deliveryRate = stat.sentCount > 0 ? (stat.deliveredCount / stat.sentCount) * 100 : 0;
  const bounceRate = stat.sentCount > 0 ? (stat.bounceCount / stat.sentCount) * 100 : 0;
  const conversionRate = stat.sentCount > 0 ? (stat.conversionCount / stat.sentCount) * 100 : 0;
  const mppShare = stat.uniqueOpens > 0 ? ((stat.uniqueOpens - stat.uniqueOpensVerified) / stat.uniqueOpens) * 100 : 0;

  return (
    <div className="flex flex-col gap-3 rounded-lg border border-zinc-200 bg-white p-4 dark:border-zinc-700 dark:bg-zinc-900">
      <div className="flex items-center justify-between">
        <div className="flex items-center gap-1.5">
          <span className="rounded bg-zinc-900 px-1.5 py-0.5 text-[10px] font-bold text-white dark:bg-zinc-200 dark:text-zinc-900">
            {stat.label}
          </span>
          <span className="text-xs text-zinc-500">
            {stat.sentCount} / {stat.assignedRecipients} sent
          </span>
        </div>
        {bounceRate > 2 && (
          <span className="inline-flex items-center gap-1 rounded-full bg-red-100 px-2 py-0.5 text-[10px] font-semibold text-red-700 dark:bg-red-950/40 dark:text-red-300">
            <AlertOctagon className="h-3 w-3" /> {bounceRate.toFixed(1)}% Bounce
          </span>
        )}
      </div>

      {/* Primär-Metriken: Verified-Open, Click, CTOR */}
      <div className="grid grid-cols-3 gap-2">
        <Metric
          icon={<ShieldCheck className="h-3 w-3" />}
          label="Verified Open"
          value={`${verifiedOpenRate.toFixed(1)}%`}
          sub={`${stat.uniqueOpensVerified} echte Opens`}
        />
        <Metric
          icon={<MousePointerClick className="h-3 w-3" />}
          label="Click-Rate"
          value={`${clickRate.toFixed(1)}%`}
          sub={`${stat.uniqueClicks} unique`}
        />
        <Metric
          icon={<TargetIcon className="h-3 w-3" />}
          label="CTOR"
          value={`${ctor.toFixed(1)}%`}
          sub="click / verified open"
        />
      </div>

      {/* Sekundär-Metriken: Delivery, Conversion, Raw-Opens */}
      <div className="grid grid-cols-3 gap-2 border-t border-zinc-100 pt-3 dark:border-zinc-800">
        <Metric
          icon={<MailCheck className="h-3 w-3" />}
          label="Delivery"
          value={`${deliveryRate.toFixed(1)}%`}
          sub={`${stat.deliveredCount} angekommen`}
        />
        <Metric
          icon={<BadgeEuro className="h-3 w-3" />}
          label="Conversion"
          value={`${conversionRate.toFixed(1)}%`}
          sub={stat.conversionValueTotal > 0 ? `${formatEuro(stat.conversionValueTotal)} Wert` : `${stat.conversionCount} Conversions`}
        />
        <Metric
          icon={<Eye className="h-3 w-3" />}
          label="Raw Open"
          value={`${rawOpenRate.toFixed(1)}%`}
          sub={mppShare > 0 ? `${mppShare.toFixed(0)}% MPP-Anteil` : 'kein MPP erkannt'}
        />
      </div>

      <div className="text-[11px] text-zinc-400">
        Roh-Counter: {stat.openCount} opens · {stat.clickCount} clicks · {stat.deliveredCount} delivered
      </div>

      {/* Phase 3: Source-Breakdown + Attributed Count */}
      {stat.conversionCount > 0 && stat.conversionsBySource && Object.keys(stat.conversionsBySource).length > 0 && (
        <div className="border-t border-zinc-100 pt-2 text-[11px] dark:border-zinc-800">
          <div className="text-zinc-500">
            Quelle:{' '}
            {Object.entries(stat.conversionsBySource)
              .filter(([, n]) => n > 0)
              .map(([src, n]) => `${n} ${src}`)
              .join(' · ')}
          </div>
          {typeof stat.attributedConversionCount === 'number' && stat.attributedConversionCount > 0 && (
            <div className="text-zinc-500">
              Attribuiert: <strong className="text-zinc-700 dark:text-zinc-300">{stat.attributedConversionCount.toFixed(2)}</strong>
              {typeof stat.attributedConversionValue === 'number' && stat.attributedConversionValue > 0 && (
                <> · {formatEuro(stat.attributedConversionValue)}</>
              )}
            </div>
          )}
        </div>
      )}
    </div>
  );
}

function formatEuro(value: number): string {
  // value kann je nach Quelle EUR (api_poll) oder Cent (pixel.value) sein — wir gehen vom
  // gespeicherten Ganzzahl-Wert aus und formatieren als ganze EUR; bei sehr großen Zahlen
  // sind das vermutlich Cent. Für v1: einfach mit Tausender-Trennung anzeigen.
  return value.toLocaleString('de-DE', { style: 'currency', currency: 'EUR', maximumFractionDigits: 0 });
}

function Metric({ icon, label, value, sub }: { icon: React.ReactNode; label: string; value: string; sub: string }) {
  return (
    <div className="flex flex-col">
      <div className="flex items-center gap-1 text-[10px] font-medium uppercase tracking-wide text-zinc-500">
        {icon}
        {label}
      </div>
      <div className="text-lg font-bold text-zinc-900 dark:text-zinc-100">{value}</div>
      <div className="text-[10px] text-zinc-400">{sub}</div>
    </div>
  );
}

// ─── Time-Series Bar-Chart (CSS-only) ───────────────────────────────────────

function TimeSeriesChart({ buckets, bucketMinutes }: { buckets: TimeBucket[]; bucketMinutes: number }) {
  if (buckets.length === 0) {
    return (
      <div className="rounded-lg border border-dashed border-zinc-300 px-4 py-6 text-center text-sm text-zinc-500 dark:border-zinc-700">
        Noch keine Events — Time-Series leer.
      </div>
    );
  }

  // Skala: max(opens+clicks+conversions) über alle Buckets — alle Bars relativ dazu
  const max = Math.max(1, ...buckets.map(b => b.opens + b.clicks + b.conversions));

  return (
    <div className="rounded-lg border border-zinc-200 bg-white p-4 dark:border-zinc-700 dark:bg-zinc-900">
      <div className="mb-3 flex items-center justify-between">
        <h3 className="text-xs font-semibold uppercase tracking-wide text-zinc-500">
          Event-Zeitverlauf · {bucketMinutes < 60 ? `${bucketMinutes}-Min-Buckets` : `${bucketMinutes / 60}h-Buckets`}
        </h3>
        <div className="flex items-center gap-3 text-[10px]">
          <Legend color="bg-blue-500" label="Opens" />
          <Legend color="bg-emerald-500" label="Clicks" />
          <Legend color="bg-amber-500" label="Conversions" />
        </div>
      </div>
      <div className="flex h-40 items-end gap-px">
        {buckets.map((b) => {
          const total = b.opens + b.clicks + b.conversions;
          const heightPct = (total / max) * 100;
          return (
            <div
              key={b.bucketStart}
              className="group relative flex h-full flex-1 flex-col justify-end"
              title={formatBucketTooltip(b, bucketMinutes)}
            >
              <div
                className="flex flex-col-reverse overflow-hidden rounded-sm"
                style={{ height: `${heightPct}%` }}
              >
                {b.opens > 0 && (
                  <div className="bg-blue-500" style={{ flexGrow: b.opens }} />
                )}
                {b.clicks > 0 && (
                  <div className="bg-emerald-500" style={{ flexGrow: b.clicks }} />
                )}
                {b.conversions > 0 && (
                  <div className="bg-amber-500" style={{ flexGrow: b.conversions }} />
                )}
              </div>
            </div>
          );
        })}
      </div>
      <div className="mt-2 flex items-center justify-between text-[10px] text-zinc-400">
        <span>{new Date(buckets[0].bucketStart).toLocaleString('de-DE', { dateStyle: 'short', timeStyle: 'short' })}</span>
        <span>{new Date(buckets[buckets.length - 1].bucketStart).toLocaleString('de-DE', { dateStyle: 'short', timeStyle: 'short' })}</span>
      </div>
    </div>
  );
}

function Legend({ color, label }: { color: string; label: string }) {
  return (
    <span className="flex items-center gap-1 text-zinc-500">
      <span className={cn('h-2 w-2 rounded-sm', color)} />
      {label}
    </span>
  );
}

function formatBucketTooltip(b: TimeBucket, bucketMinutes: number): string {
  const start = new Date(b.bucketStart);
  const end = new Date(b.bucketStart + bucketMinutes * 60 * 1000);
  const range = `${start.toLocaleString('de-DE', { dateStyle: 'short', timeStyle: 'short' })} – ${end.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' })}`;
  return `${range}\nOpens: ${b.opens} · Clicks: ${b.clicks} · Conversions: ${b.conversions}`;
}

// ─── Top-Clicked-Links ──────────────────────────────────────────────────────

function TopLinksTable({ links }: { links: TopLink[] }) {
  if (links.length === 0) {
    return (
      <div className="rounded-lg border border-dashed border-zinc-300 px-4 py-6 text-center text-sm text-zinc-500 dark:border-zinc-700">
        Noch keine Klicks registriert.
      </div>
    );
  }
  return (
    <div className="rounded-lg border border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900">
      <div className="border-b border-zinc-200 px-4 py-2 dark:border-zinc-700">
        <h3 className="text-xs font-semibold uppercase tracking-wide text-zinc-500">Top-geklickte Links</h3>
      </div>
      <table className="w-full text-xs">
        <thead>
          <tr className="border-b border-zinc-200 text-left text-[10px] uppercase tracking-wide text-zinc-500 dark:border-zinc-700">
            <th className="px-4 py-2 font-medium">URL</th>
            <th className="px-4 py-2 font-medium text-right">Klicks</th>
            <th className="px-4 py-2 font-medium text-right">Unique</th>
          </tr>
        </thead>
        <tbody>
          {links.map((link, i) => (
            <tr key={`${i}-${link.url}`} className="border-b border-zinc-100 last:border-0 dark:border-zinc-800">
              <td className="max-w-[400px] truncate px-4 py-2 text-zinc-700 dark:text-zinc-300" title={link.url}>
                <a href={link.url} target="_blank" rel="noreferrer" className="inline-flex items-center gap-1 hover:underline">
                  <span className="truncate">{link.url}</span>
                  <ExternalLink className="h-3 w-3 flex-shrink-0 text-zinc-400" />
                </a>
              </td>
              <td className="px-4 py-2 text-right font-mono text-zinc-900 dark:text-zinc-100">{link.clicks}</td>
              <td className="px-4 py-2 text-right font-mono text-zinc-500">{link.uniqueRecipients}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}
