// Rôle : registre des outils toolbox, rendu commun et contrôles d'ajout/réorganisation.
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
import { createPortal } from "react-dom";
import { Icon } from "../../../components/Icon.jsx";
import { usePointerReorder } from "../../../hooks/usePointerReorder.js";
import { lockBodyScroll } from "../../../utils/bodyScrollLock.js";
import { CalculatorModule } from "./CalculatorModule.jsx";
import { ChecklistModule } from "./ChecklistModule.jsx";
import { CountersModule } from "./CountersModule.jsx";
import { ImageAnnotationModule } from "./ImageAnnotationModule.jsx";
import { LinksModule } from "./LinksModule.jsx";
import { NotepadModule } from "./NotepadModule.jsx";
import { ImagesModule } from "./ImagesModule.jsx";
import { TaskPlannerModule } from "./TaskPlannerModule.jsx";
import { TimerModule } from "./TimerModule.jsx";

const MODULE_COMPONENTS = {
  notepad: { label: "Bloc notes", icon: "notepad", Component: NotepadModule, editable: false },
  checklist: { label: "Checklist", icon: "checklist", Component: ChecklistModule, editable: true, scrollable: true },
  images: { label: "Images", icon: "picture", Component: ImagesModule, editable: true, scrollable: true },
  links: { label: "Liens", icon: "link", Component: LinksModule, editable: true, scrollable: true },
  counters: { label: "Compteurs", icon: "abacus", Component: CountersModule, editable: true, scrollable: true },
  calculator: { label: "Calculateur", icon: "calculator", Component: CalculatorModule, editable: false },
  timer: { label: "Timer", icon: "clock", Component: TimerModule, editable: false },
  taskPlanner: { label: "Planificateur de tâches", icon: "tasklist", Component: TaskPlannerModule, editable: true, scrollable: true },
  imageAnnotation: { label: "Annotation d'images", icon: "map", Component: ImageAnnotationModule, editable: true }
};

const MODULE_COLUMN_GAP_PX = 16;

export const TOOLBOX_MODULES = Object.fromEntries(Object.entries(MODULE_COMPONENTS).map(([type, module]) => [
  type,
  { label: module.label, icon: module.icon }
]));

function getSequentialModuleColumns(modules, splitIndex) {
  return [
    modules.slice(0, splitIndex),
    modules.slice(splitIndex)
  ];
}

function getColumnHeight(heights) {
  if (!heights.length) return 0;
  return heights.reduce((total, height) => total + height, 0) + MODULE_COLUMN_GAP_PX * (heights.length - 1);
}

function chooseMeasuredSplitIndex(modules, moduleHeights, fallbackSplitIndex) {
  if (modules.length <= 1) return modules.length;
  if (modules.some((module) => !moduleHeights.get(module.id))) return fallbackSplitIndex;

  let bestSplitIndex = fallbackSplitIndex;
  let bestDiff = Number.POSITIVE_INFINITY;
  let bestLeftDominantSplitIndex = 0;
  let bestLeftDominantDiff = Number.POSITIVE_INFINITY;

  for (let splitIndex = 1; splitIndex < modules.length; splitIndex += 1) {
    const leftHeight = getColumnHeight(modules.slice(0, splitIndex).map((module) => moduleHeights.get(module.id)));
    const rightHeight = getColumnHeight(modules.slice(splitIndex).map((module) => moduleHeights.get(module.id)));
    const diff = Math.abs(leftHeight - rightHeight);

    if (leftHeight >= rightHeight && diff < bestLeftDominantDiff) {
      bestLeftDominantDiff = diff;
      bestLeftDominantSplitIndex = splitIndex;
    }

    if (diff < bestDiff) {
      bestDiff = diff;
      bestSplitIndex = splitIndex;
    }
  }

  return bestLeftDominantSplitIndex || bestSplitIndex;
}

export function AddToolControls({ onAdd }) {
  const [open, setOpen] = useState(false);
  const [quickOpen, setQuickOpen] = useState(false);
  const controlsRef = useRef(null);
  const modules = Object.entries(TOOLBOX_MODULES);

  useEffect(() => {
    if (!open) return undefined;
    return lockBodyScroll();
  }, [open]);

  useEffect(() => {
    if (!quickOpen) return undefined;

    function handlePointerDown(event) {
      if (!controlsRef.current?.contains(event.target)) setQuickOpen(false);
    }

    function handleKeyDown(event) {
      if (event.key === "Escape") setQuickOpen(false);
    }

    document.addEventListener("pointerdown", handlePointerDown);
    document.addEventListener("keydown", handleKeyDown);
    return () => {
      document.removeEventListener("pointerdown", handlePointerDown);
      document.removeEventListener("keydown", handleKeyDown);
    };
  }, [quickOpen]);

  function addTool(type) {
    onAdd(type);
    setOpen(false);
    setQuickOpen(false);
  }

  return (
    <>
      <div
        className="tool-add-controls"
        ref={controlsRef}
        onMouseLeave={() => setQuickOpen(false)}
        onFocusCapture={(event) => {
          if (event.target.closest(".tool-add-quick-toggle")) setQuickOpen(true);
        }}
        onBlurCapture={(event) => {
          if (!event.currentTarget.contains(event.relatedTarget)) setQuickOpen(false);
        }}
        aria-label="Ajouter un outil"
      >
        <button
          className="primary tool-add-modal-button"
          type="button"
          onClick={() => {
            setQuickOpen(false);
            setOpen(true);
          }}
        >
          Ajouter un outil
        </button>
        <button
          className={`primary tool-add-quick-toggle ${quickOpen ? "active" : ""}`}
          type="button"
          onMouseEnter={() => setQuickOpen(true)}
          onClick={() => setQuickOpen((value) => !value)}
          aria-label="Afficher l'ajout rapide"
          aria-expanded={quickOpen}
          title="Ajout rapide"
        >
          <Icon name="dropdown" />
        </button>
        <div className={`tool-quick-add ${quickOpen ? "is-open" : ""}`} aria-label="Ajout rapide">
          {modules.map(([type, module]) => (
            <button
              key={type}
              className="tool-quick-add-button"
              type="button"
              onClick={() => onAdd(type)}
              aria-label={`Ajouter ${module.label}`}
              title={module.label}
            >
              <span className="module-icon" aria-hidden="true">
                <span className={`module-icon-svg module-icon-${module.icon}`} />
              </span>
            </button>
          ))}
        </div>
      </div>
      {open && createPortal(
        <div className="tool-add-modal-root" role="dialog" aria-modal="true" aria-labelledby="tool-add-modal-title">
          <div className="confirm-backdrop" onClick={() => setOpen(false)} />
          <section className="confirm-modal tool-add-modal">
            <header>
              <h2 id="tool-add-modal-title">Ajouter un outil</h2>
              <button className="drawer-close-button" type="button" onClick={() => setOpen(false)} aria-label="Fermer" title="Fermer">
                <Icon name="close" />
              </button>
            </header>
            <div className="tool-add-grid">
              {modules.map(([type, module]) => (
                <button key={type} className="tool-add-card" type="button" onClick={() => addTool(type)}>
                  <span className="module-icon" aria-hidden="true">
                    <span className={`module-icon-svg module-icon-${module.icon}`} />
                  </span>
                  <span>{module.label}</span>
                </button>
              ))}
            </div>
          </section>
        </div>,
        document.body
      )}
    </>
  );
}

export function ToolboxModules({ toolbox, moduleColumns, context, onRename, onUpdateModule, onDelete, onMove }) {
  const [measuredSplitIndex, setMeasuredSplitIndex] = useState(() => Math.ceil(toolbox.modules.length / 2));
  const moduleElementsRef = useRef(new Map());
  const moduleIdSignature = useMemo(() => toolbox.modules.map((module) => module.id).join("|"), [toolbox.modules]);
  const {
    draggingId: draggingModuleId,
    dropTarget,
    startDrag: startModuleDrag
  } = usePointerReorder({
    targetSelector: ".module",
    getTargetId: (target) => target.dataset.moduleId,
    canDropOn: (target) => target.dataset.toolboxId === toolbox.id,
    onMove
  });

  useEffect(() => {
    setMeasuredSplitIndex(Math.ceil(toolbox.modules.length / 2));
  }, [toolbox.id, moduleIdSignature, toolbox.modules.length]);

  useLayoutEffect(() => {
    if (moduleColumns !== 2 || toolbox.modules.length <= 1) return undefined;

    let frameId = 0;
    function rebalanceColumns() {
      cancelAnimationFrame(frameId);
      frameId = requestAnimationFrame(() => {
        const moduleHeights = new Map();
        toolbox.modules.forEach((module) => {
          const element = moduleElementsRef.current.get(module.id);
          if (element) moduleHeights.set(module.id, element.getBoundingClientRect().height);
        });

        setMeasuredSplitIndex((currentSplitIndex) => {
          const fallbackSplitIndex = Math.min(Math.max(currentSplitIndex, 1), toolbox.modules.length - 1);
          const nextSplitIndex = chooseMeasuredSplitIndex(toolbox.modules, moduleHeights, fallbackSplitIndex);
          return nextSplitIndex === currentSplitIndex ? currentSplitIndex : nextSplitIndex;
        });
      });
    }

    const observer = new ResizeObserver(rebalanceColumns);

    toolbox.modules.forEach((module) => {
      const element = moduleElementsRef.current.get(module.id);
      if (element) observer.observe(element);
    });
    rebalanceColumns();

    return () => {
      cancelAnimationFrame(frameId);
      observer.disconnect();
    };
  }, [moduleColumns, toolbox.id, moduleIdSignature, toolbox.modules.length]);

  function registerModuleElement(moduleId, element) {
    if (element) {
      moduleElementsRef.current.set(moduleId, element);
    } else {
      moduleElementsRef.current.delete(moduleId);
    }
  }

  if (moduleColumns === 1) {
    return (
      <section className="modules" data-toolbox-id={toolbox.id} data-layout-cols={moduleColumns}>
        {toolbox.modules.map((module) => (
          <ModuleShell
            key={module.id}
            toolbox={toolbox}
            module={module}
            context={context}
            draggingModuleId={draggingModuleId}
            dropTarget={dropTarget}
            onDragStart={startModuleDrag}
            onRename={onRename}
            onUpdateModule={onUpdateModule}
            onDelete={onDelete}
            onMove={onMove}
            registerModuleElement={registerModuleElement}
          />
        ))}
      </section>
    );
  }

  const splitIndex = Math.min(Math.max(measuredSplitIndex, 1), Math.max(1, toolbox.modules.length - 1));
  const columns = getSequentialModuleColumns(toolbox.modules, splitIndex);

  return (
    <section className="modules" data-toolbox-id={toolbox.id} data-layout-cols={moduleColumns}>
      {columns.map((modules, index) => (
        <div className="module-column" key={index}>
          {modules.map((module) => (
            <ModuleShell
              key={module.id}
              toolbox={toolbox}
              module={module}
              context={context}
              draggingModuleId={draggingModuleId}
              dropTarget={dropTarget}
              onDragStart={startModuleDrag}
              onRename={onRename}
              onUpdateModule={onUpdateModule}
              onDelete={onDelete}
              onMove={onMove}
              registerModuleElement={registerModuleElement}
            />
          ))}
        </div>
      ))}
    </section>
  );
}

function ModuleShell({ toolbox, module, context, draggingModuleId, dropTarget, onDragStart, onRename, onUpdateModule, onDelete, registerModuleElement }) {
  const definition = MODULE_COMPONENTS[module.type] || MODULE_COMPONENTS.notepad;
  const Component = definition.Component;
  const label = definition.label || module.type;
  const [editing, setEditing] = useState(false);
  const scrollable = definition.scrollable && module.scrollable === true;
  const isDragging = draggingModuleId === module.id;
  const isDropTarget = dropTarget.id === module.id;
  const className = [
    "module",
    isDragging ? "is-dragging" : "",
    isDropTarget ? "is-drop-target" : "",
    isDropTarget && dropTarget.placement === "after" ? "drop-after" : "",
    scrollable ? "is-scrollable" : ""
  ].filter(Boolean).join(" ");

  return (
    <article
      className={className}
      data-toolbox-id={toolbox.id}
      data-module-id={module.id}
      ref={(element) => registerModuleElement?.(module.id, element)}
    >
      <header>
        <div>
          <button
            className="module-drag-handle"
            type="button"
            onPointerDown={(event) => onDragStart(event, module.id)}
            aria-label={`Déplacer ${module.title || label}`}
            title="Déplacer"
          >
            <Icon name="drag" />
          </button>
          <span className="module-icon" aria-hidden="true">
            <span className={`module-icon-svg module-icon-${definition.icon || "notepad"}`} />
          </span>
          <EditableModuleTitle value={module.title || label} fallback={label} onSave={(title) => onRename(module.id, title)} />
        </div>
        <div>
          {definition.scrollable && (
            <button
              className={`module-scroll-button ${scrollable ? "active" : ""}`}
              onClick={() => onUpdateModule(module.id, (currentModule) => {
                const nextModule = { ...currentModule };
                if (scrollable) {
                  delete nextModule.scrollable;
                } else {
                  nextModule.scrollable = true;
                }
                return nextModule;
              })}
              aria-label={`${scrollable ? "Désactiver" : "Activer"} le scroll de ${module.title || label}`}
              aria-pressed={scrollable}
              title={scrollable ? "Désactiver le scroll" : "Activer le scroll"}
            >
              <Icon name="scrollable" />
              <i aria-hidden="true" />
            </button>
          )}
          {definition.editable && (
            <button
              className={`module-edit-button ${editing ? "active" : ""}`}
              onClick={() => setEditing((value) => !value)}
              aria-label={`${editing ? "Masquer" : "Afficher"} l'ajout de ${module.title || label}`}
              aria-pressed={editing}
              title={editing ? "Masquer l'ajout" : "Ajouter"}
            >
              <Icon name="add" />
            </button>
          )}
          <button className="module-delete-button danger" onClick={() => onDelete(module.id)} aria-label={`Retirer ${module.title || label}`} title="Retirer">
            <span className="ui-icon ui-icon-trash" aria-hidden="true" />
          </button>
        </div>
      </header>
      <div className={`module-content ${scrollable ? "is-scrollable legacy-scrollbar" : ""}`}>
        <Component toolboxId={toolbox.id} moduleId={module.id} context={context} editing={editing} />
      </div>
    </article>
  );
}

function EditableModuleTitle({ value, fallback, onSave }) {
  return (
    <h2
      className="module-title"
      contentEditable
      suppressContentEditableWarning
      spellCheck="false"
      title="Cliquer pour renommer"
      onFocus={(event) => { event.currentTarget.dataset.previousTitle = event.currentTarget.textContent.trim(); }}
      onBlur={(event) => onSave(event.currentTarget.textContent.trim() || fallback)}
      onKeyDown={(event) => {
        if (event.key === "Enter") {
          event.preventDefault();
          event.currentTarget.blur();
        }
        if (event.key === "Escape") {
          event.preventDefault();
          event.currentTarget.textContent = event.currentTarget.dataset.previousTitle || value;
          event.currentTarget.blur();
        }
      }}
    >
      {value}
    </h2>
  );
}
