"use client" import React, { useState, useEffect, useCallback, useRef } from "react" import { motion, AnimatePresence } from "framer-motion" import type { AnalysisResult } from "@/app/page" import { HealthTrendChart } from "./health-trend-chart" import { apiGet, apiPost } from "@/lib/api" interface SavedAnalysis { id: string summary: string findings: AnalysisResult["findings"] report: AnalysisResult["report"] created_at: string } interface AnalysisHistoryProps { sessionId?: string refreshTrigger?: number onSelect: (a: SavedAnalysis) => void onCompare: (a: SavedAnalysis, b: SavedAnalysis) => void mockData?: SavedAnalysis[] inline?: boolean } function formatDate(s: string) { if (!s) return "—" return new Date(s).toLocaleDateString("ar-SA", { day: "numeric", month: "long", year: "numeric" }) } function formatDateShort(s: string) { if (!s) return "—" return new Date(s).toLocaleDateString("ar-SA", { day: "numeric", month: "short" }) } function calcPct(value: string, range: string) { const nums = range?.match(/[\d.]+/g) if (!nums || nums.length < 2) return null const lo = parseFloat(nums[0]), hi = parseFloat(nums[1]), val = parseFloat(value) if (isNaN(val) || isNaN(lo) || isNaN(hi) || hi === lo) return null return Math.max(4, Math.min(96, ((val - lo) / (hi - lo)) * 100)) } const STATUS = { high: { label: "مرتفع", textCls: "text-destructive", bgCls: "bg-destructive/10", barCls: "bg-destructive" }, low: { label: "منخفض", textCls: "text-warning", bgCls: "bg-warning/10", barCls: "bg-warning" }, normal: { label: "طبيعي", textCls: "text-success", bgCls: "bg-success/10", barCls: "bg-success" }, } as const const getS = (s: string) => STATUS[s as keyof typeof STATUS] ?? STATUS.normal function counts(a: SavedAnalysis) { const f = a.findings ?? [] return { total: f.length, high: f.filter(x => x.status === "high").length, low: f.filter(x => x.status === "low").length, normal: f.filter(x => x.status === "normal").length, } } /* ─── Icons ─── */ function ScalesIcon({ className = "w-3.5 h-3.5" }: { className?: string }) { return ( ) } function ChevronIcon({ className = "w-3.5 h-3.5" }: { className?: string }) { return ( ) } /* ───────────────────────────────────────────── Compare Hint ───────────────────────────────────────────── */ function CompareHint() { return (

اضغط «قارن» على أي تحليلين لمتابعة تغيّراتك الصحية

) } /* ───────────────────────────────────────────── Floating Compare Bar ───────────────────────────────────────────── */ function FloatingCompareBar({ selected, onCompare, onCancel, fixed = false, }: { selected: SavedAnalysis[] onCompare: () => void onCancel: () => void fixed?: boolean }) { return (
{selected.length === 1 ? (

تم تحديد تحليل واحد

حدد تحليلاً ثانياً لبدء المقارنة

) : (
{selected.map((s, i) => (
{i + 1}

{s.summary}

{formatDateShort(s.created_at)}

))}
قارن الآن
)}
) } /* ───────────────────────────────────────────── Inline Detail Panel ───────────────────────────────────────────── */ function InlineDetail({ item, onUse, }: { item: SavedAnalysis onUse: (a: SavedAnalysis) => void }) { const c = counts(item) return (
{/* Header mini */}
{c.high > 0 && ( {c.high} مرتفع )} {c.low > 0 && ( {c.low} منخفض )} {c.high === 0 && c.low === 0 && ( كل القيم طبيعية ✓ )}

{formatDate(item.created_at)}

{/* Stats row */}
{[ { num: c.normal, label: "طبيعي", bg: "bg-success/8 border-success/20", text: "text-success" }, { num: c.high, label: "مرتفع", bg: "bg-destructive/8 border-destructive/20", text: "text-destructive" }, { num: c.low, label: "منخفض", bg: "bg-warning/8 border-warning/20", text: "text-warning" }, ].map((s, i) => (

{s.num}

{s.label}

))}
{/* Summary */} {item.summary && (

الملخص

{item.summary}

)} {/* Mini-cards grid */} {item.findings && item.findings.length > 0 && (
{item.findings.map((f, i) => { const s = getS(f.status) const pct = calcPct(f.value, f.range ?? "") return (
{s.label}

{f.name}

{f.value} {f.unit && {f.unit}}
{pct !== null && (
)} {f.range && (

طبيعي: {f.range}

)}
) })}
)} {(!item.findings || item.findings.length === 0) && (

لا توجد بيانات فحوصات

)} {/* Use button */} onUse(item)} className="w-full py-4 rounded-2xl text-sm font-bold gradient-primary text-primary-foreground shadow-glow-primary hover:opacity-90 transition-opacity" > استخدم هذا التحليل
) } /* ───────────────────────────────────────────── Shared Card List (used in both inline & drawer) ───────────────────────────────────────────── */ function AnalysisCardList({ analyses, loading, selected, expandedId, cardRefs, listRef, toggleExpand, toggleSelect, handleUse, containerClassName = "space-y-2", }: { analyses: SavedAnalysis[] loading: boolean selected: SavedAnalysis[] expandedId: string | null cardRefs: React.RefObject> listRef: React.RefObject toggleExpand: (id: string) => void toggleSelect: (a: SavedAnalysis) => void handleUse: (a: SavedAnalysis) => void containerClassName?: string }) { return (
0 ? "148px" : "0px", transition: "padding-bottom 0.3s cubic-bezier(0.22, 1, 0.36, 1)", }} > {loading && (
)} {!loading && analyses.length === 0 && (

لا توجد تحاليل محفوظة

ارفع تحليلاً وسيُحفظ تلقائياً

)} {!loading && analyses.map((a, i) => { const c = counts(a) const isExpanded = expandedId === a.id const selIdx = selected.findIndex(s => s.id === a.id) const isChosen = selIdx !== -1 const selNum = selIdx + 1 const maxed = selected.length >= 2 && !isChosen return ( { if (el) cardRefs.current.set(a.id, el) else cardRefs.current.delete(a.id) }} initial={{ opacity: 0, y: 6 }} animate={{ opacity: maxed ? 0.35 : 1, y: 0 }} transition={{ delay: i * 0.04 }} className="rounded-3xl border overflow-hidden" style={{ borderColor: isChosen ? "var(--primary)" : isExpanded ? "color-mix(in oklch, var(--primary) 45%, var(--border))" : "var(--border)", background: isChosen ? "color-mix(in oklch, var(--primary) 5%, var(--card))" : "var(--card)", boxShadow: isChosen ? "0 0 0 1px color-mix(in oklch, var(--primary) 20%, transparent), 0 8px 32px -8px oklch(0 0 0 / 0.18)" : isExpanded ? "0 8px 32px -8px oklch(0 0 0 / 0.12)" : "0 1px 3px oklch(0 0 0 / 0.05), 0 4px 12px -4px oklch(0 0 0 / 0.07)", transition: "border-color 0.22s, background 0.22s, box-shadow 0.28s", }} > {/* Card body */}
{/* Date + selection badge row */}
{isChosen && ( {selNum} )}
{formatDate(a.created_at)}
{/* Summary title */}

{a.summary}

{/* Stats pills */}
{c.total} فحص {c.high > 0 && ( {c.high} مرتفع )} {c.low > 0 && ( {c.low} منخفض )} {c.high === 0 && c.low === 0 && ( ✓ كل القيم طبيعية )}
{/* Divider */}
{/* Action Row */}
toggleExpand(a.id)} className={`flex-1 flex items-center justify-center gap-2 py-3 rounded-2xl text-sm font-semibold transition-all duration-200 ${ isExpanded ? "bg-primary/10 text-primary border border-primary/25" : "bg-muted/60 text-foreground hover:bg-muted border border-transparent" }`} > {isExpanded ? "إخفاء التفاصيل" : "عرض التفاصيل"} !maxed && toggleSelect(a)} disabled={maxed} className={`flex items-center gap-2 px-5 py-3 rounded-2xl text-sm font-semibold transition-all duration-200 ${ isChosen ? "bg-primary/12 text-primary border border-primary/30" : maxed ? "text-muted-foreground/30 cursor-not-allowed border border-border/30" : "glass text-muted-foreground hover:text-foreground border border-border/50 hover:border-border" }`} > {isChosen ? ( <> محدد ) : ( <> {maxed ? "اكتمل" : "قارن"} )}
{/* Expandable Detail */} {isExpanded && ( )} ) })}
) } /* ───────────────────────────────────────────── Main Component ───────────────────────────────────────────── */ export function AnalysisHistory({ sessionId = "anonymous", refreshTrigger = 0, onSelect, onCompare, mockData, inline = false, }: AnalysisHistoryProps) { const [analyses, setAnalyses] = useState(mockData ?? []) const [loading, setLoading] = useState(false) const [open, setOpen] = useState(false) const [selected, setSelected] = useState([]) const [expandedId, setExpandedId] = useState(null) const [search, setSearch] = useState("") const [searchScores, setSearchScores] = useState | null>(null) const cardRefs = useRef>(new Map()) const listRef = useRef(null) const searchTimer = useRef | null>(null) useEffect(() => { if (mockData) setAnalyses(mockData) }, [mockData]) const fetchAnalyses = useCallback(async () => { if (mockData) return if (!sessionId || sessionId === "anonymous") return setLoading(true) try { const r = await apiGet("/api/analyses/list", { session_id: sessionId }) if (r.ok) setAnalyses((await r.json()).analyses ?? []) } catch {} finally { setLoading(false) } }, [sessionId, mockData]) useEffect(() => { if (inline && !mockData) fetchAnalyses() }, [inline, fetchAnalyses, mockData]) useEffect(() => { if (open && !mockData) fetchAnalyses() }, [open, fetchAnalyses, mockData]) useEffect(() => { if (refreshTrigger > 0 && !mockData) fetchAnalyses() }, [refreshTrigger, fetchAnalyses, mockData]) useEffect(() => { if (searchTimer.current) clearTimeout(searchTimer.current) if (!search.trim() || analyses.length === 0) { setSearchScores(null); return } searchTimer.current = setTimeout(async () => { try { const body = { query: search, analyses: analyses.map(a => ({ id: a.id, summary: a.summary, findings_text: (a.findings ?? []).map(f => f.name).join(" "), })), } const r = await apiPost("/api/search", body) if (r.ok) { const data = await r.json() const scores: Record = {} for (const item of data.results) scores[item.id] = item.score setSearchScores(scores) } } catch { setSearchScores(null) } }, 300) }, [search, analyses]) const toggleSelect = (a: SavedAnalysis) => { setSelected(prev => { const exists = prev.find(s => s.id === a.id) if (exists) return prev.filter(s => s.id !== a.id) if (prev.length >= 2) return prev return [...prev, a] }) } const toggleExpand = (id: string) => { setExpandedId(prev => { const next = prev === id ? null : id if (next) { setTimeout(() => { cardRefs.current.get(id)?.scrollIntoView({ behavior: "smooth", block: "nearest" }) }, 120) } return next }) } const handleCompare = () => { if (selected.length !== 2) return onCompare(selected[0], selected[1]) setSelected([]) setOpen(false) setExpandedId(null) } const handleUse = (a: SavedAnalysis) => { onSelect({ ...a, report: a.report ?? {} as AnalysisResult["report"] }) setOpen(false) setSelected([]) setExpandedId(null) } const closeAll = () => { setOpen(false); setSelected([]); setExpandedId(null) } if (!mockData && !inline && (!sessionId || sessionId === "anonymous")) return null /* ── INLINE MODE (mockData provided, or inline=true with real session) ── */ if (mockData || inline) { const filtered = search.trim() ? searchScores ? analyses .filter(a => (searchScores[a.id] ?? 0) > 0) .sort((a, b) => (searchScores[b.id] ?? 0) - (searchScores[a.id] ?? 0)) : analyses.filter(a => a.summary.includes(search) || formatDate(a.created_at).includes(search) ) : analyses return (
{/* Search */}
setSearch(e.target.value)} placeholder="ابحث في سجل التحاليل..." className="w-full pr-11 pl-11 py-3.5 rounded-2xl glass text-sm text-foreground placeholder:text-muted-foreground focus:outline-none border border-border/60 focus:border-primary/50 transition-colors" /> {search ? ( ) : ( ⌘K )}
{/* Compare Hint */} {selected.length === 0 && analyses.length > 1 && ( )} {analyses.length >= 2 && (
)} {/* Floating Compare Bar — fixed to bottom of viewport in inline mode */} {selected.length > 0 && ( setSelected([])} fixed={true} /> )}
) } /* ── DRAWER MODE (normal usage with sessionId) ── */ return ( <> {/* Trigger button */} {open && ( <>

سجل التحاليل

{analyses.length > 0 &&

{analyses.length} تحليل محفوظ

}
{selected.length === 0 && !loading && analyses.length > 1 && ( )}
{analyses.length >= 2 && (
)}
{selected.length > 0 && ( setSelected([])} /> )}
)}
) }