"use client";

import { useState, useEffect, useRef, useCallback } from "react";
import {
  BookOpen,
  Search,
  Calendar,
  MessageCircle,
  Bot,
  Music,
  RefreshCw,
  ChevronDown,
  Sparkles,
  X,
  Download,
} from "lucide-react";
import type { ChronicleEvent, ChronicleDay } from "@/lib/chronicle-db";

// ─── Types (client-side mirrors) ─────────────────────────────────────────────

interface TimelineResponse {
  days: ChronicleDay[];
  earliest: string | null;
}

interface SearchResponse {
  results: { date: string; events: ChronicleEvent[] }[];
  total: number;
}

interface NarrativeResponse {
  date: string;
  narrative: string | null;
  generatedAt?: number;
}

// ─── Source config ────────────────────────────────────────────────────────────

const SOURCE_CONFIG = {
  calendar: {
    icon: Calendar,
    label: "Calendar",
    color: "text-blue-500",
    bg: "bg-blue-50 dark:bg-blue-950/30",
    border: "border-blue-200 dark:border-blue-800/50",
  },
  imessage: {
    icon: MessageCircle,
    label: "iMessage",
    color: "text-green-500",
    bg: "bg-green-50 dark:bg-green-950/30",
    border: "border-green-200 dark:border-green-800/50",
  },
  agent: {
    icon: Bot,
    label: "Agent",
    color: "text-violet-500",
    bg: "bg-violet-50 dark:bg-violet-950/30",
    border: "border-violet-200 dark:border-violet-800/50",
  },
  music: {
    icon: Music,
    label: "Music",
    color: "text-orange-500",
    bg: "bg-orange-50 dark:bg-orange-950/30",
    border: "border-orange-200 dark:border-orange-800/50",
  },
} as const;

// ─── Helpers ──────────────────────────────────────────────────────────────────

function formatDate(dateStr: string): string {
  const d = new Date(dateStr + "T12:00:00");
  const today = new Date().toISOString().slice(0, 10);
  const yesterday = new Date(Date.now() - 86400000).toISOString().slice(0, 10);

  if (dateStr === today) return "Today";
  if (dateStr === yesterday) return "Yesterday";

  return d.toLocaleDateString("en-US", {
    weekday: "long",
    month: "long",
    day: "numeric",
    year: d.getFullYear() !== new Date().getFullYear() ? "numeric" : undefined,
  });
}

function formatTime(ts: number): string {
  return new Date(ts).toLocaleTimeString("en-US", {
    hour: "numeric",
    minute: "2-digit",
    hour12: true,
  });
}

// ─── Event item ───────────────────────────────────────────────────────────────

function EventItem({ event }: { event: ChronicleEvent }) {
  const cfg = SOURCE_CONFIG[event.source] ?? SOURCE_CONFIG.agent;
  const Icon = cfg.icon;

  return (
    <div className={`flex items-start gap-3 rounded-lg border px-3 py-2 ${cfg.bg} ${cfg.border}`}>
      <div className={`mt-0.5 shrink-0 ${cfg.color}`}>
        <Icon size={14} />
      </div>
      <div className="min-w-0 flex-1">
        <p className="truncate text-sm font-medium text-zinc-800 dark:text-zinc-200">
          {event.title}
        </p>
        {event.body && (
          <p className="mt-0.5 line-clamp-2 text-xs text-zinc-500 dark:text-zinc-400">
            {event.body}
          </p>
        )}
      </div>
      <span className="shrink-0 text-xs text-zinc-400 dark:text-zinc-500">
        {formatTime(event.timestamp)}
      </span>
    </div>
  );
}

// ─── Narrative section ────────────────────────────────────────────────────────

function NarrativeSection({ date, initialNarrative }: { date: string; initialNarrative?: string }) {
  const [narrative, setNarrative] = useState<string | null>(initialNarrative ?? null);
  const [generating, setGenerating] = useState(false);
  const [error, setError] = useState<string | null>(null);

  async function generate() {
    setGenerating(true);
    setError(null);
    try {
      const res = await fetch(`/api/chronicle/narrative/${date}`, { method: "POST" });
      const data = await res.json() as NarrativeResponse;
      if (!res.ok) throw new Error((data as { error?: string }).error ?? "Failed");
      setNarrative(data.narrative);
    } catch (err) {
      setError(err instanceof Error ? err.message : "Generation failed");
    } finally {
      setGenerating(false);
    }
  }

  if (narrative) {
    return (
      <div className="flex items-start gap-2 rounded-lg bg-zinc-50 px-3 py-2 dark:bg-zinc-900/50">
        <Sparkles size={14} className="mt-0.5 shrink-0 text-amber-500" />
        <p className="text-sm italic text-zinc-600 dark:text-zinc-400">{narrative}</p>
        <button
          onClick={generate}
          disabled={generating}
          title="Regenerate"
          className="ml-auto shrink-0 text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300 disabled:opacity-50"
        >
          <RefreshCw size={12} className={generating ? "animate-spin" : ""} />
        </button>
      </div>
    );
  }

  return (
    <button
      onClick={generate}
      disabled={generating}
      className="flex items-center gap-2 rounded-lg border border-dashed border-zinc-300 px-3 py-2 text-xs text-zinc-400 transition-colors hover:border-amber-400 hover:text-amber-500 dark:border-zinc-700 dark:hover:border-amber-500 disabled:opacity-50"
    >
      <Sparkles size={12} className={generating ? "animate-spin" : ""} />
      {generating ? "Generating narrative…" : error ? `Error: ${error} — retry?` : "Generate narrative"}
    </button>
  );
}

// ─── Day card ─────────────────────────────────────────────────────────────────

function DayCard({ day }: { day: ChronicleDay }) {
  const [collapsed, setCollapsed] = useState(false);

  const sourceCounts = day.events.reduce(
    (acc, e) => ({ ...acc, [e.source]: (acc[e.source] ?? 0) + 1 }),
    {} as Record<string, number>
  );

  return (
    <div className="rounded-xl border border-zinc-200 bg-white dark:border-zinc-800 dark:bg-zinc-950">
      {/* Day header */}
      <button
        onClick={() => setCollapsed((c) => !c)}
        className="flex w-full items-center gap-3 px-4 py-3 text-left"
      >
        <div className="min-w-0 flex-1">
          <h3 className="text-base font-semibold text-zinc-900 dark:text-zinc-100">
            {formatDate(day.date)}
          </h3>
          <div className="mt-1 flex flex-wrap gap-2">
            {Object.entries(sourceCounts).map(([source, count]) => {
              const cfg = SOURCE_CONFIG[source as keyof typeof SOURCE_CONFIG];
              if (!cfg) return null;
              const Icon = cfg.icon;
              return (
                <span
                  key={source}
                  className={`flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium ${cfg.bg} ${cfg.color}`}
                >
                  <Icon size={10} />
                  {count}
                </span>
              );
            })}
          </div>
        </div>
        <ChevronDown
          size={16}
          className={`shrink-0 text-zinc-400 transition-transform ${collapsed ? "-rotate-90" : ""}`}
        />
      </button>

      {/* Day body */}
      {!collapsed && (
        <div className="border-t border-zinc-100 px-4 pb-4 pt-3 dark:border-zinc-800">
          {/* Narrative */}
          <div className="mb-3">
            <NarrativeSection date={day.date} initialNarrative={day.narrative} />
          </div>

          {/* Events */}
          <div className="flex flex-col gap-1.5">
            {day.events.map((event) => (
              <EventItem key={event.id} event={event} />
            ))}
          </div>
        </div>
      )}
    </div>
  );
}

// ─── Search results ───────────────────────────────────────────────────────────

function SearchResults({ query }: { query: string }) {
  const [results, setResults] = useState<SearchResponse["results"]>([]);
  const [total, setTotal] = useState(0);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    if (!query.trim()) return;
    setLoading(true);
    fetch(`/api/chronicle/search?q=${encodeURIComponent(query)}`)
      .then((r) => r.json())
      .then((data: SearchResponse) => {
        setResults(data.results ?? []);
        setTotal(data.total ?? 0);
      })
      .catch(() => {})
      .finally(() => setLoading(false));
  }, [query]);

  if (loading) {
    return <div className="p-8 text-center text-sm text-zinc-400">Searching…</div>;
  }

  if (results.length === 0) {
    return (
      <div className="p-8 text-center text-sm text-zinc-400">
        No results for <strong>&ldquo;{query}&rdquo;</strong>
      </div>
    );
  }

  return (
    <div className="flex flex-col gap-4 p-4">
      <p className="text-xs text-zinc-400">
        {total} event{total !== 1 ? "s" : ""} matching <strong>&ldquo;{query}&rdquo;</strong>
      </p>
      {results.map(({ date, events }) => (
        <div key={date} className="rounded-xl border border-zinc-200 bg-white dark:border-zinc-800 dark:bg-zinc-950">
          <div className="px-4 py-2 border-b border-zinc-100 dark:border-zinc-800">
            <h4 className="text-sm font-semibold text-zinc-900 dark:text-zinc-100">{formatDate(date)}</h4>
          </div>
          <div className="flex flex-col gap-1.5 p-4">
            {events.map((event) => (
              <EventItem key={event.id} event={event} />
            ))}
          </div>
        </div>
      ))}
    </div>
  );
}

// ─── Main view ────────────────────────────────────────────────────────────────

export default function ChronicleView() {
  const [days, setDays] = useState<ChronicleDay[]>([]);
  const [earliest, setEarliest] = useState<string | null>(null);
  const [loading, setLoading] = useState(true);
  const [loadingMore, setLoadingMore] = useState(false);
  const [ingesting, setIngesting] = useState(false);
  const [searchQuery, setSearchQuery] = useState("");
  const [hasMore, setHasMore] = useState(true);
  const bottomRef = useRef<HTMLDivElement>(null);

  const loadTimeline = useCallback(async (before: string | null = null, append = false) => {
    if (append) setLoadingMore(true);
    else setLoading(true);

    try {
      const qs = before ? `?before=${before}` : "";
      const res = await fetch(`/api/chronicle/timeline${qs}`);
      const data = await res.json() as TimelineResponse;

      if (append) {
        setDays((prev) => [...prev, ...data.days]);
      } else {
        setDays(data.days);
      }

      setEarliest(data.earliest);
      setHasMore(data.days.length === 7);
    } catch {
      // Silently fail — empty state handles the case
    } finally {
      setLoading(false);
      setLoadingMore(false);
    }
  }, []);

  // Initial load
  useEffect(() => {
    loadTimeline();
  }, [loadTimeline]);

  // Infinite scroll via IntersectionObserver
  useEffect(() => {
    if (!bottomRef.current || !hasMore || loadingMore || loading) return;

    const observer = new IntersectionObserver(
      (entries) => {
        if (entries[0]?.isIntersecting) {
          const lastDay = days[days.length - 1];
          if (lastDay) {
            // Load days strictly before this date
            const d = new Date(lastDay.date + "T12:00:00");
            d.setDate(d.getDate() - 1);
            const before = d.toISOString().slice(0, 10);
            loadTimeline(before, true);
          }
        }
      },
      { threshold: 0.5 }
    );
    observer.observe(bottomRef.current);
    return () => observer.disconnect();
  }, [days, hasMore, loadingMore, loading, loadTimeline]);

  async function handleIngest() {
    setIngesting(true);
    try {
      await fetch("/api/chronicle/ingest", { method: "POST" });
      await loadTimeline();
    } catch {
      // Ignore
    } finally {
      setIngesting(false);
    }
  }

  const isSearching = searchQuery.trim().length > 0;

  if (loading) {
    return (
      <div className="flex h-full items-center justify-center">
        <div className="flex flex-col items-center gap-3 text-zinc-400">
          <BookOpen size={32} className="animate-pulse" />
          <p className="text-sm">Loading Chronicle…</p>
        </div>
      </div>
    );
  }

  return (
    <div className="flex h-full flex-col overflow-hidden">
      {/* Header */}
      <div className="shrink-0 border-b border-zinc-200 bg-white px-4 py-3 dark:border-zinc-800 dark:bg-zinc-950">
        <div className="flex items-center gap-3">
          <div className="flex items-center gap-2">
            <BookOpen size={18} className="text-zinc-400" />
            <h1 className="text-base font-semibold text-zinc-900 dark:text-zinc-100">Chronicle</h1>
          </div>

          {/* Search */}
          <div className="relative min-w-0 flex-1">
            <Search size={14} className="absolute left-3 top-1/2 -translate-y-1/2 text-zinc-400" />
            <input
              type="text"
              value={searchQuery}
              onChange={(e) => setSearchQuery(e.target.value)}
              placeholder="Search your timeline…"
              className="w-full rounded-lg border border-zinc-200 bg-zinc-50 py-1.5 pl-8 pr-8 text-sm text-zinc-900 placeholder-zinc-400 focus:border-zinc-400 focus:outline-none dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-100 dark:placeholder-zinc-500"
            />
            {searchQuery && (
              <button
                onClick={() => setSearchQuery("")}
                className="absolute right-2 top-1/2 -translate-y-1/2 text-zinc-400 hover:text-zinc-600"
              >
                <X size={14} />
              </button>
            )}
          </div>

          {/* Ingest button */}
          <button
            onClick={handleIngest}
            disabled={ingesting}
            title="Sync all sources"
            className="flex shrink-0 items-center gap-1.5 rounded-lg border border-zinc-200 px-3 py-1.5 text-xs font-medium text-zinc-600 transition-colors hover:bg-zinc-50 dark:border-zinc-700 dark:text-zinc-400 dark:hover:bg-zinc-900 disabled:opacity-50"
          >
            <Download size={13} className={ingesting ? "animate-bounce" : ""} />
            {ingesting ? "Syncing…" : "Sync"}
          </button>
        </div>
      </div>

      {/* Body */}
      <div className="flex-1 overflow-y-auto">
        {isSearching ? (
          <SearchResults query={searchQuery.trim()} />
        ) : days.length === 0 ? (
          <EmptyState onIngest={handleIngest} ingesting={ingesting} />
        ) : (
          <div className="mx-auto max-w-2xl px-4 py-4">
            <div className="flex flex-col gap-4">
              {days.map((day) => (
                <DayCard key={day.date} day={day} />
              ))}
            </div>

            {/* Infinite scroll sentinel */}
            <div ref={bottomRef} className="mt-4 flex items-center justify-center py-4">
              {loadingMore ? (
                <RefreshCw size={16} className="animate-spin text-zinc-400" />
              ) : hasMore ? (
                <span className="text-xs text-zinc-300 dark:text-zinc-700">scroll for more</span>
              ) : earliest ? (
                <span className="text-xs text-zinc-400">
                  Chronicle begins {formatDate(earliest)}
                </span>
              ) : null}
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

// ─── Empty state ──────────────────────────────────────────────────────────────

function EmptyState({ onIngest, ingesting }: { onIngest: () => void; ingesting: boolean }) {
  return (
    <div className="flex h-full flex-col items-center justify-center gap-6 p-8 text-center">
      <div className="flex h-16 w-16 items-center justify-center rounded-2xl bg-zinc-100 dark:bg-zinc-800">
        <BookOpen size={28} className="text-zinc-400 dark:text-zinc-500" />
      </div>
      <div>
        <h2 className="text-lg font-semibold text-zinc-900 dark:text-zinc-100">
          Your Chronicle is empty
        </h2>
        <p className="mt-1 max-w-sm text-sm text-zinc-500 dark:text-zinc-400">
          Sync your calendar, iMessages, agent activity, and music to start building a personal timeline of your days.
        </p>
      </div>
      <div className="flex flex-col items-center gap-3">
        <button
          onClick={onIngest}
          disabled={ingesting}
          className="flex items-center gap-2 rounded-lg bg-zinc-900 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-zinc-700 dark:bg-zinc-100 dark:text-zinc-900 dark:hover:bg-zinc-300 disabled:opacity-50"
        >
          <Download size={15} className={ingesting ? "animate-bounce" : ""} />
          {ingesting ? "Syncing sources…" : "Sync now"}
        </button>
        <p className="text-xs text-zinc-400">
          Pulls from Google Calendar, iMessage, AI agent, and music library
        </p>
      </div>
    </div>
  );
}
