import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useNavigate, useParams, useSearchParams } from "react-router";
import { Masthead } from "../components/Masthead";
import { useDirective, useDirectives } from "../hooks/useDirectives";
import { useAuth } from "../contexts/AuthContext";
import { DocumentEditor } from "../components/directives/DocumentEditor";
import { DocumentTaskStream } from "../components/directives/DocumentTaskStream";
import type {
  DirectiveStatus,
  DirectiveSummary,
  DirectiveWithSteps,
} from "../lib/api";

// Status dot color, matching the existing tabular UI's badge palette so the
// document mode feels like a sibling of the existing list, not a foreign UI.
const STATUS_DOT: Record<DirectiveStatus, string> = {
  draft: "bg-[#556677]",
  active: "bg-green-400",
  idle: "bg-yellow-400",
  paused: "bg-orange-400",
  archived: "bg-[#3a4a6a]",
};

// Per-task dot color for the sidebar entries inside a directive folder.
// Matches the StepsBlockNode palette.
const STEP_STATUS_DOT: Record<string, string> = {
  pending: "bg-[#556677]",
  ready: "bg-[#9bc3ff]",
  running: "bg-yellow-400",
  done: "bg-green-400",
  failed: "bg-red-400",
  skipped: "bg-[#3a4a6a]",
};

// =============================================================================
// Sidebar icons (inline SVG, no new deps)
// =============================================================================

function FolderIcon({ open = false }: { open?: boolean }) {
  return (
    <svg
      viewBox="0 0 16 16"
      width={12}
      height={12}
      className="shrink-0"
      aria-hidden
    >
      {open ? (
        <path
          d="M1.5 3.5a1 1 0 0 1 1-1h3.382a1 1 0 0 1 .894.553l.448.894a1 1 0 0 0 .894.553H13.5a1 1 0 0 1 1 1V6H1.5V3.5z M1 6.5h13.382a.5.5 0 0 1 .49.598l-.9 5A.5.5 0 0 1 13.482 12.5H2.518a.5.5 0 0 1-.49-.402l-.9-5A.5.5 0 0 1 1.62 6.5H1z"
          fill="#75aafc"
          opacity="0.85"
        />
      ) : (
        <path
          d="M1.5 4a1 1 0 0 1 1-1h3.382a1 1 0 0 1 .894.553l.448.894a1 1 0 0 0 .894.553H13.5a1 1 0 0 1 1 1V12a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1V4z"
          fill="#75aafc"
          opacity="0.65"
        />
      )}
    </svg>
  );
}

function FileIcon() {
  return (
    <svg
      viewBox="0 0 16 16"
      width={12}
      height={12}
      className="shrink-0"
      aria-hidden
    >
      <path
        d="M3 1.5h6.293a1 1 0 0 1 .707.293l3 3A1 1 0 0 1 13.293 5.5H13V14a.5.5 0 0 1-.5.5h-9A.5.5 0 0 1 3 14V1.5z"
        fill="none"
        stroke="#9bc3ff"
        strokeWidth="1"
      />
      <path
        d="M9.5 1.5v3h3"
        fill="none"
        stroke="#9bc3ff"
        strokeWidth="1"
      />
    </svg>
  );
}

function PinIcon() {
  return (
    <svg
      viewBox="0 0 16 16"
      width={10}
      height={10}
      className="shrink-0"
      aria-hidden
    >
      <path
        d="M8 1.5l1.6 3.6 3.9.4-2.95 2.7.85 3.9L8 10.2 4.6 12.1l.85-3.9L2.5 5.5l3.9-.4z"
        fill="#75aafc"
        opacity="0.7"
      />
    </svg>
  );
}

function Caret({ open }: { open: boolean }) {
  return (
    <svg
      viewBox="0 0 8 8"
      width={8}
      height={8}
      className={`shrink-0 transition-transform ${open ? "rotate-90" : ""}`}
      aria-hidden
    >
      <path d="M2 1l4 3-4 3z" fill="#7788aa" />
    </svg>
  );
}

// =============================================================================
// Sidebar
// =============================================================================

function slugify(title: string, fallback: string): string {
  const slug = title
    .trim()
    .replace(/\s+/g, "-")
    .replace(/[^a-zA-Z0-9._-]/g, "")
    .toLowerCase();
  return slug.length > 0 ? slug : fallback;
}

interface SidebarSelection {
  directiveId: string;
  /** null = the directive's document; otherwise a task id (orchestrator/step). */
  taskId: string | null;
}

interface SidebarProps {
  directives: DirectiveSummary[];
  loading: boolean;
  selection: SidebarSelection | null;
  onSelect: (sel: SidebarSelection) => void;
}

/**
 * Per-directive folder. Renders the directive as a collapsible folder whose
 * children are the pinned document entry (always first) and the live task list
 * — orchestrator, completion, and any step tasks. We fetch the directive's
 * full step list lazily, only when the folder is expanded, to avoid a thundering
 * herd of GETs at page load.
 */
function DirectiveFolder({
  directive,
  open,
  onToggle,
  selection,
  onSelect,
}: {
  directive: DirectiveSummary;
  open: boolean;
  onToggle: () => void;
  selection: SidebarSelection | null;
  onSelect: (sel: SidebarSelection) => void;
}) {
  const dotColor = STATUS_DOT[directive.status] ?? STATUS_DOT.draft;
  const fileName = `${slugify(directive.title, directive.id.slice(0, 8))}.md`;

  // Lazy fetch full directive (with steps) only when folder is open.
  const { directive: detailed } = useDirective(open ? directive.id : undefined);

  const docSelected =
    selection?.directiveId === directive.id && selection.taskId === null;

  // Collect the tasks to surface in the folder body.
  const tasks = useMemo(() => collectTasks(detailed, directive), [detailed, directive]);

  return (
    <div className="select-none">
      <button
        type="button"
        onClick={onToggle}
        title={directive.title}
        className="w-full flex items-center gap-1.5 pl-3 pr-3 py-1 font-mono text-[11px] text-[#9bc3ff] hover:bg-[rgba(117,170,252,0.05)]"
      >
        <Caret open={open} />
        {/* Color icon LEFT — the user explicitly asked for an icon, not a /status text label. */}
        <span
          className={`inline-block w-2 h-2 rounded-full shrink-0 ${dotColor}`}
          aria-label={`status: ${directive.status}`}
          title={`status: ${directive.status}`}
        />
        <FolderIcon open={open} />
        <span className="truncate flex-1 text-left">{directive.title}</span>
        {/* And RIGHT — same dot, plus a pulsing one if the orchestrator is live. */}
        {!!directive.orchestratorTaskId && (
          <span
            className="inline-block w-1.5 h-1.5 rounded-full shrink-0 bg-yellow-400 animate-pulse"
            title="Orchestrator running"
            aria-label="Orchestrator running"
          />
        )}
        <span
          className={`inline-block w-2 h-2 rounded-full shrink-0 ${dotColor}`}
          aria-hidden
        />
      </button>

      {open && (
        <ul className="py-0.5">
          {/* Pinned document entry — always at the top of the folder. */}
          <li>
            <button
              type="button"
              onClick={() =>
                onSelect({ directiveId: directive.id, taskId: null })
              }
              className={`w-full text-left flex items-center gap-1.5 pl-8 pr-3 py-1 font-mono text-[11px] transition-colors ${
                docSelected
                  ? "bg-[rgba(117,170,252,0.12)] text-white border-l-2 border-[#75aafc]"
                  : "text-[#9bc3ff] hover:bg-[rgba(117,170,252,0.06)] border-l-2 border-transparent"
              }`}
            >
              <PinIcon />
              <FileIcon />
              <span className="truncate flex-1">{fileName}</span>
            </button>
          </li>

          {tasks.length === 0 ? (
            <li className="pl-10 pr-3 py-1 font-mono text-[10px] text-[#556677]">
              No tasks yet
            </li>
          ) : (
            tasks.map((t) => {
              const isSelected =
                selection?.directiveId === directive.id &&
                selection?.taskId === t.taskId;
              const tdot = STEP_STATUS_DOT[t.status] ?? STEP_STATUS_DOT.pending;
              const live =
                t.status === "running" || t.kind === "orchestrator-active";
              return (
                <li key={t.taskId}>
                  <button
                    type="button"
                    onClick={() =>
                      onSelect({
                        directiveId: directive.id,
                        taskId: t.taskId,
                      })
                    }
                    title={t.label}
                    className={`w-full text-left flex items-center gap-1.5 pl-10 pr-3 py-1 font-mono text-[11px] transition-colors ${
                      isSelected
                        ? "bg-[rgba(117,170,252,0.12)] text-white border-l-2 border-[#75aafc]"
                        : "text-[#9bc3ff] hover:bg-[rgba(117,170,252,0.06)] border-l-2 border-transparent"
                    }`}
                  >
                    <span
                      className={`inline-block w-1.5 h-1.5 rounded-full shrink-0 ${tdot}`}
                      aria-hidden
                    />
                    <span className="truncate flex-1">{t.label}</span>
                    {live && (
                      <span
                        className="inline-block w-1.5 h-1.5 rounded-full shrink-0 bg-yellow-400 animate-pulse"
                        aria-hidden
                      />
                    )}
                  </button>
                </li>
              );
            })
          )}
        </ul>
      )}
    </div>
  );
}

interface FolderTaskRow {
  taskId: string;
  label: string;
  status: string;
  kind: "orchestrator-active" | "completion" | "step";
}

function collectTasks(
  detailed: DirectiveWithSteps | null,
  summary: DirectiveSummary,
): FolderTaskRow[] {
  const rows: FolderTaskRow[] = [];

  // Orchestrator (planner) — surfaces only while it's actively running so
  // the folder is not flooded with stale orchestrator entries.
  const orchestratorId =
    detailed?.orchestratorTaskId ?? summary.orchestratorTaskId ?? null;
  if (orchestratorId) {
    rows.push({
      taskId: orchestratorId,
      label: "orchestrator",
      status: "running",
      kind: "orchestrator-active",
    });
  }

  // Completion (PR creation) task.
  const completionId =
    detailed?.completionTaskId ?? summary.completionTaskId ?? null;
  if (completionId) {
    rows.push({
      taskId: completionId,
      label: "completion",
      status: "running",
      kind: "completion",
    });
  }

  // Step tasks — only steps that have actually been started have a taskId.
  if (detailed) {
    for (const step of detailed.steps) {
      if (!step.taskId) continue;
      rows.push({
        taskId: step.taskId,
        label: step.name,
        status: step.status,
        kind: "step",
      });
    }
  }

  return rows;
}

interface SidebarProps {
  directives: DirectiveSummary[];
  loading: boolean;
  selection: SidebarSelection | null;
  onSelect: (sel: SidebarSelection) => void;
}

function DocumentSidebar({ directives, loading, selection, onSelect }: SidebarProps) {
  // Sort active first, then idle, then paused, then archived.
  const sorted = useMemo(() => {
    const order: Record<DirectiveStatus, number> = {
      active: 0,
      paused: 1,
      idle: 2,
      draft: 3,
      archived: 4,
    };
    return [...directives].sort((a, b) => {
      const oa = order[a.status] ?? 99;
      const ob = order[b.status] ?? 99;
      if (oa !== ob) return oa - ob;
      return a.title.localeCompare(b.title, undefined, { sensitivity: "base" });
    });
  }, [directives]);

  // Track which directive folders are open. The currently selected directive
  // is forced open so deep links land on something visible.
  const [openIds, setOpenIds] = useState<Set<string>>(new Set());
  const lastSelectedRef = useRef<string | null>(null);
  useEffect(() => {
    if (selection && selection.directiveId !== lastSelectedRef.current) {
      lastSelectedRef.current = selection.directiveId;
      setOpenIds((prev) => {
        if (prev.has(selection.directiveId)) return prev;
        const next = new Set(prev);
        next.add(selection.directiveId);
        return next;
      });
    }
  }, [selection]);

  const toggleOpen = useCallback((id: string) => {
    setOpenIds((prev) => {
      const next = new Set(prev);
      if (next.has(id)) next.delete(id);
      else next.add(id);
      return next;
    });
  }, []);

  return (
    <div className="flex flex-col h-full">
      {/* Sidebar header */}
      <div className="flex items-center justify-between px-3 py-2 border-b border-dashed border-[rgba(117,170,252,0.2)]">
        <span className="text-[11px] font-mono text-[#9bc3ff] uppercase tracking-wide">
          Documents
        </span>
        <span className="text-[10px] font-mono text-[#556677]">
          {directives.length}
        </span>
      </div>

      {/* Top-level "directives/" folder header (informational, non-interactive). */}
      <div className="flex items-center gap-1.5 px-3 py-1.5 text-[11px] font-mono text-[#9bc3ff]">
        <FolderIcon open />
        <span>directives/</span>
      </div>

      {/* Body */}
      <div className="flex-1 overflow-y-auto pb-4">
        {loading && directives.length === 0 ? (
          <div className="px-3 py-6 text-center text-[#556677] font-mono text-[11px]">
            Loading...
          </div>
        ) : directives.length === 0 ? (
          <div className="px-3 py-6 text-center text-[#556677] font-mono text-[11px]">
            No directives yet
          </div>
        ) : (
          sorted.map((d) => (
            <DirectiveFolder
              key={d.id}
              directive={d}
              open={openIds.has(d.id)}
              onToggle={() => toggleOpen(d.id)}
              selection={selection}
              onSelect={onSelect}
            />
          ))
        )}
      </div>
    </div>
  );
}

// =============================================================================
// Editor shell — wraps DocumentEditor and handles the "no document selected"
// and loading states.
// =============================================================================

interface EditorShellProps {
  selectedId: string | undefined;
  selectedTaskId: string | null;
  hasDirectives: boolean;
  listLoading: boolean;
  onClearTask: () => void;
}

function EditorShell({
  selectedId,
  selectedTaskId,
  hasDirectives,
  listLoading,
  onClearTask,
}: EditorShellProps) {
  const {
    directive,
    loading,
    updateGoal,
    cleanup,
    createPR,
    pickUpOrders,
  } = useDirective(selectedId);

  if (!selectedId) {
    return (
      <div className="flex-1 flex items-center justify-center h-full">
        <p className="text-[#556677] font-mono text-[12px]">
          {listLoading
            ? "Loading documents..."
            : hasDirectives
            ? "Select a document from the sidebar"
            : "No documents yet — create one from the legacy UI"}
        </p>
      </div>
    );
  }

  if (loading && !directive) {
    return (
      <div className="flex-1 flex items-center justify-center h-full">
        <p className="text-[#556677] font-mono text-[12px]">Loading document...</p>
      </div>
    );
  }

  if (!directive) {
    return (
      <div className="flex-1 flex items-center justify-center h-full">
        <p className="text-[#7788aa] font-mono text-[12px]">Document not found</p>
      </div>
    );
  }

  // Resolve the label for the breadcrumb when a task is selected.
  const taskLabel = selectedTaskId
    ? selectedTaskId === directive.orchestratorTaskId
      ? "orchestrator"
      : selectedTaskId === directive.completionTaskId
      ? "completion"
      : directive.steps.find((s) => s.taskId === selectedTaskId)?.name ??
        selectedTaskId.slice(0, 8)
    : null;

  return (
    <div className="flex-1 flex flex-col h-full overflow-hidden">
      {/* Document header — breadcrumb-like, mirrors a code editor's tab bar */}
      <div className="px-6 py-3 border-b border-dashed border-[rgba(117,170,252,0.2)]">
        <div className="flex items-center gap-2 text-[10px] font-mono uppercase tracking-wide text-[#7788aa]">
          <FileIcon />
          <span>directives /</span>
          <span className="text-[#9bc3ff]">{directive.id.slice(0, 8)}</span>
          {selectedTaskId && (
            <>
              <span>/</span>
              <span className="text-[#9bc3ff]">{taskLabel}</span>
              <button
                type="button"
                onClick={onClearTask}
                className="ml-2 px-1.5 py-0.5 text-[#7788aa] hover:text-white border border-[#2a3a5a] rounded normal-case"
              >
                back to document
              </button>
            </>
          )}
          {!selectedTaskId && !!directive.orchestratorTaskId && (
            <span className="ml-2 inline-flex items-center gap-1 text-yellow-400">
              <span className="inline-block w-1.5 h-1.5 rounded-full bg-yellow-400 animate-pulse" />
              orchestrator running
            </span>
          )}
        </div>
      </div>

      {selectedTaskId ? (
        <DocumentTaskStream
          taskId={selectedTaskId}
          label={taskLabel ?? selectedTaskId.slice(0, 8)}
        />
      ) : (
        <DocumentEditor
          directive={directive}
          onUpdateGoal={async (goal) => {
            await updateGoal(goal);
          }}
          onCleanup={async () => {
            await cleanup();
          }}
          onCreatePR={async () => {
            await createPR();
          }}
          onPickUpOrders={async () => {
            await pickUpOrders();
          }}
        />
      )}
    </div>
  );
}

// =============================================================================
// Page
// =============================================================================

export default function DocumentDirectivesPage() {
  const { isAuthenticated, isAuthConfigured, isLoading: authLoading } = useAuth();
  const navigate = useNavigate();
  const { id: selectedId } = useParams<{ id: string }>();
  const [searchParams, setSearchParams] = useSearchParams();
  const selectedTaskId = searchParams.get("task");
  const { directives, loading: listLoading } = useDirectives();

  useEffect(() => {
    if (!authLoading && isAuthConfigured && !isAuthenticated) {
      navigate("/login");
    }
  }, [authLoading, isAuthConfigured, isAuthenticated, navigate]);

  const onSelect = useCallback(
    (sel: SidebarSelection) => {
      const next = `/directives/${sel.directiveId}${
        sel.taskId ? `?task=${sel.taskId}` : ""
      }`;
      navigate(next);
    },
    [navigate],
  );

  const onClearTask = useCallback(() => {
    const next = new URLSearchParams(searchParams);
    next.delete("task");
    setSearchParams(next, { replace: true });
  }, [searchParams, setSearchParams]);

  if (authLoading) {
    return (
      <div className="relative z-10 min-h-screen flex flex-col bg-[#0a1628]">
        <Masthead showNav />
        <main className="flex-1 flex items-center justify-center">
          <p className="text-[#7788aa] font-mono text-sm">Loading...</p>
        </main>
      </div>
    );
  }

  const selection: SidebarSelection | null = selectedId
    ? { directiveId: selectedId, taskId: selectedTaskId }
    : null;

  return (
    <div className="relative z-10 min-h-screen flex flex-col bg-[#0a1628]">
      <Masthead showNav />
      <main
        className="flex-1 flex overflow-hidden"
        style={{ height: "calc(100vh - 80px)" }}
      >
        {/* Left: file-tree sidebar */}
        <div className="w-[260px] shrink-0 border-r border-dashed border-[rgba(117,170,252,0.2)] overflow-hidden flex flex-col bg-[#091428]">
          <DocumentSidebar
            directives={directives}
            loading={listLoading}
            selection={selection}
            onSelect={onSelect}
          />
        </div>

        {/* Right: Lexical editor / task stream */}
        <EditorShell
          selectedId={selectedId}
          selectedTaskId={selectedTaskId}
          hasDirectives={directives.length > 0}
          listLoading={listLoading}
          onClearTask={onClearTask}
        />
      </main>
    </div>
  );
}
