// 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 { CompactDropdown } from "../../../components/CompactDropdown.jsx";
import { Icon } from "../../../components/Icon.jsx";
import { useGroupedReorder } from "../../../hooks/useGroupedReorder.js";
import { useInlineEdit } from "../../../hooks/useInlineEdit.js";
import { lockBodyScroll } from "../../../utils/bodyScrollLock.js";
import { CalculatorModule } from "./CalculatorModule.jsx";
import { ChecklistModule } from "./ChecklistModule.jsx";
import { CombosModule } from "./CombosModule.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 { TableModule } from "./TableModule.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 },
  combos: { label: "Combos", icon: "controller", Component: CombosModule, editable: true, scrollable: true },
  calculator: { label: "Calculateur", icon: "calculator", Component: CalculatorModule, editable: false },
  table: { label: "Tableau", icon: "table", Component: TableModule, editable: false, scrollable: true },
  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 modules = Object.entries(TOOLBOX_MODULES);

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

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

  return (
    <>
      <div
        className="tool-add-controls"
        aria-label="Ajouter un outil"
      >
        <button
          className="primary tool-add-modal-button"
          type="button"
          onClick={() => setOpen(true)}
        >
          Ajouter un outil
        </button>
        <CompactDropdown
          className="tool-add-quick-dropdown"
          menuClassName="tool-quick-add"
          label="Ajout rapide"
          openOnHover
          closeOnMouseLeave
          renderTrigger={({ open: quickOpen, toggle }) => (
            <button
              className={`primary tool-add-quick-toggle ${quickOpen ? "active" : ""}`}
              type="button"
              onClick={toggle}
              aria-label="Afficher l'ajout rapide"
              aria-expanded={quickOpen}
              title="Ajout rapide"
            >
              <Icon name="dropdown" />
            </button>
          )}
        >
          {({ close }) => (
            <>
              {modules.map(([type, module]) => (
                <button
                  key={type}
                  className="tool-quick-add-button"
                  type="button"
                  onClick={() => {
                    onAdd(type);
                    close();
                  }}
                  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>
              ))}
            </>
          )}
        </CompactDropdown>
      </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 {
    itemReorder,
    getItemProps,
    isItemDragging,
    isItemDropTarget,
    getDropPlacement
  } = useGroupedReorder({
    namespace: "toolbox-modules",
    items: toolbox.modules,
    getItemId: (module) => module.id,
    getParentId: () => toolbox.id,
    onItemMove: (operation) => onMove(operation.sourceId, operation.targetId, operation.placement),
    hierarchy: { enabled: true, stickyParents: true }
  });

  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}
            reorder={{ itemReorder, getItemProps, isItemDragging, isItemDropTarget, getDropPlacement }}
            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}
              reorder={{ itemReorder, getItemProps, isItemDragging, isItemDropTarget, getDropPlacement }}
              onRename={onRename}
              onUpdateModule={onUpdateModule}
              onDelete={onDelete}
              onMove={onMove}
              registerModuleElement={registerModuleElement}
            />
          ))}
        </div>
      ))}
    </section>
  );
}

function ModuleShell({ toolbox, module, context, reorder, 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 className = [
    "module",
    reorder.isItemDragging(module.id) ? "is-dragging" : "",
    reorder.isItemDropTarget(module.id) ? "is-drop-target" : "",
    reorder.getDropPlacement("item", module.id) === "after" ? "drop-after" : "",
    scrollable ? "is-scrollable" : ""
  ].filter(Boolean).join(" ");

  return (
    <article
      className={className}
      {...reorder.getItemProps({ itemId: module.id, parentId: toolbox.id })}
      ref={(element) => registerModuleElement?.(module.id, element)}
    >
      <header>
        <div>
          <button
            className="module-drag-handle"
            type="button"
            onPointerDown={(event) => reorder.itemReorder.startDrag(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 }) {
  const inlineEdit = useInlineEdit({
    value,
    onCommit: (title) => onSave(title || fallback),
    blurOnEscape: true
  });

  return (
    <h2
      {...inlineEdit.getContentEditableProps({
        className: "module-title",
        title: "Cliquer pour renommer"
      })}
    >
      {value}
    </h2>
  );
}
