import React, { useEffect, useState } from "react";
import { Icon } from "../../components/atoms/Icon";
import { LanguageSelector } from "./LanguageSelector";
import { ProductSwitcher } from "./ProductSwitcher";
import { ThemeToggle } from "./ThemeToggle";
import { VERSION_IDS } from "../../config/versions";

// GitHub Stars Component
function GitHubStars() {
  const [stars, setStars] = useState<number | null>(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const fetchStars = async () => {
      try {
        const response = await fetch(
          "https://api.github.com/repos/decocms/studio",
        );
        if (response.ok) {
          const data = await response.json();
          setStars(data.stargazers_count);
        }
      } catch (error) {
        console.error("Failed to fetch GitHub stars:", error);
      } finally {
        setLoading(false);
      }
    };

    fetchStars();
  }, []);

  if (loading) {
    return (
      <div className="flex items-center gap-1 text-star">
        <Icon name="Star" size={14} />
        <span className="text-xs">...</span>
      </div>
    );
  }

  if (stars === null) {
    return null;
  }

  return (
    <div className="flex items-center gap-1 text-star">
      <Icon name="Star" size={14} />
      <span className="text-xs">{stars.toLocaleString()}</span>
    </div>
  );
}

interface DocData {
  title?: string;
  icon?: string;
  [key: string]: unknown;
}

interface Doc {
  id?: string;
  data?: DocData;
  [key: string]: unknown;
}

interface FlatNode {
  name: string;
  type: "file" | "folder";
  doc?: Doc;
  path: string[];
  depth: number;
  id: string;
  hasChildren: boolean;
}

interface SidebarProps {
  tree: FlatNode[];
  locale: string;
  translations: Record<string, string>;
  currentVersion: string;
}

interface TreeItemProps {
  node: FlatNode;
  isVisible: boolean;
  isExpanded: boolean;
  onToggle: (folderId: string) => void;
  locale: string;
  translations: Record<string, string>;
  version: string;
  currentPath: string;
}

function TreeItem({
  node,
  isVisible,
  isExpanded,
  onToggle,
  locale,
  translations,
  version,
  currentPath,
}: TreeItemProps) {
  if (!isVisible) return null;

  const docId = node.doc?.id;
  // docId is now version/locale/path, skip first 2 parts
  const docPath = docId ? docId.split("/").slice(2).join("/") : null;
  const itemPath =
    node.type === "file"
      ? `/${version}/${locale}/${docPath ?? node.path.join("/")}`
      : null;

  // Active when currentPath matches — starts false (empty string) until after hydration
  const active = node.type === "file" && currentPath === itemPath;

  // href is the same path as itemPath (null for folders)
  const href = itemPath;

  // A node should be collapsible if it has children, regardless of whether it's a file or folder
  const isCollapsible = node.hasChildren;
  const isFolder = node.type === "folder";

  // Shared icon rendering logic
  const renderIcon = () => {
    if (isFolder || (isCollapsible && !node.doc?.data?.icon)) {
      return (
        <Icon
          name={
            node.id === "studio/self-hosting"
              ? "Database"
              : node.id === "studio/self-hosting/deploy"
                ? "Rocket"
                : node.id === "studio/decopilot"
                  ? "Cpu"
                  : node.id === "studio/api-reference" ||
                      node.id === "api-reference"
                    ? "Code"
                    : "Folder"
          }
          size={16}
          className={`shrink-0 ${active ? "text-primary" : ""}`}
        />
      );
    }
    if (node.doc?.data?.icon) {
      return (
        <Icon
          name={node.doc.data.icon}
          size={16}
          className={`shrink-0 ${active ? "text-primary" : ""}`}
        />
      );
    }
    return (
      <Icon
        name="FileText"
        size={16}
        className={`shrink-0 ${active ? "text-primary" : ""}`}
      />
    );
  };

  const sharedClasses = `flex items-center gap-3 px-3 py-2 rounded-lg text-sm transition-colors ${
    active
      ? "bg-primary/5 text-primary" // Active state
      : "text-muted-foreground hover:bg-muted hover:text-foreground"
  }`;

  return (
    <li>
      {isCollapsible ? (
        <button
          type="button"
          aria-expanded={isExpanded}
          onClick={() => onToggle(node.id)}
          className={`${sharedClasses} w-full text-left cursor-pointer`}
        >
          {/* Indentation spacer for nested items */}
          {node.depth > 0 && (
            <div
              className="shrink-0"
              style={{ width: `${node.depth * 24}px` }}
            />
          )}

          {/* Icon */}
          {renderIcon()}

          {/* Content */}
          <span className="flex-1">
            {node.doc?.data?.title ||
              translations[`sidebar.section.${node.name}`] ||
              node.name}
          </span>
          <Icon
            name={isExpanded ? "ChevronDown" : "ChevronRight"}
            size={16}
            className={`shrink-0 ${active ? "text-primary" : ""}`}
          />
        </button>
      ) : (
        <a
          href={href ?? `/${locale}/${node.path.join("/")}`}
          className={sharedClasses}
        >
          {/* Indentation spacer for nested items */}
          {node.depth > 0 && (
            <div
              className="shrink-0"
              style={{ width: `${node.depth * 24}px` }}
            />
          )}

          {/* Icon */}
          {renderIcon()}

          {/* Content */}
          <span className="flex-1">{node.doc?.data?.title || node.name}</span>
        </a>
      )}
    </li>
  );
}

interface TreeListProps {
  tree: FlatNode[];
  treeState: Map<string, boolean>;
  onToggle: (folderId: string) => void;
  locale: string;
  translations: Record<string, string>;
  version: string;
  currentPath: string;
}

function TreeList({
  tree,
  treeState,
  onToggle,
  locale,
  translations,
  version,
  currentPath,
}: TreeListProps) {
  const isNodeVisible = (node: FlatNode): boolean => {
    if (node.depth === 0) return true;

    // A node is visible only if ALL its ancestor folders are expanded.
    // (This fixes cases where a grandparent folder is collapsed but a child still shows.)
    for (let i = 1; i < node.path.length; i++) {
      const ancestorId = node.path.slice(0, i).join("/");
      if (treeState.get(ancestorId) === false) return false;
    }

    return true;
  };

  return (
    <ul className="space-y-0.5">
      {tree.map((node, index) => {
        const isVisible = isNodeVisible(node);
        const isExpanded = treeState.get(node.id) !== false;
        const prevNode = tree[index - 1];

        // Add separator logic:
        // 1. After "overview" file (before concepts and other content)
        // 2. Before "Legacy Admin" section
        let needsSeparator = false;

        // Check if previous node is "overview" file at root level
        if (
          prevNode &&
          prevNode.depth === 0 &&
          prevNode.type === "file" &&
          prevNode.name === "overview" &&
          node.depth === 0
        ) {
          needsSeparator = true;
        }

        // Add separator before Legacy Admin section
        if (node.depth === 0 && node.id === "admin-decocms-com") {
          needsSeparator = true;
        }

        return (
          <React.Fragment key={node.id}>
            {needsSeparator && (
              <li className="my-3">
                <div className="h-px bg-border/50" />
              </li>
            )}
            <TreeItem
              node={node}
              isVisible={isVisible}
              isExpanded={isExpanded}
              onToggle={onToggle}
              locale={locale}
              translations={translations}
              version={version}
              currentPath={currentPath}
            />
          </React.Fragment>
        );
      })}
    </ul>
  );
}

// No filtering needed - separate content folders per version

export default function Sidebar({
  tree,
  locale,
  translations,
  currentVersion,
}: SidebarProps) {
  // Version state for URL-based navigation
  const [version, setVersion] = useState(currentVersion);

  // Current path state — starts empty (avoids SSR mismatch), set after hydration
  // and updated on every Astro client-side navigation via astro:page-load
  const [currentPath, setCurrentPath] = useState("");

  useEffect(() => {
    setCurrentPath(window.location.pathname);

    const getScrollContainer = (): HTMLElement | null => {
      const sidebar = document.getElementById("sidebar");
      return sidebar
        ? (sidebar.querySelector(".flex-1.overflow-y-auto") as HTMLElement)
        : null;
    };

    const SCROLL_KEY = "sidebar-scroll-top";

    // Restore scroll position on mount (survives remounts via sessionStorage).
    requestAnimationFrame(() => {
      const container = getScrollContainer();
      if (!container) return;
      try {
        const saved = sessionStorage.getItem(SCROLL_KEY);
        if (saved !== null) container.scrollTop = Number(saved) || 0;
      } catch {
        /* ignore */
      }
    });

    const handleBeforeSwap = () => {
      const container = getScrollContainer();
      if (!container) return;
      try {
        sessionStorage.setItem(SCROLL_KEY, String(container.scrollTop));
      } catch {
        /* ignore */
      }
    };

    const handlePageLoad = () => {
      const path = window.location.pathname;
      setCurrentPath(path);
      // Keep version in sync with the URL (e.g. after browser back/forward)
      const urlVersion = path.split("/")[1];
      if (VERSION_IDS.includes(urlVersion)) {
        setVersion(urlVersion);
      }
      // Restore scroll after React finishes re-rendering (rAF fires after paint)
      requestAnimationFrame(() => {
        const container = getScrollContainer();
        if (!container) return;
        try {
          const saved = sessionStorage.getItem(SCROLL_KEY);
          if (saved !== null) container.scrollTop = Number(saved) || 0;
        } catch {
          /* ignore */
        }
      });
    };

    document.addEventListener("astro:before-swap", handleBeforeSwap);
    document.addEventListener("astro:page-load", handlePageLoad);
    return () => {
      document.removeEventListener("astro:before-swap", handleBeforeSwap);
      document.removeEventListener("astro:page-load", handlePageLoad);
    };
  }, []);

  // Initialize with default state (same on server and client for hydration match)
  const [treeState, setTreeState] = useState<Map<string, boolean>>(() => {
    const initialState = new Map();

    // Default: most sections expanded, some collapsed
    const collapsedByDefault = new Set<string>([
      "admin-decocms-com/getting-started",
      "admin-decocms-com/no-code-guides",
      "admin-decocms-com/full-code-guides",
    ]);

    tree.forEach((node) => {
      if (node.hasChildren) {
        initialState.set(node.id, !collapsedByDefault.has(node.id));
      }
    });

    return initialState;
  });

  // After hydration (and on each navigation), apply localStorage state and expand ancestors
  useEffect(() => {
    if (!currentPath) return;
    try {
      if (typeof window !== "undefined" && window.localStorage) {
        const savedState = JSON.parse(
          window.localStorage.getItem("sidebar-tree-state") || "{}",
        );

        // currentPath looks like `/<version>/<locale>/studio/.../page`.
        // Tree node IDs are anchored at `studio/...`, so strip both prefixes
        // before computing ancestor IDs.
        const parts = currentPath.split("/").filter(Boolean);
        const localeIdx = parts.indexOf(locale);
        const slugParts = localeIdx >= 0 ? parts.slice(localeIdx + 1) : parts;
        const expandedAncestors = new Set<string>();
        for (let i = 1; i <= slugParts.length - 1; i++) {
          expandedAncestors.add(slugParts.slice(0, i).join("/"));
        }

        // Build new state with saved values and expanded ancestors
        const newState = new Map();
        const collapsedByDefault = new Set<string>([
          "admin-decocms-com/getting-started",
          "admin-decocms-com/no-code-guides",
          "admin-decocms-com/full-code-guides",
        ]);

        tree.forEach((node) => {
          if (node.hasChildren) {
            const saved = savedState[node.id];
            const defaultExpanded = !collapsedByDefault.has(node.id);
            const shouldExpand =
              typeof saved === "boolean" ? saved : defaultExpanded;

            newState.set(
              node.id,
              expandedAncestors.has(node.id) ? true : shouldExpand,
            );
          }
        });

        setTreeState(newState);
      }
    } catch (error) {
      console.error("Failed to load sidebar state:", error);
    }
  }, [tree, locale, currentPath]);

  const updateFolderVisibility = (folderId: string, isExpanded: boolean) => {
    setTreeState((prev) => {
      const newState = new Map(prev);
      newState.set(folderId, isExpanded);
      return newState;
    });

    // Save state to localStorage (client-side only)
    try {
      if (typeof window !== "undefined" && window.localStorage) {
        const stateToSave: Record<string, boolean> = {};
        treeState.forEach((value, key) => {
          stateToSave[key] = value;
        });
        stateToSave[folderId] = isExpanded;
        window.localStorage.setItem(
          "sidebar-tree-state",
          JSON.stringify(stateToSave),
        );
      }
    } catch (error) {
      // Ignore localStorage errors
      console.error("Failed to save sidebar state to localStorage:", error);
    }
  };

  const handleFolderToggle = (folderId: string) => {
    const currentState = treeState.get(folderId) || false;
    updateFolderVisibility(folderId, !currentState);
  };

  return (
    <div className="flex flex-col h-screen bg-app-background border-r border-border w-[19rem] lg:w-[19rem] w-full max-w-[19rem]">
      {/* Header - hidden on mobile */}
      <div className="hidden lg:flex items-center justify-between px-4 lg:px-6 py-3 shrink-0 border-b border-border">
        <ProductSwitcher />
        <div className="flex items-center gap-1.5">
          <LanguageSelector locale={locale} compact />
          <ThemeToggle />
        </div>
      </div>

      {/* Content */}
      <div className="flex-1 overflow-y-auto px-4 lg:px-8 py-4 min-h-0">
        <TreeList
          tree={tree}
          treeState={treeState}
          onToggle={handleFolderToggle}
          locale={locale}
          translations={translations}
          version={version}
          currentPath={currentPath}
        />
      </div>

      {/* Footer */}
      <div className="px-4 lg:px-8 py-4 border-t border-border shrink-0">
        <div className="space-y-2">
          <a
            href="https://github.com/decocms/studio"
            target="_blank"
            rel="noopener noreferrer"
            className="flex items-center gap-3 px-3 py-2 rounded-lg text-sm text-foreground hover:bg-muted hover:text-foreground transition-colors"
          >
            <Icon name="Github" size={16} className="text-muted-foreground" />
            <span className="flex-1">GitHub</span>
            <GitHubStars />
          </a>
          <a
            href="https://discord.gg/deco-cx"
            className="flex items-center gap-3 px-3 py-2 rounded-lg text-sm text-foreground hover:bg-muted hover:text-foreground transition-colors"
          >
            <Icon
              name="MessageCircle"
              size={16}
              className="text-muted-foreground"
            />
            <span className="flex-1">Discord community</span>
            <Icon
              name="ArrowUpRight"
              size={16}
              className="text-muted-foreground"
            />
          </a>
        </div>
      </div>
    </div>
  );
}
