import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { Sparkles, FileText, CheckCircle2, CalendarDays, Plus, StickyNote } from 'lucide-react';
import { useNavigate } from '@tanstack/react-router';
import { useTranslation } from 'react-i18next';
import { trpc } from '@/lib/trpc';
import { useFormatPrefs, formatDate } from '@/lib/date';
import { useNotify } from '@/hooks/useNotify';
import { Button } from '@/components/ui/button';
import { Skeleton } from '@/components/ui/skeleton';
import { Item, ItemMedia, ItemContent, ItemTitle, ItemDescription } from '@/components/ui/item';
import {
  Breadcrumb,
  BreadcrumbItem,
  BreadcrumbList,
  BreadcrumbSeparator,
} from '@/components/ui/breadcrumb';
import { TaskListView } from '@/components/tasks/TaskListView';
import { Empty, EmptyHeader, EmptyMedia, EmptyTitle, EmptyDescription } from '@/components/ui/empty';
import { type TimelineEvent } from '@/components/timeline/ProjectTimeline';
import { type ProjectGroup } from '@/components/timeline/ProjectTimelineBox';
import { TimelineGanttView } from '@/components/timeline/TimelineGanttView';
import { AddEventDialog } from '@/components/timeline/AddEventDialog';
import { EditEventDialog } from '@/components/timeline/EditEventDialog';
import { useFloatingChat } from '@/context/FloatingChatContext';
import { useTimelineHistory } from '@/hooks/useTimelineHistory';
import type { EventSnapshot } from '@/components/timeline/history-types';
import { cn } from '@/lib/utils';
import { ProjectTabBar, SECTIONS, type SectionId } from './ProjectTabBar';
import { FolderChip } from './folder/FolderChip';
import { FilesSection } from './folder/FilesSection';

type ProjectDetailProps = {
  projectId: string;
  initialTab?: string;
};

export function ProjectDetail({ projectId, initialTab }: ProjectDetailProps) {
  const { t } = useTranslation();
  const prefs = useFormatPrefs();
  const navigate = useNavigate();
  const [addEventOpen, setAddEventOpen] = useState(false);
  const [editingEvent, setEditingEvent] = useState<TimelineEvent | null>(null);
  const [compact, setCompact] = useState(false);

  const scrollRef = useRef<HTMLDivElement>(null);
  const heroRef = useRef<HTMLDivElement>(null);
  const summaryRef = useRef<HTMLDivElement>(null);
  const timelineRef = useRef<HTMLDivElement>(null);
  const tasksRef = useRef<HTMLDivElement>(null);
  const notesRef = useRef<HTMLDivElement>(null);
  const filesRef = useRef<HTMLDivElement>(null);
  const sectionRefs: Record<SectionId, React.RefObject<HTMLDivElement | null>> = useMemo(() => ({
    overview: summaryRef,
    timeline: timelineRef,
    tasks: tasksRef,
    notes: notesRef,
    files: filesRef,
  }), []);

  const didInitialScroll = useRef(false);

  const { registerSection, unregisterSection } = useFloatingChat();
  const { data: project, isLoading } = trpc.projects.get.useQuery({ id: projectId });

  const { data: scanStatus } = trpc.projectFolders.getStatus.useQuery(
    { projectId },
    { refetchInterval: (query) => query.state.data?.status === 'scanning' ? 1000 : false },
  );

  const {
    historyOpRef,
    pendingCreatePayloadRef,
    record,
    undo,
    redo,
    canUndo,
    canRedo,
    clearHistory,
    updateMutateRef,
    deleteMutateRef,
    createMutateRef,
  } = useTimelineHistory({ disabled: addEventOpen || editingEvent !== null });

  // Latest-ref pattern for unstable closures so useCallback deps stay stable
  const recordRef = useRef(record);
  recordRef.current = record;
  const clearHistoryRef = useRef(clearHistory);
  clearHistoryRef.current = clearHistory;

  useEffect(() => {
    if (isLoading || !project) return;
    registerSection({ id: 'project-summary', label: 'Project Summary', ref: summaryRef, projectId });
    registerSection({ id: 'project-tasks', label: 'Tasks', ref: tasksRef, projectId });
    registerSection({ id: 'project-notes', label: 'Notes', ref: notesRef, projectId });
    return () => {
      unregisterSection('project-summary');
      unregisterSection('project-tasks');
      unregisterSection('project-notes');
    };
  }, [projectId, isLoading, project, registerSection, unregisterSection]);

  // Compact hero on scroll. scrollRef is the definitive scroll container
  // (flex-1 min-h-0 overflow-y-auto inside a flex-col parent with min-h-0
  // at every ancestor, so h-full never resolves to content-height).
  // Hysteresis: compact at >80, un-compact only at 0 — layout shift from the
  // hero re-growing can't trigger oscillation because it can't push scrollTop
  // back to exactly 0.
  // Re-run when project arrives — loading state renders a different DOM tree
  // without scrollRef, so the initial commit's scrollRef.current is null on a
  // cold mount (e.g. opening from the AppShell sidebar).
  useEffect(() => {
    const el = scrollRef.current;
    if (!el) return;
    const handle = () => {
      const top = el.scrollTop;
      setCompact((prev) => {
        if (prev && top <= 0) return false;
        if (!prev && top > 80) return true;
        return prev;
      });
    };
    el.addEventListener('scroll', handle, { passive: true });
    return () => el.removeEventListener('scroll', handle);
  }, [project]);

  // Track hero height as a CSS custom property on the scroll container so the
  // tab bar top offset updates without any React re-render. Re-run when project
  // data arrives — hero element only mounts after isLoading=false, so the
  // initial mount's heroRef.current is null on a cold route entry.
  useEffect(() => {
    const el = heroRef.current;
    if (!el) return;
    const measure = () => {
      const h = el.getBoundingClientRect().height;
      scrollRef.current?.style.setProperty('--hero-h', `${h}px`);
    };
    measure();
    const obs = new ResizeObserver(measure);
    obs.observe(el);
    return () => obs.disconnect();
  }, [project]);

  // Scroll to initialTab on mount
  useEffect(() => {
    if (didInitialScroll.current || !initialTab || !SECTIONS.includes(initialTab as SectionId)) return;
    const ref = sectionRefs[initialTab as SectionId];
    const scrollEl = scrollRef.current;
    if (!ref?.current || !scrollEl) return;
    didInitialScroll.current = true;
    const heroH = heroRef.current?.getBoundingClientRect().height ?? 88;
    const top = ref.current.offsetTop - heroH - 41;
    scrollEl.scrollTo({ top: Math.max(0, top), behavior: 'smooth' });
  }, [initialTab, sectionRefs]);

  const utils = trpc.useUtils();
  const { data: clientsList } = trpc.clients.list.useQuery();
  const { data: notesList } = trpc.notes.list.useQuery({ projectId });
  const { data: tasksList } = trpc.tasks.list.useQuery({ projectId });
  const { data: eventsList } = trpc.timelineEvents.list.useQuery({ projectId });

  const breadcrumbPath = useMemo(() => {
    if (!project?.clientId || !clientsList) return [];
    const clientMap = new Map(clientsList.map((c) => [c.id, c]));
    const client = clientMap.get(project.clientId);
    if (!client) return [];
    if (client.parentId) {
      const parent = clientMap.get(client.parentId);
      if (parent) return [parent.name, client.name];
    }
    return [client.name];
  }, [project?.clientId, clientsList]);

  const notesCount = notesList?.length ?? 0;

  const taskStats = useMemo(() => {
    const all = tasksList ?? [];
    const done = all.filter((t) => t.status === 'done').length;
    return { done, total: all.length };
  }, [tasksList]);

  const eventStats = useMemo(() => {
    const all = eventsList ?? [];
    const completed = all.filter((e) => e.isCompleted === 1).length;
    return { completed, total: all.length };
  }, [eventsList]);

  const timelineEvents: TimelineEvent[] = useMemo(() => {
    return (eventsList ?? []).map((e) => ({
      id: e.id,
      title: e.title,
      date: e.date,
      endDate: e.endDate,
      type: (e.type as TimelineEvent['type']) ?? 'milestone',
      projectId: e.projectId,
      isCompleted: e.isCompleted,
      isAiSuggested: e.isAiSuggested,
    }));
  }, [eventsList]);

  const { notify, notifyError } = useNotify();

  const deleteEvent = trpc.timelineEvents.delete.useMutation({
    onSuccess: () => {
      if (!historyOpRef.current) notify('warning', 'toast.timeline.deleted');
      historyOpRef.current = false;
      void utils.timelineEvents.list.invalidate({ projectId });
    },
    onError: (err) => {
      if (historyOpRef.current) {
        historyOpRef.current = false;
        clearHistoryRef.current();
        notify('warning', 'toast.timeline.historyError');
      } else {
        notifyError('toast.timeline.deleteError', err);
      }
    },
  });

  const updateEvent = trpc.timelineEvents.update.useMutation({
    onSuccess: () => {
      if (!historyOpRef.current) notify('success', 'toast.timeline.updated');
      historyOpRef.current = false;
      void utils.timelineEvents.list.invalidate({ projectId });
    },
    onError: (err) => {
      if (historyOpRef.current) {
        historyOpRef.current = false;
        clearHistoryRef.current();
        notify('warning', 'toast.timeline.historyError');
      } else {
        notifyError('toast.timeline.updateError', err);
      }
    },
  });

  const createEvent = trpc.timelineEvents.create.useMutation({
    onSuccess: (data) => {
      if (!historyOpRef.current) {
        notify('success', 'toast.timeline.created');
        const payload = pendingCreatePayloadRef.current;
        pendingCreatePayloadRef.current = null;
        if (payload) {
          recordRef.current({ kind: 'create', id: data.id, payload: { ...payload, id: data.id } });
        }
      }
      historyOpRef.current = false;
      void utils.timelineEvents.list.invalidate({ projectId });
    },
    onError: (err) => {
      pendingCreatePayloadRef.current = null;
      if (historyOpRef.current) {
        historyOpRef.current = false;
        clearHistoryRef.current();
        notify('warning', 'toast.timeline.historyError');
      } else {
        notifyError('toast.timeline.createError', err);
      }
    },
  });

  updateMutateRef.current = updateEvent.mutate;
  deleteMutateRef.current = deleteEvent.mutate;
  createMutateRef.current = createEvent.mutate;

  const timelineHeight = useMemo(() => {
    const GANTT_ROW_H = 28;
    const GANTT_AXIS_H = 46;
    // 1 group-header row + one row per event
    const rowCount = 1 + timelineEvents.length;
    const ganttContentH = GANTT_AXIS_H + rowCount * GANTT_ROW_H;
    // 40px controls bar + 16px gap + ganttContent + 16px bottom pad
    return Math.min(560, Math.max(200, 40 + 16 + ganttContentH + 16));
  }, [timelineEvents.length]);

  const projectGroup: ProjectGroup = useMemo(() => ({
    projectId,
    projectName: project?.name ?? '',
    projectStatus: project?.status ?? 'active',
    breadcrumb: breadcrumbPath,
    events: timelineEvents,
    startDate: new Date(),
    endDate: new Date(),
  }), [projectId, project?.name, project?.status, breadcrumbPath, timelineEvents]);

  const handleToggleComplete = useCallback((id: string, current: number) => {
    recordRef.current({
      kind: 'update',
      id,
      prev: { isCompleted: current },
      next: { isCompleted: current === 1 ? 0 : 1 },
    });
    updateEvent.mutate({ id, isCompleted: current === 1 ? 0 : 1 });
  }, [updateEvent]);

  const handleDeleteEvent = useCallback((id: string) => {
    const ev = timelineEvents.find((e) => e.id === id);
    if (ev) {
      recordRef.current({
        kind: 'delete',
        id,
        snapshot: {
          id: ev.id,
          projectId: ev.projectId ?? null,
          title: ev.title,
          date: ev.date,
          endDate: ev.endDate ?? null,
          type: (ev.type ?? 'milestone') as EventSnapshot['type'],
          isCompleted: ev.isCompleted ?? 0,
          isAiSuggested: ev.isAiSuggested ?? 0,
        },
      });
    }
    deleteEvent.mutate({ id });
  }, [timelineEvents, deleteEvent]);

  const handleEditEvent = useCallback((ev: TimelineEvent) => {
    setEditingEvent(ev);
  }, []);

  const handleDuplicate = useCallback((ev: TimelineEvent) => {
    pendingCreatePayloadRef.current = {
      id: '',
      projectId: ev.projectId ?? null,
      title: `${ev.title} (copy)`,
      date: ev.date,
      endDate: ev.endDate ?? null,
      type: (ev.type ?? 'milestone') as EventSnapshot['type'],
      isCompleted: ev.isCompleted ?? 0,
      isAiSuggested: ev.isAiSuggested ?? 0,
    };
    createEvent.mutate({
      title: `${ev.title} (copy)`,
      date: ev.date,
      endDate: ev.endDate ?? undefined,
      projectId: ev.projectId ?? undefined,
      type: ev.type,
    });
  }, [createEvent, pendingCreatePayloadRef]);

  const handleMoveEvent = useCallback((id: string, date: number, endDate: number | null) => {
    const ev = timelineEvents.find((e) => e.id === id);
    if (ev) {
      recordRef.current({
        kind: 'update',
        id,
        prev: { date: ev.date, endDate: ev.endDate ?? null },
        next: { date, endDate },
      });
    }
    updateEvent.mutate({ id, date, endDate: endDate ?? null });
  }, [timelineEvents, updateEvent]);

  const handleAddEvent = useCallback(() => setAddEventOpen(true), []);

  const updateTask = trpc.tasks.update.useMutation({
    onSuccess: () => { void utils.tasks.list.invalidate({ projectId }); },
    onError: (err) => notifyError('toast.task.updateError', err),
  });

  const deleteTask = trpc.tasks.delete.useMutation({
    onSuccess: () => {
      notify('warning', 'toast.task.deleted');
      void utils.tasks.list.invalidate({ projectId });
    },
    onError: (err) => notifyError('toast.task.deleteError', err),
  });

  const suggestTasks = trpc.ai.chat.useMutation({
    onSuccess: () => { void utils.tasks.list.invalidate({ projectId }); },
  });

  const createNote = trpc.notes.create.useMutation({
    onSuccess: (data) => {
      notify('success', 'toast.note.created');
      void utils.notes.list.invalidate({ projectId });
      void navigate({ to: '/notes/$noteId', params: { noteId: data.id } });
    },
    onError: (err) => notifyError('toast.note.createError', err),
  });

  // Suppress unused-variable lints for variables used indirectly via refs
  void updateTask;
  void deleteTask;
  void suggestTasks;

  if (isLoading) {
    return (
      <div className="flex-1 min-h-0 overflow-y-auto">
        <div className="mx-auto py-10 px-8 w-full max-w-6xl flex flex-col gap-6">
          <div className="flex flex-col gap-2 pb-1">
            <Skeleton className="h-4 w-28" />
            <Skeleton className="h-9 w-64" />
            <Skeleton className="h-4 w-40" />
          </div>
          <Skeleton className="h-9 w-80" />
          <div className="grid grid-cols-3 gap-4 mt-6">
            <Skeleton className="h-20 rounded-lg" />
            <Skeleton className="h-20 rounded-lg" />
            <Skeleton className="h-20 rounded-lg" />
          </div>
          <Skeleton className="h-16 rounded-lg" />
        </div>
      </div>
    );
  }

  if (!project) {
    return (
      <div className="flex items-center justify-center h-full text-sm text-muted-foreground">
        {t('projects.projectNotFound')}
      </div>
    );
  }

  const projectName = breadcrumbPath.length > 0 ? project.name : '';
  const subtitle = breadcrumbPath.length > 0 ? breadcrumbPath[breadcrumbPath.length - 1] : project.name;

  return (
    <div ref={scrollRef} className="flex-1 min-h-0 overflow-y-auto">

      {/* Sticky shrinking hero */}
      <div
        ref={heroRef}
        data-compact={compact}
        className={cn(
          'sticky top-0 z-30 backdrop-blur-md border-b border-border/40',
          'transition-[padding] duration-200 ease-out',
          compact ? 'py-2' : 'pt-8 pb-4',
        )}
      >
        <div className="mx-auto max-w-6xl px-8">
          {breadcrumbPath.length > 1 && (
            <div
              className={cn(
                'overflow-hidden transition-all duration-200 ease-out',
                compact ? 'max-h-0 mb-0 opacity-0' : 'max-h-6 mb-3 opacity-100',
              )}
            >
              <Breadcrumb>
                <BreadcrumbList>
                  {breadcrumbPath.slice(0, -1).map((segment, i) => (
                    <Fragment key={i}>
                      {i > 0 && <BreadcrumbSeparator />}
                      <BreadcrumbItem>
                        <span className="text-muted-foreground text-xs">{segment}</span>
                      </BreadcrumbItem>
                    </Fragment>
                  ))}
                </BreadcrumbList>
              </Breadcrumb>
            </div>
          )}
          <div className="flex items-start gap-4">
            <h1
              className={cn(
                'flex-1 min-w-0 font-semibold tracking-tight transition-[font-size,line-height] duration-200 ease-out',
                compact ? 'text-base leading-tight' : 'text-3xl leading-tight',
              )}
            >
              {projectName}
              {projectName && subtitle && (
                compact
                  ? <span className="text-muted-foreground/60 font-normal"> · </span>
                  : <br />
              )}
              <span className={cn(compact ? 'text-muted-foreground/60 font-normal' : 'text-muted-foreground/50')}>
                {subtitle}
              </span>
            </h1>
            <div
              className={cn(
                'shrink-0 overflow-hidden transition-all duration-200 ease-out',
                compact ? 'max-w-0 max-h-0 opacity-0' : 'max-w-xs max-h-8 opacity-100',
              )}
            >
              <FolderChip
                projectId={project.id}
                folderPath={project.folderPath ?? null}
                totalFiles={project.folderTotalFiles ?? 0}
                lastScannedAt={project.folderLastScannedAt ?? null}
                scanStatus={(project.folderLastScanStatus ?? 'idle') as 'idle' | 'scanning' | 'error'}
                scanProgress={scanStatus && scanStatus.status === 'scanning'
                  ? { processed: scanStatus.processed, total: scanStatus.total }
                  : null}
                onClick={() => {
                  const el = scrollRef.current;
                  const ref = filesRef.current;
                  if (el && ref) {
                    const heroH = heroRef.current?.getBoundingClientRect().height ?? 88;
                    const sectionTop = ref.getBoundingClientRect().top;
                    const containerTop = el.getBoundingClientRect().top;
                    const top = el.scrollTop + sectionTop - containerTop - heroH - 41;
                    el.scrollTo({ top: Math.max(0, top), behavior: 'smooth' });
                  }
                  void navigate({
                    search: (prev: Record<string, string | undefined>) => ({ ...prev, tab: 'files' }),
                    replace: true,
                  });
                }}
              />
            </div>
          </div>
        </div>
      </div>

      {/* Sticky tab bar — owns activeSection state and scroll spy */}
      <ProjectTabBar
        sectionRefs={sectionRefs}
        scrollRef={scrollRef}
        heroRef={heroRef}
        initialTab={initialTab}
      />

      {/* Page body */}
      <div className="mx-auto max-w-6xl px-8 py-8 flex flex-col gap-16 w-full">

        {/* Overview section */}
        <section
          ref={summaryRef}
          data-section="overview"
          data-ai-section="project-summary"
          className="flex flex-col gap-4"
        >
          <h1 className="text-2xl font-semibold tracking-tight">{t('projects.overview')}</h1>
          <div className="grid grid-cols-3 gap-4">
            <Item variant="muted">
              <ItemMedia variant="icon"><FileText /></ItemMedia>
              <ItemContent>
                <ItemTitle>{notesCount}</ItemTitle>
                <ItemDescription>{t('projects.notes')}</ItemDescription>
              </ItemContent>
            </Item>
            <Item variant="muted">
              <ItemMedia variant="icon"><CheckCircle2 /></ItemMedia>
              <ItemContent>
                <ItemTitle>{taskStats.done}/{taskStats.total}</ItemTitle>
                <ItemDescription>{t('projects.tasksComplete')}</ItemDescription>
              </ItemContent>
            </Item>
            <Item variant="muted">
              <ItemMedia variant="icon"><CalendarDays /></ItemMedia>
              <ItemContent>
                <ItemTitle>{eventStats.completed}/{eventStats.total}</ItemTitle>
                <ItemDescription>{t('projects.events')}</ItemDescription>
              </ItemContent>
            </Item>
          </div>
          <Item variant="outline">
            <ItemMedia variant="icon"><Sparkles /></ItemMedia>
            <ItemContent>
              <ItemTitle>{t('projects.aiProjectSummary')}</ItemTitle>
              <ItemDescription>
                {project.aiSummary || t('projects.aiSummaryPlaceholder')}
              </ItemDescription>
            </ItemContent>
          </Item>
        </section>

        {/* Timeline section */}
        <section
          ref={timelineRef}
          data-section="timeline"
          className="flex flex-col gap-4"
        >
          <h1 className="text-2xl font-semibold tracking-tight">{t('projects.projectTimeline')}</h1>
          <div style={{ height: timelineHeight }}>
            <TimelineGanttView
              className="h-full"
              projectGroups={[projectGroup]}
              events={timelineEvents}
              canUndo={canUndo}
              canRedo={canRedo}
              onUndo={undo}
              onRedo={redo}
              onAdd={handleAddEvent}
              onToggleComplete={handleToggleComplete}
              onDelete={handleDeleteEvent}
              onEdit={handleEditEvent}
              onDuplicate={handleDuplicate}
              onMove={handleMoveEvent}
              sectionId="project-timeline"
              sectionLabel="Project Timeline"
              projectId={projectId}
            />
          </div>
          <AddEventDialog
            open={addEventOpen}
            onOpenChange={setAddEventOpen}
            defaultProjectId={projectId}
            onRecordHistory={record}
          />
          <EditEventDialog
            event={editingEvent}
            onOpenChange={(open) => { if (!open) setEditingEvent(null); }}
            onRecordHistory={record}
          />
        </section>

        {/* Tasks section */}
        <section
          ref={tasksRef}
          data-section="tasks"
          data-ai-section="project-tasks"
          className="flex flex-col gap-4"
        >
          <h1 className="text-2xl font-semibold tracking-tight">{t('projects.tasks')}</h1>
          <TaskListView projectId={projectId} hideProjectColumn />
        </section>

        {/* Notes section */}
        <section
          ref={notesRef}
          data-section="notes"
          data-ai-section="project-notes"
          className="flex flex-col gap-4 pb-16"
        >
          <div className="flex items-center justify-between">
            <h1 className="text-2xl font-semibold tracking-tight">{t('projects.notes')}</h1>
            <Button
              size="sm"
              disabled={createNote.isPending}
              onClick={() =>
                createNote.mutate({ title: t('projects.untitledNote'), content: '', projectId })
              }
            >
              <Plus data-icon="inline-start" />
              {t('common.add')}
            </Button>
          </div>
          {notesList && notesList.length > 0 ? (
            <div className="rounded-lg border divide-y divide-border overflow-hidden">
              {notesList.map((note) => (
                <button
                  key={note.id}
                  className="w-full flex items-center gap-3 px-4 py-2.5 text-left transition-colors hover:bg-muted/50"
                  onClick={() =>
                    void navigate({ to: '/notes/$noteId', params: { noteId: note.id } })
                  }
                >
                  <FileText className="h-4 w-4 shrink-0 text-muted-foreground" />
                  <span className="flex-1 truncate text-sm font-medium">{note.title}</span>
                  <span className="ml-auto whitespace-nowrap text-xs text-muted-foreground">
                    {formatDate(note.createdAt, prefs)}
                  </span>
                </button>
              ))}
            </div>
          ) : (
            <Empty>
              <EmptyHeader>
                <EmptyMedia variant="icon"><StickyNote /></EmptyMedia>
                <EmptyTitle>{t('projects.noNotesYet')}</EmptyTitle>
                <EmptyDescription>{t('projects.noNotesYetDescription')}</EmptyDescription>
              </EmptyHeader>
            </Empty>
          )}
        </section>

        {/* Files section */}
        <section
          ref={filesRef}
          data-section="files"
          className="flex flex-col gap-4 pb-16"
        >
          <h1 className="text-2xl font-semibold tracking-tight">{t('projects.folder.title')}</h1>
          <FilesSection
            projectId={project.id}
            folderPath={project.folderPath ?? null}
            totalFiles={project.folderTotalFiles ?? 0}
            lastScannedAt={project.folderLastScannedAt ?? null}
            scanStatus={(project.folderLastScanStatus ?? 'idle') as 'idle' | 'scanning' | 'error'}
          />
        </section>

      </div>
    </div>
  );
}

