import { createFileRoute } from '@tanstack/react-router';
import { useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Archive, ArchiveX } from 'lucide-react';
import { trpc } from '@/lib/trpc';
import { useNotify } from '@/hooks/useNotify';
import { Switch } from '@/components/ui/switch';
import { Label } from '@/components/ui/label';
import { Button } from '@/components/ui/button';
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
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 { useTimelineHistory } from '@/hooks/useTimelineHistory';
import type { EventSnapshot } from '@/components/timeline/history-types';

export const Route = createFileRoute('/timeline')({
  component: TimelinePage,
});

function TimelinePage() {
  const { t } = useTranslation();
  const [dialogOpen, setDialogOpen] = useState(false);
  const [editingEvent, setEditingEvent] = useState<TimelineEvent | null>(null);
  const [showArchived, setShowArchived] = useState(false);

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

  const { data: events } = trpc.timelineEvents.list.useQuery({});
  const { data: projectsList } = trpc.projects.list.useQuery({ includeArchived: true });
  const { data: clientsList } = trpc.clients.list.useQuery();
  const utils = trpc.useUtils();
  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();
    },
    onError: (err) => {
      if (historyOpRef.current) {
        historyOpRef.current = false;
        clearHistory();
        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();
    },
    onError: (err) => {
      if (historyOpRef.current) {
        historyOpRef.current = false;
        clearHistory();
        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) {
          record({ kind: 'create', id: data.id, payload: { ...payload, id: data.id } });
        }
      }
      historyOpRef.current = false;
      void utils.timelineEvents.list.invalidate();
    },
    onError: (err) => {
      pendingCreatePayloadRef.current = null;
      if (historyOpRef.current) {
        historyOpRef.current = false;
        clearHistory();
        notify('warning', 'toast.timeline.historyError');
      } else {
        notifyError('toast.timeline.createError', err);
      }
    },
  });

  // Wire latest mutation functions into refs so undo/redo can call them
  updateMutateRef.current = updateEvent.mutate;
  deleteMutateRef.current = deleteEvent.mutate;
  createMutateRef.current = createEvent.mutate;

  const projectMap = useMemo(() => {
    const map = new Map<string, { name: string; status: string; clientId: string | null }>();
    for (const p of projectsList ?? []) {
      map.set(p.id, { name: p.name, status: p.status, clientId: p.clientId });
    }
    return map;
  }, [projectsList]);

  const clientMap = useMemo(() => {
    const map = new Map<string, { name: string; parentId: string | null }>();
    for (const c of clientsList ?? []) {
      map.set(c.id, { name: c.name, parentId: c.parentId });
    }
    return map;
  }, [clientsList]);

  function getBreadcrumb(projectId: string): string[] {
    const project = projectMap.get(projectId);
    if (!project?.clientId) return [];
    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];
  }

  const timelineEvents: TimelineEvent[] = useMemo(() => {
    return (events ?? []).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,
    }));
  }, [events]);

  const projectGroups: ProjectGroup[] = useMemo(() => {
    const grouped = new Map<string, TimelineEvent[]>();
    for (const ev of timelineEvents) {
      const key = ev.projectId ?? '__no_project__';
      if (!grouped.has(key)) grouped.set(key, []);
      grouped.get(key)!.push(ev);
    }

    const groups: ProjectGroup[] = [];
    const placeholder = new Date();
    for (const [key, groupEvents] of grouped) {
      const isNoProject = key === '__no_project__';
      const project = isNoProject ? null : projectMap.get(key);
      if (project && project.status === 'archived' && !showArchived) continue;

      groups.push({
        projectId: isNoProject ? null : key,
        projectName: isNoProject ? t('timeline.noProject') : (project?.name ?? t('timeline.unknown')),
        projectStatus: isNoProject ? 'active' : (project?.status ?? 'active'),
        breadcrumb: isNoProject ? [] : getBreadcrumb(key),
        events: groupEvents,
        startDate: placeholder,
        endDate: placeholder,
      });
    }

    groups.sort((a, b) => {
      if (a.projectStatus !== b.projectStatus) return a.projectStatus === 'active' ? -1 : 1;
      return a.projectName.localeCompare(b.projectName);
    });
    return groups;
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [timelineEvents, projectMap, clientMap, showArchived, t]);

  function handleDuplicate(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,
    });
  }

  function handleMove(id: string, date: number, endDate: number | null) {
    const ev = timelineEvents.find((e) => e.id === id);
    if (ev) {
      record({
        kind: 'update',
        id,
        prev: { date: ev.date, endDate: ev.endDate ?? null },
        next: { date, endDate },
      });
    }
    updateEvent.mutate({ id, date, endDate: endDate ?? null });
  }

  return (
    <div className="flex-1 min-h-0 flex flex-col p-6 pt-0 w-full">
      <TimelineGanttView
        className="flex-1 min-h-0"
        projectGroups={projectGroups}
        events={timelineEvents}
        canUndo={canUndo}
        canRedo={canRedo}
        onUndo={undo}
        onRedo={redo}
        onAdd={() => setDialogOpen(true)}
        renderHeaderExtras={(compact) =>
          compact ? (
            <Tooltip>
              <TooltipTrigger asChild>
                <Button
                  size="sm"
                  variant="ghost"
                  className="h-8 w-8 p-0"
                  onClick={() => setShowArchived((v) => !v)}
                >
                  {showArchived
                    ? <ArchiveX className="size-3.5" />
                    : <Archive className="size-3.5" />}
                </Button>
              </TooltipTrigger>
              <TooltipContent>{t('timeline.showArchived')}</TooltipContent>
            </Tooltip>
          ) : (
            <div className="flex items-center gap-1.5">
              <Label htmlFor="show-archived" className="text-xs text-muted-foreground">
                {t('timeline.showArchived')}
              </Label>
              <Switch id="show-archived" checked={showArchived} onCheckedChange={setShowArchived} />
            </div>
          )
        }
        onToggleComplete={(id, current) => {
          record({
            kind: 'update',
            id,
            prev: { isCompleted: current },
            next: { isCompleted: current === 1 ? 0 : 1 },
          });
          updateEvent.mutate({ id, isCompleted: current === 1 ? 0 : 1 });
        }}
        onDelete={(id) => {
          const ev = timelineEvents.find((e) => e.id === id);
          if (ev) {
            record({
              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 });
        }}
        onEdit={(ev) => setEditingEvent(ev)}
        onDuplicate={handleDuplicate}
        onMove={handleMove}
        sectionId="timeline-chart"
        sectionLabel="Timeline"
      />
      <AddEventDialog open={dialogOpen} onOpenChange={setDialogOpen} onRecordHistory={record} />
      <EditEventDialog
        event={editingEvent}
        onOpenChange={(open) => { if (!open) setEditingEvent(null); }}
        onRecordHistory={record}
      />
    </div>
  );
}
