import React, { useState, useCallback, useRef, useEffect, useMemo } from 'react';
import type { CBNode } from '../../types';
import type { EditorStore } from '../../store/editorStore';

// ── Constants ────────────────────────────────────────────────────────────────

const TYPE_ICONS: Record<string, string> = {
  section:    '▣',
  container:  '⊞',
  column:     '▥',
  heading:    'H',
  text:       '¶',
  image:      '⬚',
  button:     '⬜',
  spacer:     '↕',
  divider:    '─',
  video:      '▶',
  maps:       '⊕',
  html:       '<>',
  icon:       '★',
  shortcode:  '[]',
  logo:       '◈',
  form:       '⊟',
  alert:      '⚑',
  counter:    '#',
  progress:   '▰',
  testimonial:'❝',
  accordion:  '≡',
};

// ── Helpers ──────────────────────────────────────────────────────────────────

function getLabel(node: CBNode, overrides: Record<string, string>): string {
  if (overrides[node.id]) return overrides[node.id];
  if (node.type === 'heading') {
    const c = node.content as { text?: string } | undefined;
    return c?.text?.replace(/<[^>]+>/g, '').slice(0, 26) || 'Heading';
  }
  if (node.type === 'text') {
    const c = node.content as { html?: string; text?: string } | undefined;
    const raw = (c?.html ?? '').replace(/<[^>]+>/g, '') ?? c?.text ?? '';
    return raw.trim().slice(0, 26) || 'Text';
  }
  if (node.type === 'button') {
    const c = node.content as { text?: string } | undefined;
    return c?.text?.slice(0, 26) || 'Button';
  }
  if (node.type === 'shortcode') {
    const c = node.content as { shortcode?: string } | undefined;
    return c?.shortcode?.slice(0, 22) || 'Shortcode';
  }
  return node.type.charAt(0).toUpperCase() + node.type.slice(1);
}

function collectAllIds(nodes: CBNode[]): string[] {
  return nodes.flatMap(n => [n.id, ...collectAllIds(n.children ?? [])]);
}

// Returns IDs of matching nodes AND all their ancestors (so paths stay visible)
function buildMatchSet(nodes: CBNode[], query: string, overrides: Record<string, string>): Set<string> {
  if (!query) return new Set();
  const q = query.toLowerCase();
  const matched = new Set<string>();

  function walk(node: CBNode): boolean {
    const label = getLabel(node, overrides).toLowerCase();
    const selfMatch = label.includes(q) || node.type.toLowerCase().includes(q);
    const childMatches = (node.children ?? []).some(c => walk(c));
    if (selfMatch || childMatches) {
      matched.add(node.id);
      return true;
    }
    return false;
  }
  nodes.forEach(walk);
  return matched;
}

function findNodeInTree(nodes: CBNode[], id: string): CBNode | null {
  for (const n of nodes) {
    if (n.id === id) return n;
    if (n.children) {
      const f = findNodeInTree(n.children, id);
      if (f) return f;
    }
  }
  return null;
}

// ── Types ─────────────────────────────────────────────────────────────────────

interface DragInfo { nodeId: string }
interface DropInfo { nodeId: string; position: 'before' | 'after' }
interface CtxState { x: number; y: number; nodeId: string }

// ── Icon micro-components ─────────────────────────────────────────────────────

const IcoEye = () => (
  <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
    <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/>
    <circle cx="12" cy="12" r="3"/>
  </svg>
);
const IcoEyeOff = () => (
  <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
    <path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19"/>
    <line x1="1" y1="1" x2="23" y2="23"/>
  </svg>
);
const IcoLockOn = () => (
  <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
    <rect x="3" y="11" width="18" height="11" rx="2"/>
    <path d="M7 11V7a5 5 0 0 1 10 0v4"/>
  </svg>
);
const IcoLockOff = () => (
  <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
    <rect x="3" y="11" width="18" height="11" rx="2"/>
    <path d="M7 11V7a5 5 0 0 1 9.9-1"/>
  </svg>
);
const IcoDots = () => (
  <svg width="10" height="10" viewBox="0 0 24 24" fill="currentColor">
    <circle cx="8"  cy="5"  r="2"/><circle cx="16" cy="5"  r="2"/>
    <circle cx="8"  cy="12" r="2"/><circle cx="16" cy="12" r="2"/>
    <circle cx="8"  cy="19" r="2"/><circle cx="16" cy="19" r="2"/>
  </svg>
);

// ── Context Menu ──────────────────────────────────────────────────────────────

function CtxMenu({ ctx, node, store, onRename, onClose }: {
  ctx: CtxState; node: CBNode; store: EditorStore;
  onRename: (id: string) => void; onClose: () => void;
}) {
  const ref = useRef<HTMLDivElement>(null);
  const isHidden = !!(node.hideOn?.desktop && node.hideOn?.tablet && node.hideOn?.mobile);
  const isLocked = !!node.locked;
  const [pos, setPos] = useState({ x: ctx.x, y: ctx.y });

  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const r  = el.getBoundingClientRect();
    setPos({
      x: Math.max(8, Math.min(ctx.x, window.innerWidth  - r.width  - 8)),
      y: Math.max(8, Math.min(ctx.y, window.innerHeight - r.height - 8)),
    });
  }, [ctx.x, ctx.y]);

  useEffect(() => {
    const onD = (e: MouseEvent)   => { if (!ref.current?.contains(e.target as Node)) onClose(); };
    const onK = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); };
    document.addEventListener('mousedown', onD);
    document.addEventListener('keydown',   onK);
    return () => {
      document.removeEventListener('mousedown', onD);
      document.removeEventListener('keydown',   onK);
    };
  }, [onClose]);

  const act = (fn: () => void) => () => { fn(); onClose(); };

  return (
    <div ref={ref} className="jalpi-lctx" style={{ left: pos.x, top: pos.y }}>
      <button className="jalpi-lctx__item" onClick={act(() => store.selectNode(node.id))}>
        <IcoEye /> Select
      </button>
      <button className="jalpi-lctx__item" onClick={act(() => onRename(node.id))}>
        <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
          <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
          <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
        </svg>
        Rename
      </button>
      <button className="jalpi-lctx__item" onClick={act(() => store.duplicateNode(node.id))}>
        <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
          <rect x="9" y="9" width="13" height="13" rx="2"/>
          <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
        </svg>
        Duplicate
      </button>
      <button className="jalpi-lctx__item" onClick={act(() => store.copyNode(node.id))}>
        <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
          <rect x="9" y="9" width="13" height="13" rx="2"/>
          <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
        </svg>
        Copy
      </button>

      <div className="jalpi-lctx__sep"/>

      <button className="jalpi-lctx__item" onClick={act(() => store.lockNode(node.id, !isLocked))}>
        {isLocked ? <IcoLockOff /> : <IcoLockOn />}
        {isLocked ? 'Unlock' : 'Lock'}
      </button>
      <button className="jalpi-lctx__item" onClick={act(() => store.toggleNodeVisibility(node.id))}>
        {isHidden ? <IcoEye /> : <IcoEyeOff />}
        {isHidden ? 'Show' : 'Hide'}
      </button>

      <div className="jalpi-lctx__sep"/>

      <button className="jalpi-lctx__item jalpi-lctx__item--danger" onClick={act(() => store.deleteNode(node.id))}>
        <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
          <polyline points="3 6 5 6 21 6"/>
          <path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/>
          <path d="M10 11v6M14 11v6"/>
        </svg>
        Delete
      </button>
    </div>
  );
}

// ── Layer Row ─────────────────────────────────────────────────────────────────

interface RowProps {
  node:           CBNode;
  depth:          number;
  store:          EditorStore;
  labelOverrides: Record<string, string>;
  onSetLabel:     (id: string, v: string) => void;
  collapsedIds:   Set<string>;
  onToggle:       (id: string) => void;
  searchActive:   boolean;
  matchSet:       Set<string>;
  dragInfo:       DragInfo | null;
  dropInfo:       DropInfo | null;
  onDragStart:    (nodeId: string) => void;
  onDragOver:     (e: React.DragEvent, nodeId: string, pos: 'before' | 'after') => void;
  onDragEnd:      () => void;
  onDrop:         (e: React.DragEvent, nodeId: string, pos: 'before' | 'after') => void;
  onCtxMenu:      (e: React.MouseEvent, nodeId: string) => void;
  editingId:      string | null;
  onStartEdit:    (id: string) => void;
  onEndEdit:      () => void;
}

function LayerRow({
  node, depth, store, labelOverrides, onSetLabel,
  collapsedIds, onToggle, searchActive, matchSet,
  dragInfo, dropInfo, onDragStart, onDragOver, onDragEnd, onDrop,
  onCtxMenu, editingId, onStartEdit, onEndEdit,
}: RowProps) {
  const { state } = store;
  const isSelected   = state.selectedId === node.id;
  const hasChildren  = (node.children?.length ?? 0) > 0;
  const isHidden     = !!(node.hideOn?.desktop && node.hideOn?.tablet && node.hideOn?.mobile);
  const isLocked     = !!node.locked;
  const isDragging   = dragInfo?.nodeId === node.id;
  const isDropBefore = dropInfo?.nodeId === node.id && dropInfo.position === 'before';
  const isDropAfter  = dropInfo?.nodeId === node.id && dropInfo.position === 'after';
  const isEditing    = editingId === node.id;
  const label        = getLabel(node, labelOverrides);

  if (searchActive && !matchSet.has(node.id)) return null;

  // When searching, force-expand to reveal matching children
  const isExpanded = searchActive ? true : !collapsedIds.has(node.id);

  const rowCls = [
    'jalpi-layer__row',
    isSelected   ? 'is-selected'    : '',
    isDragging   ? 'is-dragging'    : '',
    isDropBefore ? 'is-drop-before' : '',
    isDropAfter  ? 'is-drop-after'  : '',
    isHidden     ? 'is-hidden'      : '',
    isLocked     ? 'is-locked'      : '',
  ].filter(Boolean).join(' ');

  return (
    <>
      <div
        className={rowCls}
        style={{ paddingLeft: `${6 + depth * 14}px` }}
        onClick={(e) => { e.stopPropagation(); store.selectNode(node.id); }}
        onContextMenu={(e) => { e.preventDefault(); e.stopPropagation(); onCtxMenu(e, node.id); }}
        draggable={!isLocked}
        onDragStart={(e) => {
          if (isLocked) { e.preventDefault(); return; }
          e.stopPropagation();
          e.dataTransfer.effectAllowed = 'move';
          onDragStart(node.id);
        }}
        onDragOver={(e) => {
          e.stopPropagation();
          const r = e.currentTarget.getBoundingClientRect();
          onDragOver(e, node.id, e.clientY < r.top + r.height / 2 ? 'before' : 'after');
        }}
        onDrop={(e) => {
          e.stopPropagation();
          const r = e.currentTarget.getBoundingClientRect();
          onDrop(e, node.id, e.clientY < r.top + r.height / 2 ? 'before' : 'after');
        }}
        onDragEnd={(e) => { e.stopPropagation(); onDragEnd(); }}
      >
        {/* Drag handle (hidden for locked) */}
        <span className={`jalpi-layer__drag${isLocked ? ' is-disabled' : ''}`} aria-hidden="true">
          <IcoDots />
        </span>

        {/* Expand/collapse */}
        {hasChildren ? (
          <button
            className="jalpi-layer__expand"
            onClick={(e) => { e.stopPropagation(); onToggle(node.id); }}
            title={isExpanded ? 'Collapse' : 'Expand'}
          >
            <svg width="8" height="8" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round">
              {isExpanded
                ? <polyline points="6 9 12 15 18 9"/>
                : <polyline points="9 6 15 12 9 18"/>}
            </svg>
          </button>
        ) : (
          <span className="jalpi-layer__expand jalpi-layer__expand--spacer"/>
        )}

        {/* Type icon */}
        <span className="jalpi-layer__icon" title={node.type}>
          {TYPE_ICONS[node.type] ?? '◻'}
        </span>

        {/* Label */}
        {isEditing ? (
          <input
            autoFocus
            className="jalpi-layer__name-input"
            defaultValue={label}
            onBlur={(e) => { onSetLabel(node.id, e.target.value.trim() || label); onEndEdit(); }}
            onKeyDown={(e) => {
              if (e.key === 'Enter')  { onSetLabel(node.id, e.currentTarget.value.trim() || label); onEndEdit(); }
              if (e.key === 'Escape') { onEndEdit(); }
              e.stopPropagation();
            }}
            onClick={(e) => e.stopPropagation()}
          />
        ) : (
          <span
            className="jalpi-layer__name"
            title={label}
            onDoubleClick={(e) => { e.stopPropagation(); onStartEdit(node.id); }}
          >
            {label}
          </span>
        )}

        {/* Quick-action buttons */}
        <div className="jalpi-layer__quick">
          <button
            className={`jalpi-layer__qbtn${isHidden ? ' is-active' : ''}`}
            title={isHidden ? 'Show element' : 'Hide element'}
            onClick={(e) => { e.stopPropagation(); store.toggleNodeVisibility(node.id); }}
          >
            {isHidden ? <IcoEyeOff /> : <IcoEye />}
          </button>
          <button
            className={`jalpi-layer__qbtn${isLocked ? ' is-active is-locked' : ''}`}
            title={isLocked ? 'Unlock element' : 'Lock element'}
            onClick={(e) => { e.stopPropagation(); store.lockNode(node.id, !isLocked); }}
          >
            {isLocked ? <IcoLockOn /> : <IcoLockOff />}
          </button>
        </div>
      </div>

      {/* Children (recursive) */}
      {isExpanded && hasChildren && node.children!.map((child) => (
        <LayerRow
          key={child.id}
          node={child}
          depth={depth + 1}
          store={store}
          labelOverrides={labelOverrides}
          onSetLabel={onSetLabel}
          collapsedIds={collapsedIds}
          onToggle={onToggle}
          searchActive={searchActive}
          matchSet={matchSet}
          dragInfo={dragInfo}
          dropInfo={dropInfo}
          onDragStart={onDragStart}
          onDragOver={onDragOver}
          onDragEnd={onDragEnd}
          onDrop={onDrop}
          onCtxMenu={onCtxMenu}
          editingId={editingId}
          onStartEdit={onStartEdit}
          onEndEdit={onEndEdit}
        />
      ))}
    </>
  );
}

// ── Main Panel ────────────────────────────────────────────────────────────────

interface Props {
  store: EditorStore;
}

export function LayersPanel({ store }: Props) {
  const { state } = store;
  const sections  = state.pageData.sections;

  const [search,         setSearch]         = useState('');
  const [collapsedIds,   setCollapsedIds]   = useState<Set<string>>(new Set());
  const [labelOverrides, setLabelOverrides] = useState<Record<string, string>>({});
  const [editingId,      setEditingId]      = useState<string | null>(null);
  const [dragInfo,       setDragInfo]       = useState<DragInfo | null>(null);
  const [dropInfo,       setDropInfo]       = useState<DropInfo | null>(null);
  const [ctx,            setCtx]            = useState<CtxState | null>(null);
  const searchRef = useRef<HTMLInputElement>(null);

  const searchActive = search.trim().length > 0;
  const matchSet = useMemo(
    () => buildMatchSet(sections, search.trim(), labelOverrides),
    [sections, search, labelOverrides]
  );

  const collapseAll  = () => setCollapsedIds(new Set(collectAllIds(sections)));
  const expandAll    = () => setCollapsedIds(new Set());
  const toggleExpand = useCallback((id: string) => {
    setCollapsedIds(prev => {
      const next = new Set(prev);
      next.has(id) ? next.delete(id) : next.add(id);
      return next;
    });
  }, []);

  const setLabel = useCallback((id: string, v: string) => {
    setLabelOverrides(prev => ({ ...prev, [id]: v }));
  }, []);

  // ── Drag-and-drop ──

  const handleDragStart = useCallback((nodeId: string) => {
    setDragInfo({ nodeId });
  }, []);

  const handleDragOver = useCallback((e: React.DragEvent, nodeId: string, pos: 'before' | 'after') => {
    e.preventDefault();
    setDropInfo(prev =>
      prev?.nodeId === nodeId && prev.position === pos ? prev : { nodeId, position: pos }
    );
  }, []);

  const handleDrop = useCallback((e: React.DragEvent, nodeId: string, pos: 'before' | 'after') => {
    e.preventDefault();
    if (dragInfo && dragInfo.nodeId !== nodeId) {
      store.moveNodeTo(dragInfo.nodeId, nodeId, pos);
    }
    setDragInfo(null);
    setDropInfo(null);
  }, [dragInfo, store]);

  const handleDragEnd = useCallback(() => {
    setDragInfo(null);
    setDropInfo(null);
  }, []);

  const handleCtxMenu = useCallback((e: React.MouseEvent, nodeId: string) => {
    setCtx({ x: e.clientX, y: e.clientY, nodeId });
  }, []);

  const ctxNode = ctx ? findNodeInTree(sections, ctx.nodeId) : null;

  const rowProps = {
    store, labelOverrides, onSetLabel: setLabel,
    collapsedIds, onToggle: toggleExpand,
    searchActive, matchSet,
    dragInfo, dropInfo,
    onDragStart: handleDragStart,
    onDragOver:  handleDragOver,
    onDragEnd:   handleDragEnd,
    onDrop:      handleDrop,
    onCtxMenu:   handleCtxMenu,
    editingId,
    onStartEdit: setEditingId,
    onEndEdit:   () => setEditingId(null),
  };

  return (
    <div className="jalpi-layers" onDragOver={(e) => e.preventDefault()}>

      {/* ── Header ── */}
      <div className="jalpi-layers__header">
        {/* Search */}
        <div className="jalpi-layers__search">
          <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round">
            <circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/>
          </svg>
          <input
            ref={searchRef}
            type="text"
            className="jalpi-layers__search-input"
            placeholder="Search…"
            value={search}
            onChange={(e) => setSearch(e.target.value)}
            onKeyDown={(e) => { if (e.key === 'Escape') setSearch(''); }}
          />
          {search && (
            <button className="jalpi-layers__search-clear" onClick={() => setSearch('')} title="Clear">
              <svg width="9" height="9" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round">
                <path d="M18 6L6 18M6 6l12 12"/>
              </svg>
            </button>
          )}
        </div>

        {/* Controls */}
        <div className="jalpi-layers__controls">
          <button className="jalpi-layers__ctrl" onClick={collapseAll} title="Collapse all">
            <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
              <line x1="3" y1="5"  x2="21" y2="5"/>
              <line x1="3" y1="12" x2="14" y2="12"/>
              <line x1="3" y1="19" x2="8"  y2="19"/>
            </svg>
          </button>
          <button className="jalpi-layers__ctrl" onClick={expandAll} title="Expand all">
            <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
              <line x1="3" y1="5"  x2="8"  y2="5"/>
              <line x1="3" y1="12" x2="14" y2="12"/>
              <line x1="3" y1="19" x2="21" y2="19"/>
            </svg>
          </button>
          <button className="jalpi-layers__add-btn" onClick={() => store.addSection()} title="Add section">
            +
          </button>
        </div>
      </div>

      {/* ── Layer list ── */}
      <div className="jalpi-layers__list">
        {sections.length === 0 && (
          <div className="jalpi-layers__empty">
            <p>No sections yet.</p>
            <button className="jalpi-btn jalpi-btn--accent-sm" onClick={() => store.addSection()}>
              Add Section
            </button>
          </div>
        )}
        {sections.length > 0 && searchActive && matchSet.size === 0 && (
          <div className="jalpi-layers__empty">
            <p>No results for "{search}"</p>
          </div>
        )}
        {sections.map((section) => (
          <LayerRow key={section.id} node={section} depth={0} {...rowProps} />
        ))}
      </div>

      {/* ── Context menu ── */}
      {ctx && ctxNode && (
        <CtxMenu
          ctx={ctx}
          node={ctxNode}
          store={store}
          onRename={(id) => { setEditingId(id); setCtx(null); }}
          onClose={() => setCtx(null)}
        />
      )}
    </div>
  );
}
