import React, { useMemo } from 'react';
import { useApp } from '../context/AppContext';
import { NODE_TYPES } from '../utils/fileUtils';
import './Sidebar.css';

function pathHash32(path) {
  let h = 0;
  for (let i = 0; i < path.length; i += 1) {
    h = Math.imul(31, h) + path.charCodeAt(i);
  }
  return h >>> 0;
}

/** 可见顺序：红→橙→黄→绿→青→蓝→紫→浅黑→浅白，循环；均为浅色粉彩 */
const FOLDER_PALETTE_LIGHT = [
  {
    bar: 'hsl(355 58% 64%)',
    bg: 'hsl(355 48% 97.4%)',
    border: 'hsl(355 38% 90%)',
    iconTop: 'hsl(355 62% 88%)',
    iconBottom: 'hsl(355 58% 72%)',
    wash: 'hsl(355 42% 95%)',
    washStrong: 'hsl(355 38% 92%)',
    muted: 'hsl(350 12% 46%)',
  },
  {
    bar: 'hsl(28 78% 62%)',
    bg: 'hsl(32 62% 97.2%)',
    border: 'hsl(30 48% 89%)',
    iconTop: 'hsl(32 72% 87%)',
    iconBottom: 'hsl(28 75% 70%)',
    wash: 'hsl(32 50% 94.5%)',
    washStrong: 'hsl(30 45% 91.5%)',
    muted: 'hsl(28 14% 45%)',
  },
  {
    bar: 'hsl(48 72% 58%)',
    bg: 'hsl(50 58% 97.6%)',
    border: 'hsl(48 42% 89%)',
    iconTop: 'hsl(52 68% 88%)',
    iconBottom: 'hsl(48 70% 68%)',
    wash: 'hsl(50 48% 95%)',
    washStrong: 'hsl(48 40% 92%)',
    muted: 'hsl(45 12% 44%)',
  },
  {
    bar: 'hsl(145 52% 52%)',
    bg: 'hsl(142 45% 97.3%)',
    border: 'hsl(145 36% 88%)',
    iconTop: 'hsl(142 48% 86%)',
    iconBottom: 'hsl(145 50% 66%)',
    wash: 'hsl(142 38% 94.5%)',
    washStrong: 'hsl(145 32% 91%)',
    muted: 'hsl(145 10% 43%)',
  },
  {
    bar: 'hsl(178 55% 48%)',
    bg: 'hsl(175 42% 97.4%)',
    border: 'hsl(178 34% 88%)',
    iconTop: 'hsl(175 46% 85%)',
    iconBottom: 'hsl(178 50% 62%)',
    wash: 'hsl(175 36% 94.5%)',
    washStrong: 'hsl(178 30% 91%)',
    muted: 'hsl(178 10% 42%)',
  },
  {
    bar: 'hsl(218 62% 62%)',
    bg: 'hsl(215 50% 97.5%)',
    border: 'hsl(218 40% 89%)',
    iconTop: 'hsl(215 58% 87%)',
    iconBottom: 'hsl(218 62% 68%)',
    wash: 'hsl(215 42% 95%)',
    washStrong: 'hsl(218 36% 92%)',
    muted: 'hsl(218 12% 44%)',
  },
  {
    bar: 'hsl(268 52% 62%)',
    bg: 'hsl(265 45% 97.6%)',
    border: 'hsl(268 36% 90%)',
    iconTop: 'hsl(265 50% 88%)',
    iconBottom: 'hsl(268 55% 70%)',
    wash: 'hsl(265 38% 95%)',
    washStrong: 'hsl(268 32% 92%)',
    muted: 'hsl(265 12% 44%)',
  },
  {
    bar: 'hsl(220 14% 46%)',
    bg: 'hsl(220 10% 95.8%)',
    border: 'hsl(220 8% 87%)',
    iconTop: 'hsl(220 12% 84%)',
    iconBottom: 'hsl(220 14% 60%)',
    wash: 'hsl(220 8% 93%)',
    washStrong: 'hsl(220 6% 90%)',
    muted: 'hsl(220 10% 42%)',
  },
  {
    bar: 'hsl(210 12% 74%)',
    bg: 'hsl(42 28% 98.4%)',
    border: 'hsl(40 18% 91%)',
    iconTop: 'hsl(45 22% 94%)',
    iconBottom: 'hsl(210 10% 78%)',
    wash: 'hsl(42 22% 96.2%)',
    washStrong: 'hsl(40 18% 94%)',
    muted: 'hsl(220 8% 46%)',
  },
];

const PALETTE_LEN = FOLDER_PALETTE_LIGHT.length;

function folderThemeForSlot(slot) {
  return FOLDER_PALETTE_LIGHT[((slot % PALETTE_LEN) + PALETTE_LEN) % PALETTE_LEN];
}

/**
 * 按侧边栏当前可见顺序（深度优先：列出文件夹 → 若展开则进入子级）分配 0,1,2… 再对 9 取模。
 */
function buildVisibleFolderSlotMap(nodes) {
  const map = new Map();
  let seq = 0;
  function walk(list) {
    if (!list) return;
    for (const n of list) {
      if (n.type !== NODE_TYPES.FOLDER) continue;
      map.set(n.path, seq % PALETTE_LEN);
      seq += 1;
      if (n.isExpanded && n.children?.length) {
        walk(n.children);
      }
    }
  }
  walk(nodes);
  return map;
}

function folderGradientId(path) {
  return `fol-${pathHash32(path).toString(36)}`;
}

/** 填充式文件夹 SVG，渐变填充；展开时使用略开的轮廓 */
function FolderGlyph({ path, expanded }) {
  const gid = folderGradientId(path);
  /* Material Design Icons 风格，viewBox 0 0 24 24 */
  const closedPath =
    'M10 4H4C2.89 4 2 4.89 2 6v12a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V8c0-1.11-.9-2-2-2h-8l-2-2z';
  const openPath =
    'M19 20H4C2.89 20 2 19.1 2 18V8C2 6.89 2.89 6 4 6H10L12 4H19A2 2 0 0 1 21 6V18C21 19.1 20.1 20 19 20Z';

  return (
    <svg
      className="folder-glyph-svg"
      width="20"
      height="16"
      viewBox="0 0 24 24"
      aria-hidden
    >
      <defs>
        <linearGradient id={gid} x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor="var(--folder-icon-top)" />
          <stop offset="100%" stopColor="var(--folder-icon-bottom)" />
        </linearGradient>
      </defs>
      <path fill={`url(#${gid})`} d={expanded ? openPath : closedPath} />
    </svg>
  );
}

function FileGlyph() {
  return (
    <svg className="file-glyph-svg" width="18" height="18" viewBox="0 0 24 24" aria-hidden>
      <path
        fill="none"
        stroke="currentColor"
        strokeWidth="1.6"
        strokeLinejoin="round"
        d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8l-6-6z"
      />
      <path
        fill="none"
        stroke="currentColor"
        strokeWidth="1.6"
        strokeLinejoin="round"
        d="M14 2v6h6"
      />
    </svg>
  );
}

function TreeNode({ node, level = 0, folderSlotByPath }) {
  const { selectFile, toggleNodeExpansion, currentFile } = useApp();

  // 计算当前目录下的直接文章数量
  const articleCount = useMemo(() => {
    if (node.type !== NODE_TYPES.FOLDER || !node.children) {
      return 0;
    }
    return node.children.filter(child => 
      child.type === NODE_TYPES.FILE && child.name.endsWith('.md')
    ).length;
  }, [node]);

  const handleClick = () => {
    if (node.type === NODE_TYPES.FOLDER) {
      toggleNodeExpansion(node.path);
    } else {
      selectFile(node.path);
    }
  };

  const isSelected = currentFile === node.path;
  const hasChildren = node.children && node.children.length > 0;
  const paddingLeft = `${level * 10 + 10}px`;
  const contentClass =
    node.type === NODE_TYPES.FOLDER
      ? `tree-node-content tree-node-content--folder${isSelected ? ' selected' : ''}`
      : `tree-node-content tree-node-content--file${isSelected ? ' selected' : ''}`;

  const folderSlot =
    node.type === NODE_TYPES.FOLDER ? folderSlotByPath.get(node.path) ?? 0 : null;
  const folderTheme =
    node.type === NODE_TYPES.FOLDER ? folderThemeForSlot(folderSlot) : null;
  const folderShellStyle =
    folderTheme &&
    ({
      '--folder-bar': folderTheme.bar,
      '--folder-bg': folderTheme.bg,
      '--folder-border': folderTheme.border,
      '--folder-icon-top': folderTheme.iconTop,
      '--folder-icon-bottom': folderTheme.iconBottom,
      '--folder-wash': folderTheme.wash,
      '--folder-wash-strong': folderTheme.washStrong,
      '--folder-muted': folderTheme.muted,
    });

  const fileRow = (
    <div
      className={contentClass}
      style={{ paddingLeft }}
      onClick={handleClick}
      role="button"
      tabIndex={0}
      onKeyDown={(event) => {
        if (event.key === 'Enter' || event.key === ' ') {
          event.preventDefault();
          handleClick();
        }
      }}
    >
      {node.type === NODE_TYPES.FOLDER && (
        <span className="tree-node-icon-wrap tree-node-icon-wrap--folder" aria-hidden>
          <FolderGlyph path={node.path} expanded={Boolean(node.isExpanded && hasChildren)} />
        </span>
      )}
      {node.type === NODE_TYPES.FILE && (
        <span className="tree-node-icon-wrap tree-node-icon-wrap--file" aria-hidden>
          <FileGlyph />
        </span>
      )}
      <span className="tree-node-name">
        {node.type === NODE_TYPES.FILE && node.name.endsWith('.md')
          ? node.name.slice(0, -3)
          : node.name}
      </span>
      {node.type === NODE_TYPES.FOLDER && articleCount > 0 && (
        <span className="article-count-badge">[{articleCount}]</span>
      )}
    </div>
  );

  if (node.type === NODE_TYPES.FOLDER) {
    return (
      <div className="tree-node tree-node--folder">
        <div className="folder-shell" style={folderShellStyle}>
          {fileRow}
          {node.isExpanded && hasChildren && (
            <div className="tree-node-children">
              {node.children.map((child) => (
                <TreeNode
                  key={child.path}
                  node={child}
                  level={level + 1}
                  folderSlotByPath={folderSlotByPath}
                />
              ))}
            </div>
          )}
        </div>
      </div>
    );
  }

  return (
    <div className="tree-node tree-node--file">
      {fileRow}
    </div>
  );
}

export default function Sidebar() {
  const { directoryTree, isLoading, error, sidebarOpen, toggleSidebar } = useApp();

  const folderSlotByPath = useMemo(
    () => buildVisibleFolderSlotMap(directoryTree),
    [directoryTree]
  );

  return (
    <>
      <aside className={sidebarOpen ? 'sidebar open' : 'sidebar closed'} aria-hidden={!sidebarOpen}>
        <div className="sidebar-header">
          <h2>
            <span className="sidebar-title-deco" aria-hidden>
              <span className="sidebar-title-deco-block sidebar-title-deco-block--g" />
              <span className="sidebar-title-deco-block sidebar-title-deco-block--r" />
              <span className="sidebar-title-deco-block sidebar-title-deco-block--b" />
            </span>
            文章目录
          </h2>
          <button
            type="button"
            onClick={toggleSidebar}
            className="toggle-button"
            aria-label={sidebarOpen ? '隐藏目录' : '展开目录'}
            title={sidebarOpen ? '隐藏目录' : '展开目录'}
          >
            {sidebarOpen ? '◀' : '▶'}
          </button>
        </div>

        <div className="sidebar-content">
          {isLoading && (
            <div className="loading">
              <div className="loading-spinner" aria-hidden />
              <span>加载中...</span>
            </div>
          )}

          {error && (
            <div className="error">
              <span>目录加载失败: {error}</span>
            </div>
          )}

          {!isLoading && !error && directoryTree.length > 0 && (
            <div className="directory-tree">
              {directoryTree.map((node) => (
                <TreeNode key={node.path} node={node} folderSlotByPath={folderSlotByPath} />
              ))}
            </div>
          )}

          {!isLoading && !error && directoryTree.length === 0 && (
            <div className="sidebar-empty-hint">
              <p>暂无文章目录</p>
              <p className="sidebar-empty-detail">
                接口已返回空列表。请在后端确认笔记目录已挂载且路径正确（例如将整个
                <code>data</code>
                挂到容器的
                <code>/app/mengyanote</code>
                ，笔记应在
                <code>mengyanote/mengyanote/</code>
                下），或查看
                <code>/api/health</code>
                中的
                <code>markdown_root</code>
                与
                <code>markdown_root_entry_count</code>
                。
              </p>
            </div>
          )}
        </div>
      </aside>
    </>
  );
}
