// Rôle : fournit l'outil calculateur avec résultats enregistrés en arborescence et échange texte.
import { useLayoutEffect, useMemo, useRef, useState } from "react";
import { Icon } from "../../../components/Icon.jsx";
import { useInlineEdit } from "../../../hooks/useInlineEdit.js";
import { moveItem, useGroupedReorder } from "../../../hooks/useGroupedReorder.js";
import { TextExchangeActions } from "./TextExchangeActions.jsx";

const DISPLAY_NUMBER_FORMATTER = new Intl.NumberFormat("fr-FR", { maximumFractionDigits: 6 });

function calculateExpression(expression) {
  const normalized = String(expression || "").replaceAll(",", ".").trim();
  if (!normalized || !/^[\d\s+\-*/().]+$/.test(normalized)) return null;

  try {
    const value = Function(`"use strict"; return (${normalized})`)();
    return Number.isFinite(value) ? value : null;
  } catch {
    return null;
  }
}

function formatResult(value) {
  if (!Number.isFinite(value)) return "";
  return Number.parseFloat(value.toFixed(6)).toString();
}

function formatDisplayResult(value) {
  if (!Number.isFinite(value)) return "";
  return DISPLAY_NUMBER_FORMATTER.format(Number.parseFloat(value.toFixed(6)));
}

function getResultLengthClass(value) {
  const length = formatResult(value).replace(/[^\d]/g, "").length;
  if (length >= 13) return "is-compact";
  if (length >= 9) return "is-long";
  return "";
}

function getEntryResultLengthClass(value) {
  const length = formatResult(value).replace(/[^\d]/g, "").length;
  if (length >= 21) return "is-compact";
  if (length >= 18) return "is-long";
  return "";
}

function getChildren(entries, parentId) {
  return entries.filter((entry) => (entry.parentId || "") === parentId);
}

function getDescendantIds(entries, parentId) {
  const children = getChildren(entries, parentId);
  return children.flatMap((child) => [child.id, ...getDescendantIds(entries, child.id)]);
}

function getEntriesInTreeOrder(entries, parentId = "") {
  return getChildren(entries, parentId).flatMap((entry) => [entry, ...getEntriesInTreeOrder(entries, entry.id)]);
}

function FormattedCalculatorNumber({ value }) {
  if (!Number.isFinite(value)) return "-";
  let integerGroupIndex = 0;
  const normalized = Number.parseFloat(value.toFixed(6));
  return DISPLAY_NUMBER_FORMATTER.formatToParts(normalized).map((part, index) => {
    if (part.type === "integer") {
      const className = `calculator-number-group ${integerGroupIndex % 2 ? "is-muted" : "is-strong"}`;
      integerGroupIndex += 1;
      return <span className={className} key={`${part.type}-${index}`}>{part.value}</span>;
    }
    if (part.type === "group") return <span className="calculator-number-separator" key={`${part.type}-${index}`}>{part.value}</span>;
    return <span key={`${part.type}-${index}`}>{part.value}</span>;
  });
}

export function CalculatorModule({ toolboxId, moduleId, context, editing }) {
  const data = context.normalizeCalculatorData(context.getModuleData(toolboxId, moduleId, { entries: [] }));
  const textContent = context.moduleText?.calculator || {};
  const [expression, setExpression] = useState("");
  const [label, setLabel] = useState("");
  const [activeParentId, setActiveParentId] = useState("");
  const [calculatorHeight, setCalculatorHeight] = useState(0);
  const [copied, setCopied] = useState(false);
  const calculatorCardRef = useRef(null);
  const result = useMemo(() => calculateExpression(expression), [expression]);
  const activeParent = data.entries.find((entry) => entry.id === activeParentId);
  const scopeParentId = (parentId = "") => `${moduleId}:${parentId}`;
  const reorder = useGroupedReorder({
    namespace: "calculator",
    items: data.entries,
    getItemId: (entry) => entry.id,
    getParentId: (entry) => scopeParentId(entry.parentId || ""),
    onItemMove: (operation) => {
      save(moveItem(data.entries, operation.sourceId, operation.targetId, operation.placement));
    },
    hierarchy: { enabled: true, stickyParents: true }
  });

  useLayoutEffect(() => {
    const element = calculatorCardRef.current;
    if (!element) return undefined;

    function updateHeight() {
      setCalculatorHeight(Math.ceil(element.getBoundingClientRect().height));
    }

    updateHeight();
    const observer = new ResizeObserver(updateHeight);
    observer.observe(element);
    return () => observer.disconnect();
  }, []);

  function save(entries) {
    context.setModuleData(toolboxId, moduleId, { ...data, entries });
  }

  function setScrollResults(scrollResults) {
    context.setModuleData(toolboxId, moduleId, { ...data, scrollResults });
  }

  function saveResult(event) {
    event.preventDefault();
    if (result == null) return;
    const cleanLabel = label.trim();

    save([
      ...data.entries,
      {
        id: context.uid("calc"),
        parentId: activeParentId,
        label: cleanLabel,
        value: result
      }
    ]);
    setExpression(activeParent ? formatResult(activeParent.value) : "");
    setLabel("");
  }

  function useEntry(entry) {
    setExpression(formatResult(entry.value));
    setActiveParentId(entry.id);
  }

  function deleteEntry(entryId) {
    const deletedIds = new Set([entryId, ...getDescendantIds(data.entries, entryId)]);
    if (deletedIds.has(activeParentId)) {
      setActiveParentId("");
      setExpression("");
    }
    save(data.entries.filter((entry) => !deletedIds.has(entry.id)));
  }

  function renameEntry(entryId, label) {
    const cleanLabel = label.trim();
    save(data.entries.map((entry) => entry.id === entryId ? { ...entry, label: cleanLabel } : entry));
  }

  function resetCalculator() {
    setExpression("");
    setLabel("");
    setActiveParentId("");
  }

  function returnToRoot() {
    setActiveParentId("");
    setExpression("");
  }

  async function copyChecklistImport() {
    const text = getEntriesInTreeOrder(data.entries)
      .map((entry) => `${entry.label || formatResult(entry.value)}:${formatResult(entry.value)}`)
      .join("\n");
    if (!text || !await context.copyText(text)) return;
    setCopied(true);
    context.notify?.(textContent.copiedTitle || "Copié");
    window.setTimeout(() => setCopied(false), 1400);
  }

  return (
    <div className="calculator-module">
      {editing && (
        <div className="module-add-panel calculator-exchange-panel">
          <TextExchangeActions type="calculator" data={data} context={context} textContent={textContent} onImport={(nextData) => context.setModuleData(toolboxId, moduleId, nextData, "calculator")} />
        </div>
      )}
      <form className="calculator-form" onSubmit={saveResult}>
        <div className="calculator-card" ref={calculatorCardRef}>
          <label>
            <span>{textContent.expressionLabel || "Calcul"}</span>
            <input value={expression} onChange={(event) => setExpression(event.target.value)} placeholder={textContent.expressionPlaceholder || "10*10"} inputMode="decimal" />
          </label>
          <div className={`calculator-result ${result == null ? "" : getResultLengthClass(result)}`} aria-live="polite">
            <span>{textContent.resultLabel || "Résultat"}</span>
            <strong aria-label={result == null ? "-" : formatDisplayResult(result)}>
              <FormattedCalculatorNumber value={result} />
            </strong>
          </div>
          <label>
            <span>{textContent.labelLabel || "Libellé"}</span>
            <input value={label} onChange={(event) => setLabel(event.target.value)} placeholder={textContent.labelPlaceholder || "Lingots de fer"} />
          </label>
          <button className="primary" disabled={result == null}>{textContent.saveButton || "Enregistrer"}</button>
        </div>
      </form>
      <div
        className={`tool-split-results calculator-saved ${data.scrollResults ? "is-scrollable" : ""}`}
        style={data.scrollResults && calculatorHeight ? { "--calculator-scroll-height": `${calculatorHeight}px` } : undefined}
      >
        <div className="tool-split-actions">
          {activeParent && (
            <button className="tool-split-root-button" type="button" onClick={returnToRoot}>
              {textContent.rootButton || "Revenir à la racine"}
            </button>
          )}
          <button className="tool-split-action-button danger" type="button" onClick={resetCalculator} aria-label={textContent.resetTitle || "Réinitialiser"} title={textContent.resetTitle || "Réinitialiser"}>
            <Icon name="rubber" />
          </button>
          <button className="tool-split-action-button" type="button" onClick={copyChecklistImport} disabled={!data.entries.length} aria-label={textContent.copyTitle || "Copier pour checklist"} title={copied ? textContent.copiedTitle || "Copié" : textContent.copyTitle || "Copier pour checklist"}>
            <Icon name="copy" />
          </button>
          <button
            className={`tool-split-scroll-toggle ${data.scrollResults ? "active" : ""}`}
            type="button"
            onClick={() => setScrollResults(!data.scrollResults)}
            aria-pressed={data.scrollResults}
            title={textContent.scrollableTitle || "Liste scrollable"}
            aria-label={textContent.scrollableTitle || "Liste scrollable"}
          >
            <Icon name="scrollable" />
            <i aria-hidden="true" />
          </button>
        </div>
        <div className={`tool-split-tree calculator-tree ${data.scrollResults ? "is-scrollable legacy-scrollbar" : ""}`}>
          {data.entries.length ? (
            <CalculatorEntries entries={data.entries} parentId="" getScopedParentId={scopeParentId} activeParentId={activeParentId} textContent={textContent} reorder={reorder} onUse={useEntry} onRename={renameEntry} onDelete={deleteEntry} />
          ) : (
            <p className="muted">{textContent.emptyResults || "Aucun résultat enregistré."}</p>
          )}
        </div>
      </div>
    </div>
  );
}

function CalculatorEntries({ entries, parentId, getScopedParentId, activeParentId, textContent, reorder, onUse, onRename, onDelete }) {
  const children = getChildren(entries, parentId);
  const [editingId, setEditingId] = useState("");
  if (!children.length) return null;

  return (
    <ul className="tool-split-entry-list calculator-entry-list">
      {children.map((entry) => {
        const className = [
          entry.id === activeParentId ? "active" : "",
          entry.id === editingId ? "is-editing" : "",
          reorder.isItemDragging(entry.id) ? "is-dragging" : "",
          reorder.isItemDropTarget(entry.id) ? "is-drop-target" : "",
          reorder.getDropPlacement("item", entry.id, parentId) === "after" ? "drop-after" : ""
        ].filter(Boolean).join(" ");

        return (
        <li className={className} key={entry.id} {...reorder.getItemProps({ itemId: entry.id, parentId: getScopedParentId(parentId) })}>
          <div className="tool-split-entry calculator-entry has-drag-handle">
            <button
              className="calculator-drag-handle"
              type="button"
              onPointerDown={(event) => reorder.itemReorder.startDrag(event, entry.id)}
              aria-label={`${textContent.reorderTitle || "Déplacer"} ${entry.label || formatResult(entry.value)}`}
              title={textContent.reorderTitle || "Déplacer"}
            >
              <Icon name="drag" />
            </button>
            {entry.id === editingId ? (
              <>
                <span className="tool-split-entry-value is-readonly" title={textContent.readonlyValueTitle || "Quantité non modifiable"}>
                  <strong className={getEntryResultLengthClass(entry.value)} aria-label={formatDisplayResult(entry.value)}>
                    <FormattedCalculatorNumber value={entry.value} />
                  </strong>
                </span>
                <EditableCalculatorLabel entry={entry} textContent={textContent} onRename={onRename} onDone={() => setEditingId("")} />
              </>
            ) : (
              <button className="tool-split-entry-summary" type="button" onClick={() => onUse(entry)} title={textContent.useEntryTitle || "Utiliser comme base"}>
                <span>
                  <strong className={getEntryResultLengthClass(entry.value)} aria-label={formatDisplayResult(entry.value)}>
                    <FormattedCalculatorNumber value={entry.value} />
                  </strong>
                  {entry.label && <em>{entry.label}</em>}
                </span>
              </button>
            )}
            <button type="button" onClick={() => setEditingId(entry.id === editingId ? "" : entry.id)} aria-label={`${textContent.renameTitle || "Renommer"} ${entry.label}`} title={textContent.renameTitle || "Renommer"}>
              <Icon name="edit" />
            </button>
            <button type="button" className="danger" onClick={() => onDelete(entry.id)} aria-label={`${textContent.deleteTitle || "Supprimer"} ${entry.label}`} title={textContent.deleteTitle || "Supprimer"}>
              <Icon name="trash" />
            </button>
          </div>
          <CalculatorEntries entries={entries} parentId={entry.id} getScopedParentId={getScopedParentId} activeParentId={activeParentId} textContent={textContent} reorder={reorder} onUse={onUse} onRename={onRename} onDelete={onDelete} />
        </li>
        );
      })}
    </ul>
  );
}

function EditableCalculatorLabel({ entry, textContent, onRename, onDone }) {
  const inlineEdit = useInlineEdit({
    value: entry.label,
    onCommit: (label) => onRename(entry.id, label),
    onDone,
    autoFocus: true,
    focusKey: entry.id
  });

  return (
    <input
      {...inlineEdit.getInputProps({
        className: "tool-split-entry-label",
        "aria-label": `${textContent.renameTitle || "Renommer"} ${entry.label}`
      })}
    />
  );
}
