import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';

const FileTreeTabContext = createContext(null);

export function FileTreeTabProvider({ children, toggleVisible, visibility, loginSeq, openModal }) {
  const [tab, setTab] = useState('workspace');
  // This provider is mounted once for the app's entire lifetime (see
  // App.jsx), not per-login, so switching connections doesn't remount it and
  // reset `tab` on its own. `loginSeq` increments once per successful login
  // (ConnectionContext); resetting here on every change (including the
  // initial mount) makes each freshly-connected project default to the
  // Workspace tab rather than sticking on whatever tab was last active.
  useEffect(() => {
    setTab('workspace');
    setHistoryView('list');
  }, [loginSeq]);
  // Both trees load after connecting (depot from the server, workspace from
  // disk); selections start empty until RepoPanel populates them.
  const [repoSelection, setRepoSelection] = useState({
    selectedIds: new Set(),
    tree: null,
    anchorId: null,
  });
  const [workspaceSelection, setWorkspaceSelection] = useState({
    selectedIds: new Set(),
    tree: null,
    anchorId: null,
  });
  // Published by RepoPanel so the History panel's graph can resolve a file's
  // server-head and last-synced content hashes (it has no file-control hook of
  // its own). serverHashes: path -> head hash|null; syncedHashes: path ->
  // synced hash|null.
  const [fileHashes, setFileHashes] = useState({
    serverHashes: new Map(),
    syncedHashes: new Map(),
  });
  // Published by RepoPanel: true when the changelist holds anything at all.
  // The Repository panel header reads it to shimmer "List Changes" (see
  // PanelHeaderMenus.jsx). A boolean rather than a count — nothing displays a
  // number, and a count would restart the animation on every edit.
  const [hasPendingChanges, setHasPendingChanges] = useState(false);
  const [highlightRequest, setHighlightRequest] = useState(null);
  const highlightSeq = useRef(0);
  // "List Changes" in the Repository panel header opens the submit modal with
  // the entire changelist; RepoPanel consumes the request.
  const [listChangesRequest, setListChangesRequest] = useState(null);
  const listChangesSeq = useRef(0);

  const requestHighlightFiles = useCallback(
    (relativePaths) => {
      if (visibility && !visibility.repo) {
        toggleVisible?.('repo', true);
      }
      setTab('repo');
      highlightSeq.current += 1;
      setHighlightRequest({ paths: relativePaths, seq: highlightSeq.current });
    },
    [toggleVisible, visibility]
  );

  const requestListChanges = useCallback(() => {
    listChangesSeq.current += 1;
    setListChangesRequest({ seq: listChangesSeq.current });
  }, []);
  // One-shot, like rollback below: this provider outlives RepoPanel, so a
  // consumed request left in state re-fires on every remount (each fresh
  // mount's seq guard starts at 0) — leaving and returning to the Revision
  // Control page kept popping the submit modal / "Nothing to submit".
  const clearListChangesRequest = useCallback(() => setListChangesRequest(null), []);
  // Same one-shot rule for highlights: a stale request would re-expand and
  // re-select the old paths on every return to the page.
  const clearHighlightRequest = useCallback(() => setHighlightRequest(null), []);

  // History panel view mode. Lives here (not in HistoryPanel) because the
  // panel header button (Panel.jsx) and the revision-detail modal's
  // "Show selected in graph view" both need to flip it.
  const [historyView, setHistoryView] = useState('list');

  // One-shot "open this file in the graph view" request (revision-detail
  // modal). HistoryPanel consumes it; same lifecycle rules as the other
  // requests in this file.
  const [graphViewRequest, setGraphViewRequest] = useState(null);
  const graphViewSeq = useRef(0);
  const requestGraphView = useCallback(
    (relativePath) => {
      if (visibility && !visibility.history) {
        toggleVisible?.('history', true);
      }
      setHistoryView('graph');
      graphViewSeq.current += 1;
      setGraphViewRequest({ path: relativePath, seq: graphViewSeq.current });
    },
    [toggleVisible, visibility]
  );
  const clearGraphViewRequest = useCallback(() => setGraphViewRequest(null), []);

  // The revision-detail modal (History panel) asks RepoPanel's file-control
  // engine to roll files back to a revision; RepoPanel consumes the request.
  // Same pattern as highlight: make sure the Repository panel is visible, since
  // it owns the busy state and the result popup.
  const [rollbackRequest, setRollbackRequest] = useState(null);
  const rollbackSeq = useRef(0);
  const requestRollback = useCallback(
    (paths, revision) => {
      if (visibility && !visibility.repo) {
        toggleVisible?.('repo', true);
      }
      rollbackSeq.current += 1;
      setRollbackRequest({ paths, revision, seq: rollbackSeq.current });
    },
    [toggleVisible, visibility]
  );
  // A rollback is a one-shot action, not durable state. This provider lives for
  // the whole app lifetime (see note above), so a consumed request left sitting
  // in state would be re-read as "still pending" by a fresh RepoPanel mount
  // (whose per-mount seq guard resets to 0) every time you leave and return to
  // the Revision Control page — re-firing the rollback on its own, repeatedly.
  // RepoPanel clears it as soon as it has dispatched the rollback.
  const clearRollbackRequest = useCallback(() => setRollbackRequest(null), []);

  const value = useMemo(
    () => ({
      tab,
      setTab,
      repoSelection,
      setRepoSelection,
      workspaceSelection,
      setWorkspaceSelection,
      activeSelection: tab === 'repo' ? repoSelection : workspaceSelection,
      fileHashes,
      setFileHashes,
      hasPendingChanges,
      setHasPendingChanges,
      // Opens one of AppModals' modals by name. A plain passthrough of
      // AppShell's setActiveModal — no seq/one-shot bookkeeping, because
      // opening a modal is not a request another component consumes.
      openModal,
      highlightRequest,
      requestHighlightFiles,
      clearHighlightRequest,
      listChangesRequest,
      requestListChanges,
      clearListChangesRequest,
      rollbackRequest,
      requestRollback,
      clearRollbackRequest,
      historyView,
      setHistoryView,
      graphViewRequest,
      requestGraphView,
      clearGraphViewRequest,
    }),
    [tab, repoSelection, workspaceSelection, fileHashes, setFileHashes, hasPendingChanges, setHasPendingChanges, openModal, highlightRequest, requestHighlightFiles, clearHighlightRequest, listChangesRequest, requestListChanges, clearListChangesRequest, rollbackRequest, requestRollback, clearRollbackRequest, historyView, setHistoryView, graphViewRequest, requestGraphView, clearGraphViewRequest]
  );

  return <FileTreeTabContext.Provider value={value}>{children}</FileTreeTabContext.Provider>;
}

export function useFileTreeTab() {
  const ctx = useContext(FileTreeTabContext);
  if (!ctx) throw new Error('useFileTreeTab must be used within FileTreeTabProvider');
  return ctx;
}
