"use client";

import { useState, useRef, useEffect, useMemo } from "react";
import { Button } from "@/components/ui/button";
import { StatsOverview } from "@/components/dashboard";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
import {
  BarChart2, GitBranch, Loader2, AlertCircle, TrendingUp, Users,
  PieChart, Calendar, X, FolderOpen, Search, MessageSquare, FileText,
  Settings, Bot, Cpu, Sun, Moon, Sparkles, ChevronRight, RefreshCw
} from "lucide-react";

const COLORS = ["#6366f1", "#f59e0b", "#10b981", "#ef4444", "#8b5cf6", "#06b6d4", "#ec4899", "#f97316", "#84cc16", "#64748b"];

export default function Home() {
  const [stats, setStats] = useState<any>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [dialogOpen, setDialogOpen] = useState(false);
  const [ollamaStatus, setOllamaStatus] = useState<"checking" | "online" | "offline">("checking");
  const [ollamaModel, setOllamaModel] = useState("...");
  const [ollamaModels, setOllamaModels] = useState<string[]>([]);
  const [settingsOpen, setSettingsOpen] = useState(false);
  const [aiSummary, setAiSummary] = useState<string | null>(null);
  const [summaryLoading, setSummaryLoading] = useState(false);
  const [summaryError, setSummaryError] = useState<string | null>(null);
  const [dark, setDark] = useState(false);
  const statsRef = useRef<any>(null);

  useEffect(() => {
    fetch("/api/settings").then(r => r.json()).then(d => {
      setOllamaModel(d.model || "?");
      setOllamaModels(d.models || []);
      if (d.models?.length > 0) setOllamaStatus("online");
    }).catch(() => {});
    if (window.matchMedia("(prefers-color-scheme: dark)").matches) setDark(true);
  }, []);

  useEffect(() => { document.documentElement.classList.toggle("dark", dark); }, [dark]);

  const analyzeRepo = async (repoPath: string) => {
    setLoading(true); setError(null); setAiSummary(null); setSummaryError(null);
    try {
      const res = await fetch(`/api/stats?path=${encodeURIComponent(repoPath)}`);
      const data = await res.json();
      if (data.error) throw new Error(data.error);
      setStats(data.stats);
      statsRef.current = data.stats;
      generateSummary(data.stats, ollamaModel !== "?" ? ollamaModel : undefined);
    } catch (e: any) {
      setError(e?.message || "Erreur");
    } finally {
      setLoading(false);
    }
  };

  const generateSummary = async (s: any, model?: string) => {
    setSummaryLoading(true); setSummaryError(null);
    // Strip heavy fields to avoid huge request body
    const light = { ...s };
    if (light.recentCommits) {
      light.recentCommits = light.recentCommits.map((c: any) => ({ ...c, filesList: undefined }));
    }
    try {
      const res = await fetch("/api/summary", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ stats: light, model }),
      });
      const d = await res.json();
      if (d.error) { setSummaryError(d.error); setAiSummary(null); }
      else setAiSummary(d.summary);
    } catch {
      setSummaryError("Ollama injoignable. Lancez `ollama serve`.");
    } finally {
      setSummaryLoading(false);
    }
  };

  return (
    <div className={`min-h-screen ${dark ? "dark" : ""} bg-background text-foreground transition-colors`}>
      {/* Header */}
      <header className="sticky top-0 z-40 border-b bg-background/80 backdrop-blur-lg">
        <div className="flex h-14 items-center justify-between px-6">
          <div className="flex items-center gap-3">
            <div className="w-8 h-8 rounded-lg bg-primary/10 flex items-center justify-center">
              <GitBranch className="h-4 w-4 text-primary" />
            </div>
            <h1 className="text-lg font-bold tracking-tight">GitPulse</h1>
          </div>
          <div className="flex items-center gap-2">
            <div className="flex items-center gap-1 px-2 py-1 rounded-md bg-muted/50 text-xs">
              <div className={`w-1.5 h-1.5 rounded-full ${ollamaStatus === "online" ? "bg-green-500 shadow-[0_0_6px_#22c55e]" : "bg-gray-400"}`} />
              <button onClick={() => setSettingsOpen(true)} className="hover:text-foreground font-mono">
                {ollamaModel}
              </button>
            </div>
            <Button variant="ghost" size="icon" onClick={() => setDark(!dark)}>
              {dark ? <Sun className="h-4 w-4" /> : <Moon className="h-4 w-4" />}
            </Button>
            <Button size="sm" onClick={() => setDialogOpen(true)}>+ Nouveau projet</Button>
          </div>
        </div>
      </header>

      <AddRepoDialog open={dialogOpen} onClose={() => setDialogOpen(false)} onSubmit={(p) => { setDialogOpen(false); analyzeRepo(p); }} />
      <SettingsDialog open={settingsOpen} onClose={() => setSettingsOpen(false)} model={ollamaModel} models={ollamaModels} ollamaStatus={ollamaStatus} onSelectModel={(m) => { setOllamaModel(m); if (statsRef.current) generateSummary(statsRef.current, m); setSettingsOpen(false); }} />

      <div className="max-w-7xl mx-auto px-4 py-8">
        {loading && (
          <div className="flex flex-col items-center justify-center py-40 gap-4">
            <div className="relative">
              <div className="w-12 h-12 rounded-full border-2 border-primary/30" />
              <Loader2 className="h-12 w-12 animate-spin text-primary absolute inset-0" />
            </div>
            <p className="text-muted-foreground animate-pulse">Analyse en cours...</p>
          </div>
        )}

        {error && (
          <Card className="border-destructive/50 bg-destructive/5">
            <CardContent className="flex items-center gap-3 py-6">
              <AlertCircle className="h-5 w-5 text-destructive flex-shrink-0" /><div><p className="font-semibold text-destructive">Erreur</p><p className="text-sm text-muted-foreground">{error}</p></div>
              <Button variant="outline" size="sm" className="ml-auto" onClick={() => setDialogOpen(true)}>Réessayer</Button>
            </CardContent>
          </Card>
        )}

        {!loading && !stats && !error && (
          <div className="flex flex-col items-center justify-center py-40 gap-8">
            <div className="relative">
              <div className="w-20 h-20 rounded-2xl bg-primary/10 flex items-center justify-center">
                <BarChart2 className="h-10 w-10 text-primary" />
              </div>
              <div className="absolute -top-2 -right-2 w-6 h-6 rounded-full bg-green-500 flex items-center justify-center animate-pulse">
                <Sparkles className="h-3 w-3 text-white" />
              </div>
            </div>
            <div className="text-center max-w-md">
              <h2 className="text-3xl font-bold tracking-tight">GitPulse</h2>
              <p className="mt-3 text-muted-foreground leading-relaxed">
                Visualisation puissante de vos dépôts Git. Statistiques, tendances, contributeurs — le tout enrichi par IA.
              </p>
            </div>
            <Button size="lg" className="rounded-full px-8" onClick={() => setDialogOpen(true)}>
              <GitBranch className="mr-2 h-4 w-4" />Ajouter un dépôt
            </Button>
          </div>
        )}

        {!loading && stats && (
          <div className="space-y-8">
            {/* Project header */}
            <div className="flex items-center justify-between">
              <div>
                <h2 className="text-2xl font-bold">{stats.repoName || "Projet"}</h2>
                <p className="text-sm text-muted-foreground mt-0.5">{stats.repoPath}</p>
              </div>
              <div className="flex gap-2">
                <Button variant="outline" size="sm" onClick={() => exportCSV(stats)}>📥 CSV</Button>
                <Button variant="outline" size="sm" onClick={() => generateSummary(stats, ollamaModel !== "?" ? ollamaModel : undefined)} disabled={summaryLoading}>
                  <RefreshCw className={`h-3 w-3 mr-1 ${summaryLoading ? "animate-spin" : ""}`} />
                  IA
                </Button>
              </div>
            </div>

            {/* Stats cards */}
            <StatsOverview stats={stats} />

            {/* AI Summary banner */}
            {summaryLoading && (
              <Card className="border-primary/30 bg-primary/5">
                <CardContent className="flex items-center gap-3 py-4">
                  <Loader2 className="h-5 w-5 animate-spin text-primary" />
                  <div><p className="font-medium">Résumé IA en cours</p><p className="text-xs text-muted-foreground">Modèle : {ollamaModel}</p></div>
                </CardContent>
              </Card>
            )}
            {!summaryLoading && summaryError && (
              <Card className="border-amber-500/30 bg-amber-500/5">
                <CardContent className="flex items-center gap-3 py-4">
                  <AlertCircle className="h-5 w-5 text-amber-500" />
                  <div><p className="font-medium text-amber-600">{summaryError}</p></div>
                  <Button variant="outline" size="sm" className="ml-auto" onClick={() => generateSummary(stats, ollamaModel !== "?" ? ollamaModel : undefined)}>Réessayer</Button>
                </CardContent>
              </Card>
            )}
            {!summaryLoading && !summaryError && aiSummary && (
              <Card className="border-primary/20 bg-gradient-to-r from-primary/5 to-transparent">
                <CardHeader className="pb-2">
                  <CardTitle className="text-base flex items-center gap-2">
                    <Sparkles className="h-4 w-4 text-primary" />
                    Analyse IA · {ollamaModel}
                  </CardTitle>
                </CardHeader>
                <CardContent><div className="text-sm whitespace-pre-wrap leading-relaxed">{aiSummary}</div></CardContent>
              </Card>
            )}
            {!summaryLoading && !summaryError && !aiSummary && ollamaStatus === "offline" && (
              <Card className="border-dashed">
                <CardContent className="flex items-center gap-4 py-6 justify-center text-sm text-muted-foreground">
                  <Cpu className="h-5 w-5" />
                  <span>Ollama hors ligne — <code className="bg-muted px-1 rounded">ollama serve</code> puis <code className="bg-muted px-1 rounded">ollama pull llama3.2</code> pour activer l'IA</span>
                </CardContent>
              </Card>
            )}

            {/* Tabs */}
            <Tabs defaultValue="commits" className="w-full">
              <TabsList>
                <TabsTrigger value="commits"><MessageSquare className="mr-2 h-4 w-4" />Commits</TabsTrigger>
                <TabsTrigger value="charts"><TrendingUp className="mr-2 h-4 w-4" />Graphiques</TabsTrigger>
                <TabsTrigger value="contributors"><Users className="mr-2 h-4 w-4" />Contributeurs</TabsTrigger>
                <TabsTrigger value="files"><FileText className="mr-2 h-4 w-4" />Fichiers</TabsTrigger>
                <TabsTrigger value="types"><PieChart className="mr-2 h-4 w-4" />Types</TabsTrigger>
              </TabsList>

              <div className="mt-6">
                <TabsContent value="commits">
                  <Card>
                    <CardHeader className="flex flex-row items-center justify-between">
                      <CardTitle className="flex items-center gap-2"><MessageSquare className="h-5 w-5 text-primary" />Historique ({stats.recentCommits?.length || 0} commits)</CardTitle>
                    </CardHeader>
                    <CardContent><CommitSearch commits={stats.recentCommits || []} /></CardContent>
                  </Card>
                </TabsContent>

                <TabsContent value="charts">
                  <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
                    <Card>
                      <CardHeader><CardTitle className="flex items-center gap-2"><Calendar className="h-5 w-5 text-primary" />Activité quotidienne</CardTitle></CardHeader>
                      <CardContent><div className="h-56"><DailyBarChart data={stats.timeSeries?.daily || []} /></div></CardContent>
                    </Card>
                    <Card>
                      <CardHeader><CardTitle className="flex items-center gap-2"><GitBranch className="h-5 w-5 text-primary" />Heatmap</CardTitle></CardHeader>
                      <CardContent><HeatmapGrid data={stats.heatmap || []} /></CardContent>
                    </Card>
                    <Card className="md:col-span-2">
                      <CardHeader><CardTitle>Par mois</CardTitle></CardHeader>
                      <CardContent><div className="h-48"><MonthlyBars data={stats.timeSeries?.monthly || []} /></div></CardContent>
                    </Card>
                  </div>
                </TabsContent>

                <TabsContent value="contributors">
                  <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
                    <Card><CardHeader><CardTitle>Top contributeurs</CardTitle></CardHeader><CardContent><ContributorBars contributors={stats.contributors || []} /></CardContent></Card>
                    <Card>
                      <CardHeader><CardTitle>Détails</CardTitle></CardHeader>
                      <CardContent>
                        <div className="grid grid-cols-2 gap-3">
                          <StatItem label="Contributeurs" value={stats.contributors?.length || 0} />
                          <StatItem label="Jours actifs" value={stats.activeDays || 0} />
                          <StatItem label="Top" value={stats.topContributor?.name || "-"} />
                          <StatItem label="Commits (top)" value={stats.topContributor?.commits || 0} />
                          <StatItem label="+ Lignes (top)" value={stats.topContributor?.linesAdded || 0} />
                          <StatItem label="Fichiers modifiés" value={stats.totalFilesChanged || 0} />
                        </div>
                      </CardContent>
                    </Card>
                  </div>
                </TabsContent>

                <TabsContent value="files">
                  <Card>
                    <CardHeader><CardTitle>Fichiers les plus modifiés</CardTitle></CardHeader>
                    <CardContent className="overflow-x-auto">
                      <table className="w-full text-sm"><thead><tr className="border-b text-left"><th className="pb-2 font-medium text-muted-foreground">Fichier</th><th className="pb-2 font-medium text-muted-foreground text-right">+</th><th className="pb-2 font-medium text-muted-foreground text-right">−</th><th className="pb-2 font-medium text-muted-foreground text-right">Total</th><th className="pb-2 font-medium text-muted-foreground text-right">Commits</th></tr></thead>
                      <tbody>{(stats.fileChanges || []).slice(0, 25).map((f: any) => (<tr key={f.path} className="border-b last:border-0 hover:bg-muted/50"><td className="py-2 truncate max-w-[300px]" title={f.path}><span className="font-mono text-xs">{f.path}</span></td><td className="py-2 text-right text-green-500 font-mono text-xs">{f.additions.toLocaleString("fr-FR")}</td><td className="py-2 text-right text-red-500 font-mono text-xs">{f.deletions.toLocaleString("fr-FR")}</td><td className="py-2 text-right font-mono text-xs">{f.totalChanges.toLocaleString("fr-FR")}</td><td className="py-2 text-right text-muted-foreground">{f.commits}</td></tr>))}{(stats.fileChanges || []).length === 0 && (<tr><td colSpan={5} className="py-8 text-center text-muted-foreground">Aucune donnée</td></tr>)}</tbody></table>
                    </CardContent>
                  </Card>
                </TabsContent>

                <TabsContent value="types">
                  <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
                    <Card><CardHeader><CardTitle>Répartition</CardTitle></CardHeader><CardContent><CommitTypeBars types={stats.commitTypes || []} /></CardContent></Card>
                    <Card><CardHeader><CardTitle>Distribution</CardTitle></CardHeader><CardContent><div className="h-56 flex items-center"><DonutChart types={stats.commitTypes || []} /></div></CardContent></Card>
                  </div>
                </TabsContent>
              </div>
            </Tabs>
          </div>
        )}
      </div>
    </div>
  );
}

// ==================== Settings Dialog ====================
function SettingsDialog({ open, onClose, model, models, ollamaStatus, onSelectModel }: { open: boolean; onClose: () => void; model: string; models: string[]; ollamaStatus: string; onSelectModel: (m: string) => void }) {
  if (!open) return null;
  return (
    <div className="fixed inset-0 z-50 flex items-center justify-center p-4">
      <div className="absolute inset-0 bg-black/50 backdrop-blur-sm" onClick={onClose} />
      <div className="relative w-full max-w-sm bg-card rounded-xl border shadow-2xl overflow-hidden">
        <div className="flex items-center justify-between px-5 py-4 border-b bg-muted/30">
          <h2 className="font-semibold flex items-center gap-2"><Cpu className="h-4 w-4 text-primary" />Modèle IA</h2>
          <Button variant="ghost" size="icon" onClick={onClose}><X className="h-4 w-4" /></Button>
        </div>
        <div className="p-4 space-y-1 max-h-80 overflow-y-auto">
          <div className="flex items-center gap-2 text-xs text-muted-foreground mb-2">
            <div className={`w-1.5 h-1.5 rounded-full ${ollamaStatus === "online" ? "bg-green-500" : "bg-gray-400"}`} />
            {ollamaStatus === "online" ? "Ollama connecté" : "Ollama hors ligne"}
          </div>
          {models.map((m) => (
            <button key={m} onClick={() => onSelectModel(m)} className={`w-full flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm text-left transition-all ${m === model ? "bg-primary/10 border border-primary/30 ring-1 ring-primary/20" : "hover:bg-muted border border-transparent"}`}>
              <Bot className="h-4 w-4 flex-shrink-0" />
              <span className="font-mono flex-1">{m}</span>
              {m === model && <span className="text-[10px] text-primary font-bold">ACTIF</span>}
              <ChevronRight className="h-3 w-3 text-muted-foreground" />
            </button>
          ))}
          {models.length === 0 && <p className="text-sm text-muted-foreground text-center py-4">Aucun modèle. <code className="bg-muted px-1 rounded">ollama pull llama3.2</code></p>}
        </div>
      </div>
    </div>
  );
}

// ==================== AddRepoDialog ====================
function AddRepoDialog({ open, onClose, onSubmit }: { open: boolean; onClose: () => void; onSubmit: (path: string) => void }) {
  const [path, setPath] = useState(""); const [browsePath, setBrowsePath] = useState("");
  const [folders, setFolders] = useState<{ name: string; path: string; isGitRepo: boolean }[]>([]);
  const [browseLoading, setBrowseLoading] = useState(false); const [browseError, setBrowseError] = useState<string | null>(null);
  const [parentPath, setParentPath] = useState<string | null>(null); const inputRef = useRef<HTMLInputElement>(null);
  useEffect(() => { if (open) { setPath(""); setTimeout(() => inputRef.current?.focus(), 100); } }, [open]);
  useEffect(() => { const h = (e: KeyboardEvent) => { if (e.key === "Escape" && open) onClose(); }; document.addEventListener("keydown", h); return () => document.removeEventListener("keydown", h); }, [open, onClose]);
  const fetchBrowse = async (dirPath: string) => { setBrowseLoading(true); setBrowseError(null); try { const res = await fetch(`/api/fs/browse?path=${encodeURIComponent(dirPath)}`); const data = await res.json(); if (data.error) throw new Error(data.error); setBrowsePath(data.path); setParentPath(data.parentPath); setFolders(data.folders.sort((a: any, b: any) => { if (a.isGitRepo && !b.isGitRepo) return -1; if (!a.isGitRepo && b.isGitRepo) return 1; return a.name.localeCompare(b.name); })); } catch (e: any) { setBrowseError(e?.message || "Erreur"); } finally { setBrowseLoading(false); } };
  useEffect(() => { if (open) fetchBrowse(path.trim() || "/"); }, [open]);
  if (!open) return null;
  const gitRepos = folders.filter((f: any) => f.isGitRepo), regularDirs = folders.filter((f: any) => !f.isGitRepo);
  return (
    <div className="fixed inset-0 z-50 flex items-center justify-center p-4">
      <div className="absolute inset-0 bg-black/50 backdrop-blur-sm" onClick={onClose} />
      <div className="relative w-full max-w-4xl h-[85vh] bg-card rounded-xl border shadow-2xl flex flex-col overflow-hidden">
        <div className="flex items-center justify-between p-5 border-b flex-shrink-0">
          <h2 className="text-lg font-semibold flex items-center gap-2"><FolderOpen className="h-5 w-5 text-primary" />Sélectionner un dépôt</h2>
          <Button variant="ghost" size="icon" onClick={onClose}><X className="h-4 w-4" /></Button>
        </div>
        <div className="px-5 py-3 border-b flex-shrink-0 space-y-2">
          <div className="flex items-center gap-2 text-sm">
            <span className="text-muted-foreground flex-shrink-0">Chemin :</span>
            <div className="flex items-center gap-1 flex-1 min-w-0 overflow-x-auto">
              {parentPath !== null && (
                <button type="button" onClick={() => fetchBrowse(parentPath!)} className="flex items-center gap-1 px-2 py-0.5 rounded-md bg-muted hover:bg-muted/80 text-xs font-medium flex-shrink-0 transition-colors">
                  ← Retour
                </button>
              )}
              <button type="button" onClick={() => fetchBrowse("/")} className="text-muted-foreground hover:text-foreground flex-shrink-0 font-mono text-xs">/</button>
              {browsePath !== "/" && (<><span className="text-muted-foreground">›</span><span className="truncate text-muted-foreground text-xs" title={browsePath}>{browsePath.split(/[\/\\]/).slice(1).join(" › ")}</span></>)}
            </div>
          </div>
          <form onSubmit={(e) => { e.preventDefault(); const t = path.trim(); if (t) onSubmit(t); }} className="flex gap-2">
            <input ref={inputRef} type="text" value={path} onChange={(e) => setPath(e.target.value)} placeholder="Collez ou tapez un chemin..." className="flex-1 rounded-md border border-input bg-background px-3 py-1.5 text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring" autoComplete="off" />
            <Button type="submit" size="sm" disabled={!path.trim()}>Analyser</Button>
          </form>
        </div><div className="flex-1 overflow-y-auto p-4">{browseLoading && <div className="flex items-center gap-2 py-8 text-sm text-muted-foreground justify-center"><Loader2 className="h-5 w-5 animate-spin" /> Chargement...</div>}{browseError && <div className="text-center py-8"><AlertCircle className="h-8 w-8 text-destructive mx-auto mb-2" /><p className="text-sm text-destructive">{browseError}</p></div>}{!browseLoading && !browseError && folders.length === 0 && (<div className="text-center py-16"><FolderOpen className="h-10 w-10 text-muted-foreground mx-auto mb-3 opacity-30" /><p className="text-sm text-muted-foreground">Dossier vide</p></div>)}{!browseLoading && !browseError && (<div className="space-y-4">{gitRepos.length > 0 && (<div><h3 className="text-xs font-semibold text-green-600 uppercase tracking-wider mb-2 flex items-center gap-1"><GitBranch className="h-3 w-3" />Dépôts Git ({gitRepos.length})</h3><div className="grid grid-cols-1 sm:grid-cols-2 gap-2">{gitRepos.map((f: any) => (<button key={f.path} type="button" onClick={() => { setPath(f.path); onSubmit(f.path); }} className="flex items-center gap-3 p-3 rounded-lg border border-green-200 bg-green-50 dark:bg-green-950/20 hover:bg-green-100 dark:hover:bg-green-950/40 transition-colors text-left group"><GitBranch className="h-5 w-5 text-green-600 flex-shrink-0" /><div className="min-w-0 flex-1"><p className="text-sm font-medium truncate">{f.name}</p><p className="text-[11px] text-green-700/70 dark:text-green-400/70 truncate">{f.path}</p></div><span className="text-xs text-green-600 font-medium opacity-0 group-hover:opacity-100 transition-opacity flex-shrink-0">Ouvrir →</span></button>))}</div></div>)}{regularDirs.length > 0 && (<div>{gitRepos.length > 0 && <h3 className="text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-2 mt-4">Dossiers ({regularDirs.length})</h3>}<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-2">{regularDirs.map((f: any) => (<button key={f.path} type="button" onClick={() => { setPath(f.path); fetchBrowse(f.path); }} className="flex items-center gap-2 p-2.5 rounded-lg border hover:bg-muted transition-colors text-left"><FolderOpen className="h-4 w-4 text-muted-foreground flex-shrink-0" /><span className="text-sm truncate">{f.name}</span></button>))}</div></div>)}</div>)}</div></div></div>);
}

// ==================== Commits ====================
function CommitFeed({ commits }: { commits: any[] }) {
  if (!commits?.length) return <p className="text-muted-foreground text-sm py-8 text-center">Aucun commit.</p>;
  const getType = (msg: string) => { const m = msg.toLowerCase(); if (m.startsWith("feat")) return { l: "feat", c: "bg-emerald-500/10 text-emerald-600 ring-emerald-500/20" }; if (m.startsWith("fix")) return { l: "fix", c: "bg-orange-500/10 text-orange-600 ring-orange-500/20" }; if (m.startsWith("refactor")) return { l: "refactor", c: "bg-purple-500/10 text-purple-600 ring-purple-500/20" }; if (m.startsWith("docs")) return { l: "docs", c: "bg-blue-500/10 text-blue-600 ring-blue-500/20" }; if (m.startsWith("chore")) return { l: "chore", c: "bg-gray-500/10 text-gray-600 ring-gray-500/20" }; if (m.startsWith("perf")) return { l: "perf", c: "bg-red-500/10 text-red-600 ring-red-500/20" }; if (m.startsWith("test")) return { l: "test", c: "bg-yellow-500/10 text-yellow-600 ring-yellow-500/20" }; return { l: "", c: "" }; };
  return (<div className="space-y-0.5 max-h-[650px] overflow-y-auto">{commits.map((c, i) => { const t = getType(c.message || ""); return (<div key={c.hash || i} className="flex items-start gap-3 px-3 py-2.5 rounded-lg hover:bg-muted/50 transition-colors border-b border-border/20 last:border-0"><div className="w-2 h-2 rounded-full bg-primary mt-2 flex-shrink-0" /><div className="flex-1 min-w-0"><div className="flex items-center gap-2 flex-wrap">{t.l && <span className={`text-[10px] px-1.5 py-0.5 rounded font-bold uppercase ring-1 ring-inset ${t.c}`}>{t.l}</span>}<span className="text-sm truncate flex-1">{c.message || "(vide)"}</span></div><div className="flex items-center gap-2 mt-1 flex-wrap"><span className="text-xs font-medium">{c.author}</span><span className="text-xs text-muted-foreground">{new Date(c.date).toLocaleDateString("fr-FR", { day: "2-digit", month: "short", hour: "2-digit", minute: "2-digit" })}</span>{c.insertions > 0 && <span className="text-xs text-green-500 font-mono">+{c.insertions}</span>}{c.deletions > 0 && <span className="text-xs text-red-500 font-mono">-{c.deletions}</span>}{c.files > 0 && <span className="text-xs text-muted-foreground">{c.files}f</span>}</div></div><span className="text-[10px] text-muted-foreground font-mono flex-shrink-0 mt-1">{c.shortHash}</span></div>); })}</div>);
}

function CommitSearch({ commits }: { commits: any[] }) {
  const [search, setSearch] = useState(""); const [authorFilter, setAuthorFilter] = useState(""); const [typeFilter, setTypeFilter] = useState("");
  const authors = useMemo(() => [...new Set(commits.map((c: any) => c.author))].sort(), [commits]);
  const types = useMemo(() => [...new Set(commits.map((c: any) => (c.message || "").split(":")[0]?.split("(")[0] || "other"))].sort(), [commits]);
  const filtered = useMemo(() => commits.filter((c: any) => { if (search && !(c.message || "").toLowerCase().includes(search.toLowerCase())) return false; if (authorFilter && c.author !== authorFilter) return false; if (typeFilter && !(c.message || "").toLowerCase().startsWith(typeFilter.toLowerCase())) return false; return true; }), [commits, search, authorFilter, typeFilter]);
  return (<div className="space-y-4"><div className="flex flex-wrap gap-2"><div className="relative flex-1 min-w-[200px]"><Search className="h-4 w-4 absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" /><input type="text" value={search} onChange={(e) => setSearch(e.target.value)} placeholder="Rechercher..." className="w-full pl-9 pr-3 py-2 rounded-md border border-input bg-background text-sm focus:outline-none focus:ring-2 focus:ring-ring" /></div><select value={authorFilter} onChange={(e) => setAuthorFilter(e.target.value)} className="rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring">{authors.map((a: string) => <option key={a} value={a === authorFilter ? "" : a}>{a === authorFilter ? "Tous" : a}</option>)}</select><select value={typeFilter} onChange={(e) => setTypeFilter(e.target.value)} className="rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring">{types.map((t: string) => <option key={t} value={t === typeFilter ? "" : t}>{t === typeFilter ? "Tous" : t}</option>)}</select></div><p className="text-xs text-muted-foreground">{filtered.length} commit{filtered.length > 1 ? "s" : ""}</p><CommitFeed commits={filtered.slice(0, 100)} /></div>);
}

// ==================== Charts ====================
function DailyBarChart({ data }: { data: { date: string; count: number }[] }) {
  const s = [...data].sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()).slice(-60); const max = Math.max(...s.map(d => d.count), 1);
  if (!s.length) return <p className="text-muted-foreground text-sm py-8 text-center">Aucune donnée</p>;
  return (<div className="flex items-end gap-[1px] h-full w-full min-w-[240px]">{s.map((d, i) => { const h = Math.max((d.count / max) * 100, 3); return <div key={i} className="flex-1 flex flex-col items-center gap-1 group" title={`${d.date}: ${d.count} commits`}><div className="w-full rounded-sm bg-primary/60 group-hover:bg-primary transition-all" style={{ height: `${h}%`, minHeight: "2px" }} />{i % 15 === 0 && <span className="text-[10px] text-muted-foreground">{new Date(d.date).toLocaleDateString("fr-FR", { month: "short", day: "numeric" })}</span>}</div>; })}</div>);
}
function HeatmapGrid({ data }: { data: { date: string; count: number; level: number }[] }) {
  if (!data?.length) return <p className="text-muted-foreground text-sm py-8 text-center">Aucune donnée</p>;
  const colors = ["#ebedf0", "#9be9a8", "#40c463", "#30a14e", "#216e3a", "#1e6c25"]; const weeks: typeof data[] = []; let cw: typeof data = [];
  for (const d of data) { cw.push(d); if (cw.length === 7) { weeks.unshift(cw); cw = []; } } if (cw.length > 0) weeks.unshift(cw);
  return (<div className="overflow-x-auto pb-2"><div className="flex gap-1">{weeks.map((w, wi) => (<div key={wi} className="flex flex-col gap-1">{w.map((d, di) => (<div key={`${wi}-${di}`} className="w-3 h-3 rounded-sm transition-transform hover:scale-125 cursor-help" style={{ backgroundColor: colors[d.level] || colors[0] }} title={`${d.date}: ${d.count} commits`} />))}</div>))}</div><div className="flex items-center justify-end gap-1 text-[10px] text-muted-foreground mt-2"><span>Moins</span>{colors.map(c => <div key={c} className="w-2.5 h-2.5 rounded-sm" style={{ backgroundColor: c }} />)}<span>Plus</span></div></div>);
}
function MonthlyBars({ data }: { data: { dateLabel: string; count: number }[] }) {
  const s = [...data].slice(-12); const max = Math.max(...s.map(d => d.count), 1);
  if (!s.length) return <p className="text-muted-foreground text-sm py-8 text-center">Aucune donnée</p>;
  return (<div className="flex items-end gap-1 h-full w-full">{s.map((d, i) => { const h = Math.max((d.count / max) * 100, 4); return <div key={i} className="flex-1 flex flex-col items-center gap-1" title={`${d.dateLabel}: ${d.count} commits`}><span className="text-[10px] text-muted-foreground">{d.count || ""}</span><div className="w-full rounded-t-sm bg-primary/60 hover:bg-primary transition-all" style={{ height: `${h}%`, minHeight: "4px" }} /><span className="text-[10px] text-muted-foreground">{d.dateLabel}</span></div>; })}</div>);
}
function ContributorBars({ contributors }: { contributors: { name: string; commits: number }[] }) {
  const s = [...contributors].sort((a, b) => b.commits - a.commits).slice(0, 10); const max = Math.max(...s.map(c => c.commits), 1);
  if (!s.length) return <p className="text-muted-foreground text-sm py-8 text-center">Aucune donnée</p>;
  return (<div className="space-y-3">{s.map((c, i) => (<div key={c.name} className="space-y-1"><div className="flex justify-between text-sm"><span className="font-medium">{c.name}</span><span className="text-muted-foreground">{c.commits}</span></div><div className="w-full bg-muted rounded-full h-3 overflow-hidden"><div className="h-full rounded-full transition-all duration-500" style={{ width: `${(c.commits / max) * 100}%`, backgroundColor: COLORS[i % COLORS.length] }} /></div></div>))}</div>);
}
function CommitTypeBars({ types }: { types: { type: string; percentage: number }[] }) {
  const tl: Record<string, string> = { feat: "Nouveau", fix: "Correction", refactor: "Refactorisation", docs: "Docs", style: "Style", chore: "Tâche", perf: "Perf", test: "Test", ci: "CI/CD", build: "Build", revert: "Revert" };
  const tc: Record<string, string> = { feat: COLORS[0], fix: COLORS[3], refactor: COLORS[2], docs: COLORS[5], style: COLORS[6], chore: COLORS[9], perf: COLORS[1], test: COLORS[7], ci: COLORS[4], build: COLORS[8], revert: COLORS[9] };
  if (!types.length) return <p className="text-muted-foreground text-sm py-8 text-center">Aucune donnée</p>;
  return (<div className="space-y-3">{types.filter(t => t.percentage > 0).map(t => (<div key={t.type} className="space-y-1"><div className="flex justify-between text-sm"><span className="font-medium">{tl[t.type] || t.type}</span><span className="text-muted-foreground">{t.percentage}%</span></div><div className="w-full bg-muted rounded-full h-3 overflow-hidden"><div className="h-full rounded-full transition-all" style={{ width: `${t.percentage}%`, backgroundColor: tc[t.type] || COLORS[0] }} /></div></div>))}</div>);
}
function DonutChart({ types }: { types: { type: string; count: number; percentage: number }[] }) {
  const f = types.filter(t => t.count > 0); if (!f.length) return <p className="text-muted-foreground text-sm py-8 text-center">Aucune donnée</p>;
  const total = f.reduce((s, t) => s + t.count, 0); let cp = 0;
  const tl: Record<string, string> = { feat: "New", fix: "Fix", refactor: "Ref", docs: "Doc", style: "Sty", chore: "Chr", perf: "Prf", test: "Tst", ci: "CI", build: "Bld", revert: "Rvt" };
  return (<div className="flex items-center gap-4 h-full w-full"><svg viewBox="0 0 100 100" className="w-36 h-36 flex-shrink-0 -rotate-90 drop-shadow-sm">{f.map((t, i) => { const p = (t.count / total) * 100; const s = cp; cp += p; const e = cp; const a1 = (s / 100) * 2 * Math.PI, a2 = (e / 100) * 2 * Math.PI; const x1 = 50 + 40 * Math.cos(a1), y1 = 50 + 40 * Math.sin(a1), x2 = 50 + 40 * Math.cos(a2), y2 = 50 + 40 * Math.sin(a2); return <path key={t.type} d={`M 50 50 L ${x1} ${y1} A 40 40 0 ${p > 50 ? 1 : 0} 1 ${x2} ${y2} Z`} fill={COLORS[i % COLORS.length]} stroke="var(--background)" strokeWidth="1.5" />; })}<circle cx="50" cy="50" r="24" fill="var(--card)" /><text x="50" y="50" textAnchor="middle" dy="0.3em" fontSize="9" fill="currentColor" fontWeight="bold" transform="rotate(90 50 50)">{total}</text></svg><div className="space-y-1.5 text-xs flex-1">{f.slice(0, 7).map((t, i) => (<div key={t.type} className="flex items-center gap-2"><div className="w-3 h-3 rounded-sm flex-shrink-0" style={{ backgroundColor: COLORS[i % COLORS.length] }} /><span className="flex-1">{tl[t.type] || t.type}</span><span className="text-muted-foreground font-mono">{t.percentage}%</span></div>))}</div></div>);
}
function StatItem({ label, value }: { label: string; value: string | number }) {
  return (<div className="p-3 rounded-lg bg-muted/50"><p className="text-[11px] text-muted-foreground">{label}</p><p className="text-base font-bold mt-0.5">{typeof value === "number" ? value.toLocaleString("fr-FR") : value}</p></div>);
}
function exportCSV(stats: any) {
  const lines: string[] = []; const sep = ";";
  lines.push(`GitPulse${sep}${stats.repoName}`, new Date().toLocaleDateString("fr-FR"), "", `Commits${sep}${stats.totalCommits}`, `+Lignes${sep}${stats.totalLinesAdded}`, `-Lignes${sep}${stats.totalLinesDeleted}`, `Jours${sep}${stats.activeDays}`);
  for (const c of stats.contributors || []) lines.push(`${c.name}${sep}${c.commits}${sep}${c.linesAdded}${sep}${c.linesDeleted}`);
  const blob = new Blob(["\uFEFF" + lines.join("\n")], { type: "text/csv" }); const u = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = u; a.download = `gitpulse-${stats.repoName || "export"}.csv`; a.click(); URL.revokeObjectURL(u);
}
