import { useEffect, useMemo, useRef, useState } from 'react';
import { NavLink, useLocation, useNavigate } from 'react-router-dom';
import { ChevronRight, Folder, MessageSquare, Plus } from 'lucide-react';
import { toast } from 'sonner';
import { Button } from '@/components/ui/button';
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import {
  ContextMenu,
  ContextMenuContent,
  ContextMenuItem,
  ContextMenuSeparator,
  ContextMenuTrigger,
} from '@/components/ui/context-menu';
import { AddProjectModal } from './AddProjectModal';
import { api } from '@/api/client';
import { sessionEvents } from '@/hooks/sessionEvents';
import { useSidebar } from '@/hooks/useSidebar';
import type { SidebarProject } from '@/api/types';
import { cn } from '@/lib/utils';

const EXPANDED_KEY = 'boocode.sidebar.expanded';
const MAX_VISIBLE_SESSIONS = 5;

function readExpanded(): Set<string> {
  try {
    const raw = localStorage.getItem(EXPANDED_KEY);
    if (!raw) return new Set();
    const parsed = JSON.parse(raw);
    if (!Array.isArray(parsed)) return new Set();
    return new Set(parsed.filter((v): v is string => typeof v === 'string'));
  } catch {
    return new Set();
  }
}

function writeExpanded(ids: Set<string>): void {
  try {
    localStorage.setItem(EXPANDED_KEY, JSON.stringify(Array.from(ids)));
  } catch {
    /* quota or disabled storage — ignore */
  }
}

function relTime(iso: string): string {
  const now = Date.now();
  const t = Date.parse(iso);
  if (Number.isNaN(t)) return '';
  const sec = Math.max(0, Math.floor((now - t) / 1000));
  if (sec < 60) return `${sec}s`;
  const min = Math.floor(sec / 60);
  if (min < 60) return `${min}m`;
  const hr = Math.floor(min / 60);
  if (hr < 24) return `${hr}h`;
  const day = Math.floor(hr / 24);
  if (day < 30) return `${day}d`;
  const mo = Math.floor(day / 30);
  if (mo < 12) return `${mo}mo`;
  return `${Math.floor(mo / 12)}y`;
}

function activeProjectId(
  pathname: string,
  projects: SidebarProject[],
  activeSession: { session_id: string; project_id: string } | null
): string | null {
  const pm = pathname.match(/^\/project\/([^/]+)/);
  if (pm?.[1]) return pm[1];
  const sm = pathname.match(/^\/session\/([^/]+)/);
  const sid = sm?.[1];
  if (!sid) return null;
  // Prefer the cache lookup so we resolve correctly even when an older
  // activeSession (from a prior route) hasn't been cleared yet.
  const fromCache = projects.find((p) =>
    p.recent_sessions.some((s) => s.id === sid)
  )?.id;
  if (fromCache) return fromCache;
  // Fallback: the session was loaded via deep link (not in cache) and
  // emitted session_loaded — use that. Guard against stale values by
  // matching the current URL sid.
  if (activeSession && activeSession.session_id === sid) {
    return activeSession.project_id;
  }
  return null;
}

function activeSessionId(pathname: string): string | null {
  const m = pathname.match(/^\/session\/([^/]+)/);
  return m?.[1] ?? null;
}

export function ProjectSidebar() {
  const { data, error, loading, retry, activeSession: loadedActiveSession } =
    useSidebar();
  const [addOpen, setAddOpen] = useState(false);
  const [expanded, setExpanded] = useState<Set<string>>(() => readExpanded());
  const [renamingSession, setRenamingSession] = useState<string | null>(null);
  const [renameValue, setRenameValue] = useState('');
  const navigate = useNavigate();
  const location = useLocation();
  const lastToastedError = useRef<string | null>(null);

  useEffect(() => {
    if (error && !data && error !== lastToastedError.current) {
      toast.error(error);
      lastToastedError.current = error;
    }
    if (!error) lastToastedError.current = null;
  }, [error, data]);

  const projects = data?.projects ?? [];
  const activeProject = useMemo(
    () => activeProjectId(location.pathname, projects, loadedActiveSession),
    [location.pathname, projects, loadedActiveSession]
  );
  const activeSession = useMemo(
    () => activeSessionId(location.pathname),
    [location.pathname]
  );

  function toggle(id: string) {
    setExpanded((prev) => {
      const next = new Set(prev);
      if (next.has(id)) next.delete(id);
      else next.add(id);
      writeExpanded(next);
      return next;
    });
  }

  async function handleRemove(id: string) {
    try {
      await api.projects.remove(id);
      sessionEvents.emit({ type: 'project_deleted', project_id: id });
      navigate('/');
    } catch (err) {
      toast.error(err instanceof Error ? err.message : 'failed to remove project');
    }
  }

  async function handleArchiveSession(sessionId: string, projectId: string) {
    try {
      await api.sessions.archive(sessionId);
      sessionEvents.emit({ type: 'session_archived', session_id: sessionId, project_id: projectId });
      if (activeSession === sessionId) navigate(`/project/${projectId}`);
    } catch (err) {
      toast.error(err instanceof Error ? err.message : 'failed to archive session');
    }
  }

  async function handleRenameSession(sessionId: string) {
    const trimmed = renameValue.trim();
    setRenamingSession(null);
    if (!trimmed) return;
    try {
      await api.sessions.update(sessionId, { name: trimmed });
      sessionEvents.emit({ type: 'session_renamed', session_id: sessionId, name: trimmed });
    } catch (err) {
      toast.error(err instanceof Error ? err.message : 'failed to rename session');
    }
  }

  const rowCls = (active: boolean) =>
    active ? 'bg-sidebar-accent text-sidebar-accent-foreground' : 'hover:bg-sidebar-accent/60';

  return (
    <aside className="w-60 shrink-0 border-r bg-sidebar text-sidebar-foreground flex flex-col h-screen">
      <div className="px-4 py-3 border-b flex items-center justify-between">
        <NavLink to="/" className="font-semibold tracking-tight text-base">
          BooCode
        </NavLink>
        <Button size="icon-sm" variant="ghost" onClick={() => setAddOpen(true)} aria-label="Add project">
          <Plus />
        </Button>
      </div>

      <nav className="flex-1 overflow-y-auto py-2">
        {loading && data == null && (
          <div className="space-y-2 px-2">
            {[0, 1, 2, 3].map((i) => (
              <div key={i} className="bg-muted/40 animate-pulse rounded h-6" />
            ))}
          </div>
        )}
        {data != null && projects.length === 0 && (
          <div className="px-4 py-2 text-xs text-muted-foreground">
            No projects yet. Click + to add one.
          </div>
        )}
        {error != null && !data && (
          <div className="px-4 py-2 space-y-2">
            <div className="text-xs text-muted-foreground">{error}</div>
            <Button size="sm" variant="outline" onClick={retry}>
              Retry
            </Button>
          </div>
        )}
        {data != null &&
          projects.map((p) => {
            const isActiveProject = activeProject === p.id;
            const isExpanded = isActiveProject || expanded.has(p.id);
            const visible = p.recent_sessions.slice(0, MAX_VISIBLE_SESSIONS);
            return (
              <div key={p.id} className="px-2">
                <DropdownMenu>
                  <div
                    className={`group flex items-center gap-1 rounded-md px-2 py-1.5 text-sm ${rowCls(isActiveProject)}`}
                    onContextMenu={(e) => {
                      e.preventDefault();
                      (
                        e.currentTarget.parentElement?.querySelector(
                          '[data-ctxtrigger]'
                        ) as HTMLElement | null
                      )?.click();
                    }}
                  >
                    <button
                      type="button"
                      aria-label={isExpanded ? 'Collapse' : 'Expand'}
                      aria-expanded={isExpanded}
                      disabled={isActiveProject}
                      onClick={(e) => {
                        e.stopPropagation();
                        if (isActiveProject) return;
                        toggle(p.id);
                      }}
                      className={cn(
                        'flex items-center justify-center size-4 shrink-0 opacity-70 hover:opacity-100',
                        isActiveProject &&
                          'opacity-50 cursor-not-allowed hover:opacity-50'
                      )}
                    >
                      <ChevronRight
                        className={`size-3.5 transition-transform ${isExpanded ? 'rotate-90' : ''}`}
                      />
                    </button>
                    <NavLink to={`/project/${p.id}`} className="flex items-center gap-2 min-w-0 flex-1">
                      <Folder className="size-3.5 shrink-0 opacity-70" />
                      <span className="truncate" title={p.name}>{p.name}</span>
                    </NavLink>
                  </div>
                  <DropdownMenuTrigger asChild>
                    <button data-ctxtrigger className="hidden" aria-hidden />
                  </DropdownMenuTrigger>
                  <DropdownMenuContent align="start">
                    <DropdownMenuItem variant="destructive" onClick={() => void handleRemove(p.id)}>
                      Remove from sidebar
                    </DropdownMenuItem>
                  </DropdownMenuContent>
                </DropdownMenu>

                {isExpanded && (
                  <div className="ml-5 mt-0.5 space-y-0.5">
                    {visible.map((s) => (
                      <ContextMenu key={s.id}>
                        <ContextMenuTrigger asChild>
                          {renamingSession === s.id ? (
                            <div className={`flex items-center gap-2 rounded-md px-2 py-1 text-sm min-w-0 ${rowCls(activeSession === s.id)}`}>
                              <MessageSquare className="size-3.5 shrink-0 opacity-70" />
                              <input
                                autoFocus
                                value={renameValue}
                                onChange={(e) => setRenameValue(e.target.value)}
                                onBlur={() => void handleRenameSession(s.id)}
                                onKeyDown={(e) => {
                                  if (e.key === 'Enter') void handleRenameSession(s.id);
                                  if (e.key === 'Escape') setRenamingSession(null);
                                }}
                                className="bg-transparent border-b border-border text-sm outline-none flex-1 min-w-0"
                              />
                            </div>
                          ) : (
                            <NavLink
                              to={`/session/${s.id}`}
                              className={`flex items-center gap-2 rounded-md px-2 py-1 text-sm min-w-0 ${rowCls(activeSession === s.id)}`}
                            >
                              <MessageSquare className="size-3.5 shrink-0 opacity-70" />
                              <span className="truncate flex-1" title={s.name}>{s.name}</span>
                              <span className="text-xs text-muted-foreground shrink-0 tabular-nums">
                                {relTime(s.updated_at)}
                              </span>
                            </NavLink>
                          )}
                        </ContextMenuTrigger>
                        <ContextMenuContent>
                          <ContextMenuItem onSelect={() => {
                            setRenamingSession(s.id);
                            setRenameValue(s.name);
                          }}>
                            Rename
                          </ContextMenuItem>
                          <ContextMenuSeparator />
                          <ContextMenuItem onSelect={() => void handleArchiveSession(s.id, p.id)}>
                            Archive
                          </ContextMenuItem>
                        </ContextMenuContent>
                      </ContextMenu>
                    ))}
                    {p.total_sessions > MAX_VISIBLE_SESSIONS && (
                      <NavLink
                        to={`/project/${p.id}`}
                        className="block rounded-md px-2 py-1 text-xs text-muted-foreground hover:bg-sidebar-accent/60"
                      >
                        View all ({p.total_sessions})
                      </NavLink>
                    )}
                  </div>
                )}
              </div>
            );
          })}
      </nav>

      <AddProjectModal open={addOpen} onOpenChange={setAddOpen} onAdded={() => {}} />
    </aside>
  );
}
