/**
 * @fileoverview React component for BlocklyEditor.jsx
 * 
 * @module components.game.editors.BlocklyEditor
 */


import React, {
  useEffect,
  useRef,
  forwardRef,
  useImperativeHandle,
  useState,
  useCallback,
  useMemo,
} from "react";
import PropTypes from "prop-types";
import * as Blockly from "blockly/core";
import Theme from "@blockly/theme-modern";
import { javascriptGenerator } from "blockly/javascript";
import { useGameState } from "../../../contexts/GameStateContext";
import { validateBlocklyWorkspace } from "@/blockly/validation";
import { useEditor } from "../../../contexts/EditorContext";
import {
  loadWorkspace,
  createDebouncedSave,
} from "../../../services/blockstorage";
import { getCategoryIcon } from "./toolboxIcons";
import { Hammer } from "lucide-react";
import "./custom_category";
import "./BlocklyEditor.mobile.css";

const LoadingSpinner = () => (
  <div className="blockly-loading-spinner">
    <p className="blockly-loading-text">Carregando Editor...</p>
  </div>
);

const BlocklyEditor = forwardRef(function BlocklyEditor(
  { toolboxGenerator, debugSolutions = null, starterBlocks = null, starter = null },
  ref,
) {
  const starterBlocksProp = starterBlocks ?? starter;
  const { registerExecutionFunction, onWorkspaceChange } = useGameState();
  const { gameConfig: editorGameConfig, currentPhase, editorData, updateEditorData, gameNameKey } =
    useEditor();

  const hasSolution = debugSolutions && debugSolutions[currentPhase];

  const handleLoadSolution = () => {
    if (!workspaceRef.current) {
      alert("Editor não está pronto");
      return;
    }

    if (window.confirm("Deseja carregar a solução desta fase?")) {
      try {
        const solution = debugSolutions[currentPhase];
        workspaceRef.current.clear();
        Blockly.serialization.workspaces.load(solution, workspaceRef.current);
      } catch (error) {
        alert("Erro ao carregar a solução");
      }
    }
  };

  useEffect(() => {
    if (!editorGameConfig || !toolboxGenerator) return;

    const phases = editorGameConfig.fases ?? editorGameConfig.phases; // support English alias
    const phaseConfig = phases?.[currentPhase - 1];
    const toolbox = toolboxGenerator(phaseConfig?.allowedBlocks);
    const max = phaseConfig?.maxBlocks;

    updateEditorData({
      toolboxJson: toolbox,
      maxBlocks: max,
    });
  }, [editorGameConfig, currentPhase, toolboxGenerator, updateEditorData]);

  const { toolboxJson, maxBlocks } = editorData;

  const blocklyDiv = useRef(null);
  const workspaceRef = useRef(null);
  const [initialJson, setInitialJson] = useState(undefined);
  const [isLoading, setIsLoading] = useState(true);
  const isInitializedRef = useRef(false);
  const [currentBlockCount, setCurrentBlockCount] = useState(0);

  const debouncedSave = useMemo(() => createDebouncedSave(1000), []);
  const stableToolboxJson = useMemo(() => toolboxJson, [toolboxJson]);

  const updateCategoriesState = useCallback((limitReached) => {
    if (!workspaceRef.current) return;

    const toolbox = workspaceRef.current.getToolbox();
    if (!toolbox) return;

    const categories = toolbox.getToolboxItems();

    categories.forEach((category) => {
      category.setDisabled(limitReached);
    });
  }, []);

  const workspaceChange = useCallback(() => {
    if (!workspaceRef.current) return;
    const blockCount = workspaceRef.current.getAllBlocks().length;
    setCurrentBlockCount(blockCount);

    const limitReached =
      maxBlocks !== undefined &&
      maxBlocks !== Infinity &&
      maxBlocks <= blockCount;
    updateCategoriesState(limitReached);

    if (onWorkspaceChange) {
      onWorkspaceChange(blockCount);
    }
  }, [onWorkspaceChange, maxBlocks, updateCategoriesState]);

  useEffect(() => {
    updateCategoriesState();
  }, [updateCategoriesState]);

  useEffect(() => {
    const generateAndValidateCode = () => {
      if (!workspaceRef.current) {
        return { codigo: null, workspace: null };
      }
      const validation = validateBlocklyWorkspace(workspaceRef.current, {
        allowMultipleTopBlocks: editorGameConfig?.allowMultipleTopBlocks ?? false,
        preferredStartBlocks: ["start", "when_run", "main"],
      });
      if (!validation.isValid) {
        return { codigo: null, workspace: null };
      }
      const codigo = javascriptGenerator.workspaceToCode(workspaceRef.current);
      return { codigo, workspace: workspaceRef.current };
    };
    registerExecutionFunction(generateAndValidateCode);


    return () => {
      registerExecutionFunction(null);
    };
  }, [registerExecutionFunction]);

  useEffect(() => {
    setIsLoading(true);
    const loadedData = loadWorkspace(gameNameKey);
    setInitialJson(loadedData);
    setIsLoading(false);
  }, [gameNameKey]);

  useImperativeHandle(ref, () => workspaceRef.current, []);

  useEffect(() => {
    if (
      isLoading ||
      isInitializedRef.current ||
      !blocklyDiv.current ||
      !stableToolboxJson
    ) {
      return;
    }
    isInitializedRef.current = true;

    const toolboxWithIcons = {
      ...stableToolboxJson,
      contents: stableToolboxJson.contents.map((cat) => ({
        ...cat,
        "css-icon": getCategoryIcon(cat.name),
      })),
    };

    workspaceRef.current = Blockly.inject(blocklyDiv.current, {
      toolbox: toolboxWithIcons,
      trashcan: true,
      scrollbars: true,
      renderer: "zelos",
      theme: Theme,
      grid: { spacing: 25, length: 3, colour: "#ccc", snap: true },
      zoom: { controls: false, wheel: true, startScale: 0.7 },
    });

    // Criar variáveis pré-definidas para o jogo Cripto
    if (editorGameConfig?.gameId === "cripto") {
      ["entrada", "saida", "pos"].forEach((varName) => {
        const variableMap = workspaceRef.current.getVariableMap();
        if (!variableMap.getVariable(varName)) {
          workspaceRef.current.createVariable(varName);
        }
      });
    }

    // Carregar workspace: prioridade = localStorage > starterBlocks > vazio
    if (initialJson) {
      try {
        Blockly.serialization.workspaces.load(
          initialJson,
          workspaceRef.current,
        );
      } catch (error) {
        console.error(
          "Falha ao carregar workspace do localStorage, limpando.",
          error,
        );
        workspaceRef.current.clear();
      }
    } else if (starterBlocksProp && starterBlocksProp[currentPhase]) {
      // Se não há nada salvo, carregar blocos iniciais da fase
      try {
        Blockly.serialization.workspaces.load(
          starterBlocksProp[currentPhase],
          workspaceRef.current,
        );
      } catch (error) {
        console.error("Falha ao carregar blocos iniciais.", error);
      }
    }

    workspaceChange();

    const listener = (event) => {
      if (!workspaceRef.current || event.isUiEvent) {
        return;
      }

      if (event.type === Blockly.Events.BLOCK_CREATE) {
        const currentCount = workspaceRef.current.getAllBlocks().length;
        if (
          maxBlocks !== undefined &&
          maxBlocks !== Infinity &&
          currentCount > maxBlocks
        ) {
          const blockToRemove = workspaceRef.current.getBlockById(
            event.blockId,
          );
          if (blockToRemove) {
            console.warn(
              `Limite de blocos atingido (${maxBlocks}). Removendo bloco extra: ${event.blockId}`,
            );

            Blockly.Events.disable();
            blockToRemove.dispose(false);
            Blockly.Events.enable();
            return;
          }
        }
      }

      workspaceChange();
      const currentState = Blockly.serialization.workspaces.save(
        workspaceRef.current,
      );
      debouncedSave(gameNameKey, currentState);
    };

    workspaceRef.current.addChangeListener(listener);

    const observer = new ResizeObserver(() => {
      if (workspaceRef.current) {
        Blockly.svgResize(workspaceRef.current);
      }
    });
    observer.observe(blocklyDiv.current);

    setTimeout(() => {
      updateCategoriesState();
      const bg = document.querySelector(".blocklyToolboxBackground");
      if (bg) {
        bg.setAttribute("fill", "none");
        bg.setAttribute("stroke", "none");
      }
    }, 100);

    return () => {
      debouncedSave.cancel();
      if (workspaceRef.current) {
        workspaceRef.current.removeChangeListener(listener);
        try {
          workspaceRef.current.dispose();
        } catch (error) {
          console.warn("Erro ao fazer dispose do workspace:", error);
        }
        workspaceRef.current = null;
      }
      observer.disconnect();
      isInitializedRef.current = false;
    };
  }, [
    isLoading,
    initialJson,
    gameNameKey,
    stableToolboxJson,
    debouncedSave,
    workspaceChange,
    maxBlocks,
    updateCategoriesState,
    editorGameConfig,
    currentPhase,
  ]);

  if (isLoading || !toolboxJson) {
    return <LoadingSpinner />;
  }

  return (
    <div className="relative w-full h-full">
      {hasSolution && (
        <button
          onClick={handleLoadSolution}
          className="blockly-debug-solution-btn"
          title="Carregar solução (Debug)"
        >
          <Hammer className="w-5 h-5 text-yellow-500" />
        </button>
      )}
      <div ref={blocklyDiv} className="w-full h-full" />
    </div>
  );
});

export default React.memo(BlocklyEditor);

BlocklyEditor.propTypes = {
  toolboxGenerator: PropTypes.func.isRequired,
  debugSolutions: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
  starterBlocks: PropTypes.object,
  starter: PropTypes.object,
};
