/**
 * @fileoverview Utility module for blocks.js
 * 
 * @module games.cripto.blocks.blocks
 */

"use strict";

import * as Blockly from "blockly/core";
import "blockly/blocks";
import { javascriptGenerator } from "blockly/javascript";

const HUE_LOGICA = 210;
const HUE_MATEMATICA = 230;
const HUE_TEXTO = 160;
const HUE_REPETICAO = 120;
const HUE_VARIAVEIS = 330;

export const registerBlocks = () => {
  defineBlocks();
  defineGenerators();
};

/**
 * Registra todos os blocos e geradores do jogo Cripto no Blockly.
 * Deve ser chamado uma vez durante a inicialização do editor.
 * @returns {void}
 */


export const generateDynamicToolbox = (allowedBlocks = []) => {
  const blockDefinitions = {
    // Matemática
    math_number: {
      kind: "block",
      type: "math_number",
    },
    math_arithmetic: {
      kind: "block",
      type: "math_arithmetic",
    },
    math_modulo: {
      kind: "block",
      type: "math_modulo",
    },

    // Texto
    text: {
      kind: "block",
      type: "text",
    },
    text_indexOf: {
      kind: "block",
      type: "text_indexOf",
    },
    text_charAt: {
      kind: "block",
      type: "text_charAt",
    },
    text_join: {
      kind: "block",
      type: "text_join",
    },
    text_length: {
      kind: "block",
      type: "text_length",
    },
    alfabeto: {
      kind: "block",
      type: "alfabeto",
    },
    alfabeto_secreto: {
      kind: "block",
      type: "alfabeto_secreto",
    },

    // Lógica
    controls_if: {
      kind: "block",
      type: "controls_if",
    },
    logic_compare: {
      kind: "block",
      type: "logic_compare",
    },

    // Repetição
    controls_whileUntil: {
      kind: "block",
      type: "controls_whileUntil",
    },
    definir_contador: {
      kind: "block",
      type: "definir_contador",
    },
    obter_contador: {
      kind: "block",
      type: "obter_contador",
    },
    // Blocos Customizados de Entrada/Saída
    definir_entrada: {
      kind: "block",
      type: "definir_entrada",
    },
    definir_saida: {
      kind: "block",
      type: "definir_saida",
    },
    concatenar_saida: {
      kind: "block",
      type: "concatenar_saida",
    },
    obter_entrada: {
      kind: "block",
      type: "obter_entrada",
    },
    obter_saida: {
      kind: "block",
      type: "obter_saida",
    },

    // Variáveis Customizadas
    definir_letra: {
      kind: "block",
      type: "definir_letra",
    },
    obter_letra: {
      kind: "block",
      type: "obter_letra",
    },
    definir_posicao: {
      kind: "block",
      type: "definir_posicao",
    },
    obter_posicao: {
      kind: "block",
      type: "obter_posicao",
    },
    definir_nova_posicao: {
      kind: "block",
      type: "definir_nova_posicao",
    },
    obter_nova_posicao: {
      kind: "block",
      type: "obter_nova_posicao",
    },
    definir_nova_letra: {
      kind: "block",
      type: "definir_nova_letra",
    },
    obter_nova_letra: {
      kind: "block",
      type: "obter_nova_letra",
    },
    definir_chave: {
      kind: "block",
      type: "definir_chave",
    },
    obter_chave: {
      kind: "block",
      type: "obter_chave",
    },
    definir_letra_secreta: {
      kind: "block",
      type: "definir_letra_secreta",
    },
    obter_letra_secreta: {
      kind: "block",
      type: "obter_letra_secreta",
    },
    definir_soma: {
      kind: "block",
      type: "definir_soma",
    },
    obter_soma: {
      kind: "block",
      type: "obter_soma",
    },
  };

  const toolboxContents = {
    kind: "categoryToolbox",
    contents: [
      {
        kind: "category",
        name: "Entrada/Saída",
        colour: HUE_VARIAVEIS,
        contents: [],
        cssConfig: { container: "variaveis" },
      },
      {
        kind: "category",
        name: "Lógica",
        colour: HUE_LOGICA,
        contents: [],
        cssConfig: { container: "logica" },
      },
      {
        kind: "category",
        name: "Repetição",
        colour: HUE_REPETICAO,
        contents: [],
        cssConfig: { container: "repeticao" },
      },
      {
        kind: "category",
        name: "Texto",
        colour: HUE_TEXTO,
        contents: [],
        cssConfig: { container: "texto" },
      },
      {
        kind: "category",
        name: "Matemática",
        colour: HUE_MATEMATICA,
        contents: [],
        cssConfig: { container: "matematica" },
      },
    ],
  };

  allowedBlocks.forEach((blockId) => {
    const blockDef = blockDefinitions[blockId];

    if (!blockDef) {
      console.warn(`Bloco não encontrado: ${blockId}`);
      return;
    }

    const categoryMap = {
      obter_entrada: 0,
      obter_saida: 0,
      definir_entrada: 0,
      definir_saida: 0,
      concatenar_saida: 0,
      definir_letra: 0,
      obter_letra: 0,
      definir_posicao: 0,
      obter_posicao: 0,
      definir_nova_posicao: 0,
      obter_nova_posicao: 0,
      definir_nova_letra: 0,
      obter_nova_letra: 0,
      definir_chave: 0,
      obter_chave: 0,
      definir_letra_secreta: 0,
      obter_letra_secreta: 0,
      definir_soma: 0,
      obter_soma: 0,
      controls_if: 1,
      logic_compare: 1,
      controls_whileUntil: 2,
      definir_contador: 2,
      obter_contador: 2,
      text: 3,
      text_charAt: 3,
      text_join: 3,
      text_length: 3,
      text_indexOf: 3,
      alfabeto: 3,
      alfabeto_secreto: 3,
      math_number: 4,
      math_arithmetic: 4,
      math_modulo: 4,
    };

    const categoryIndex = categoryMap[blockId];

    if (
      categoryIndex !== undefined &&
      categoryIndex >= 0 &&
      toolboxContents.contents[categoryIndex]
    ) {
      if (!toolboxContents.contents[categoryIndex].contents) {
        toolboxContents.contents[categoryIndex].contents = [];
      }
      toolboxContents.contents[categoryIndex].contents.push(blockDef);
    }
  });

  return toolboxContents;
};

/**
 * Gera a estrutura de toolbox do Blockly contendo apenas blocos permitidos.
 * Recebe `allowedBlocks` (lista de ids) e retorna o JSON do toolbox.
 * @param {Array<string>} [allowedBlocks=[]] - Lista de blocos habilitados
 * @returns {Object} Estrutura `categoryToolbox` para o Blockly
 */

const defineBlocks = () => {
  Blockly.Blocks["text_charAt"] = {
    init: function () {
      this.setHelpUrl(Blockly.Msg["TEXT_CHARAT_HELPURL"]);
      this.setColour(HUE_TEXTO);
      this.setOutput(true, "String");
      this.appendValueInput("VALUE").setCheck("String").appendField("no texto");
      this.appendValueInput("AT").setCheck("Number").appendField("obter letra");
      this.setInputsInline(true);
      this.setTooltip(Blockly.Msg["TEXT_CHARAT_TOOLTIP"]);
    },
  };

  // Bloco: Definir Contador
  Blockly.Blocks["definir_contador"] = {
    init: function () {
      this.appendValueInput("VALUE")
        .setCheck(null)
        .appendField("definir CONTADOR como");
      this.setPreviousStatement(true, null);
      this.setNextStatement(true, null);
      this.setColour(HUE_REPETICAO);
      this.setTooltip("Define o valor do CONTADOR");
      this.setHelpUrl("");
    },
  };

  // Bloco: Definir Entrada
  Blockly.Blocks["definir_entrada"] = {
    init: function () {
      this.appendValueInput("VALUE")
        .setCheck(null)
        .appendField("definir ENTRADA como");
      this.setPreviousStatement(true, null);
      this.setNextStatement(true, null);
      this.setColour(HUE_VARIAVEIS);
      this.setTooltip("Define o valor da ENTRADA");
      this.setHelpUrl("");
    },
  };

  // Bloco: Definir Saída
  Blockly.Blocks["definir_saida"] = {
    init: function () {
      this.appendValueInput("VALUE")
        .setCheck(null)
        .appendField("definir SAÍDA como");
      this.setPreviousStatement(true, null);
      this.setNextStatement(true, null);
      this.setColour(HUE_VARIAVEIS);
      this.setTooltip("Define o valor da SAÍDA");
      this.setHelpUrl("");
    },
  };

  // Bloco: Concatenar Saída
  Blockly.Blocks["concatenar_saida"] = {
    init: function () {
      this.appendValueInput("VALUE")
        .setCheck(null)
        .appendField("adicionar à SAÍDA");
      this.setPreviousStatement(true, null);
      this.setNextStatement(true, null);
      this.setColour(HUE_VARIAVEIS);
      this.setTooltip("Adiciona um valor ao final da saída");
      this.setHelpUrl("");
    },
  };

  // Bloco: Obter Entrada
  Blockly.Blocks["obter_entrada"] = {
    init: function () {
      this.appendDummyInput().appendField("ENTRADA");
      this.setOutput(true, null);
      this.setColour(HUE_VARIAVEIS);
      this.setTooltip("Obtém o valor atual da entrada");
      this.setHelpUrl("");
    },
  };

  // Bloco: Obter Saída
  Blockly.Blocks["obter_saida"] = {
    init: function () {
      this.appendDummyInput().appendField("SAÍDA");
      this.setOutput(true, null);
      this.setColour(HUE_VARIAVEIS);
      this.setTooltip("Obtém o valor atual da saída");
      this.setHelpUrl("");
    },
  };

  // Bloco: Obter Contador
  Blockly.Blocks["obter_contador"] = {
    init: function () {
      this.appendDummyInput().appendField("CONTADOR");
      this.setOutput(true, null);
      this.setColour(HUE_REPETICAO);
      this.setTooltip("Obtém o valor atual do CONTADOR");
      this.setHelpUrl("");
    },
  };

  // Bloco: Alfabeto (constante)
  Blockly.Blocks["alfabeto"] = {
    init: function () {
      this.appendDummyInput().appendField("ALFABETO");
      this.setOutput(true, "String");
      this.setColour(HUE_TEXTO);
      this.setTooltip(
        "Retorna o alfabeto completo: ABCDEFGHIJKLMNOPQRSTUVWXYZ",
      );
      this.setHelpUrl("");
    },
  };

  // Bloco: Alfabeto Secreto (constante)
  Blockly.Blocks["alfabeto_secreto"] = {
    init: function () {
      this.appendDummyInput().appendField("ALFABETO SECRETO");
      this.setOutput(true, "String");
      this.setColour(HUE_TEXTO);
      this.setTooltip(
        "Retorna o alfabeto embaralhado: QWERTYUIOPASDFGHJKLZXCVBNM",
      );
      this.setHelpUrl("");
    },
  };

  // ============ BLOCOS DE VARIÁVEIS CUSTOMIZADAS ============

  // Bloco: Definir letra
  Blockly.Blocks["definir_letra"] = {
    init: function () {
      this.appendValueInput("VALUE")
        .setCheck(null)
        .appendField("definir LETRA como");
      this.setPreviousStatement(true, null);
      this.setNextStatement(true, null);
      this.setColour(HUE_VARIAVEIS);
      this.setTooltip("Define o valor da variável LETRA");
      this.setHelpUrl("");
    },
  };

  // Bloco: Obter letra
  Blockly.Blocks["obter_letra"] = {
    init: function () {
      this.appendDummyInput().appendField("LETRA");
      this.setOutput(true, null);
      this.setColour(HUE_VARIAVEIS);
      this.setTooltip("Obtém o valor da variável LETRA");
      this.setHelpUrl("");
    },
  };

  // Bloco: Definir posicao
  Blockly.Blocks["definir_posicao"] = {
    init: function () {
      this.appendValueInput("VALUE")
        .setCheck(null)
        .appendField("definir POSIÇÃO como");
      this.setPreviousStatement(true, null);
      this.setNextStatement(true, null);
      this.setColour(HUE_VARIAVEIS);
      this.setTooltip("Define o valor da variável POSIÇÃO");
      this.setHelpUrl("");
    },
  };

  // Bloco: Obter posicao
  Blockly.Blocks["obter_posicao"] = {
    init: function () {
      this.appendDummyInput().appendField("POSIÇÃO");
      this.setOutput(true, null);
      this.setColour(HUE_VARIAVEIS);
      this.setTooltip("Obtém o valor da variável POSIÇÃO");
      this.setHelpUrl("");
    },
  };

  // Bloco: Definir nova_posicao
  Blockly.Blocks["definir_nova_posicao"] = {
    init: function () {
      this.appendValueInput("VALUE")
        .setCheck(null)
        .appendField("definir nova_posicao como");
      this.setPreviousStatement(true, null);
      this.setNextStatement(true, null);
      this.setColour(HUE_VARIAVEIS);
      this.setTooltip("Define o valor da variável nova_posicao");
      this.setHelpUrl("");
    },
  };

  // Bloco: Obter nova_posicao
  Blockly.Blocks["obter_nova_posicao"] = {
    init: function () {
      this.appendDummyInput().appendField("nova_posicao");
      this.setOutput(true, null);
      this.setColour(HUE_VARIAVEIS);
      this.setTooltip("Obtém o valor da variável nova_posicao");
      this.setHelpUrl("");
    },
  };

  // Bloco: Definir nova_letra
  Blockly.Blocks["definir_nova_letra"] = {
    init: function () {
      this.appendValueInput("VALUE")
        .setCheck(null)
        .appendField("definir nova_letra como");
      this.setPreviousStatement(true, null);
      this.setNextStatement(true, null);
      this.setColour(HUE_VARIAVEIS);
      this.setTooltip("Define o valor da variável nova_letra");
      this.setHelpUrl("");
    },
  };

  // Bloco: Obter nova_letra
  Blockly.Blocks["obter_nova_letra"] = {
    init: function () {
      this.appendDummyInput().appendField("nova_letra");
      this.setOutput(true, null);
      this.setColour(HUE_VARIAVEIS);
      this.setTooltip("Obtém o valor da variável nova_letra");
      this.setHelpUrl("");
    },
  };

  // Bloco: Definir chave
  Blockly.Blocks["definir_chave"] = {
    init: function () {
      this.appendValueInput("VALUE")
        .setCheck(null)
        .appendField("definir chave como");
      this.setPreviousStatement(true, null);
      this.setNextStatement(true, null);
      this.setColour(HUE_VARIAVEIS);
      this.setTooltip(
        "Define o valor da variável chave (deslocamento da cifra)",
      );
      this.setHelpUrl("");
    },
  };

  // Bloco: Obter chave
  Blockly.Blocks["obter_chave"] = {
    init: function () {
      this.appendDummyInput().appendField("chave");
      this.setOutput(true, null);
      this.setColour(HUE_VARIAVEIS);
      this.setTooltip("Obtém o valor da variável chave");
      this.setHelpUrl("");
    },
  };

  // Bloco: Definir letra_secreta
  Blockly.Blocks["definir_letra_secreta"] = {
    init: function () {
      this.appendValueInput("VALUE")
        .setCheck(null)
        .appendField("definir LETRA_SECRETA como");
      this.setPreviousStatement(true, null);
      this.setNextStatement(true, null);
      this.setColour(HUE_VARIAVEIS);
      this.setTooltip("Define o valor da variável LETRA_SECRETA");
      this.setHelpUrl("");
    },
  };

  // Bloco: Obter letra_secreta
  Blockly.Blocks["obter_letra_secreta"] = {
    init: function () {
      this.appendDummyInput().appendField("LETRA_SECRETA");
      this.setOutput(true, null);
      this.setColour(HUE_VARIAVEIS);
      this.setTooltip("Obtém o valor da variável LETRA_SECRETA");
      this.setHelpUrl("");
    },
  };

  // Bloco: Definir soma
  Blockly.Blocks["definir_soma"] = {
    init: function () {
      this.appendValueInput("VALUE")
        .setCheck(null)
        .appendField("definir SOMA como");
      this.setPreviousStatement(true, null);
      this.setNextStatement(true, null);
      this.setColour(HUE_VARIAVEIS);
      this.setTooltip("Define o valor da variável SOMA (acumulador)");
      this.setHelpUrl("");
    },
  };

  // Bloco: Obter soma
  Blockly.Blocks["obter_soma"] = {
    init: function () {
      this.appendDummyInput().appendField("SOMA");
      this.setOutput(true, null);
      this.setColour(HUE_VARIAVEIS);
      this.setTooltip("Obtém o valor da variável SOMA");
      this.setHelpUrl("");
    },
  };
};

const defineGenerators = () => {
  javascriptGenerator.STATEMENT_PREFIX = "highlightBlock(%1);\n";
  javascriptGenerator.addReservedWords("highlightBlock");

  // Gerador: Definir Entrada
  javascriptGenerator.forBlock["definir_entrada"] = function (block) {
    const value =
      javascriptGenerator.valueToCode(
        block,
        "VALUE",
        javascriptGenerator.ORDER_ATOMIC,
      ) || "''";
    return "definirEntrada(" + value + ");\n";
  };

  // Gerador: Definir Saída
  javascriptGenerator.forBlock["definir_saida"] = function (block) {
    const value =
      javascriptGenerator.valueToCode(
        block,
        "VALUE",
        javascriptGenerator.ORDER_ATOMIC,
      ) || "''";
    return "definirSaida(" + value + ");\n";
  };

  // Gerador: Definir Contador
  javascriptGenerator.forBlock["definir_contador"] = function (block) {
    const value =
      javascriptGenerator.valueToCode(
        block,
        "VALUE",
        javascriptGenerator.ORDER_ATOMIC,
      ) || "''";
    return "definirContador(" + value + ");\n";
  };

  // Gerador: Concatenar Saída
  javascriptGenerator.forBlock["concatenar_saida"] = function (block) {
    const value =
      javascriptGenerator.valueToCode(
        block,
        "VALUE",
        javascriptGenerator.ORDER_ATOMIC,
      ) || "''";
    return "concatenarSaida(" + value + ");\n";
  };

  // Gerador: Obter Entrada
  javascriptGenerator.forBlock["obter_entrada"] = function (block) {
    const code = "obterEntrada()";
    return [code, javascriptGenerator.ORDER_FUNCTION_CALL];
  };

  // Gerador: Obter Saída
  javascriptGenerator.forBlock["obter_saida"] = function (block) {
    const code = "obterSaida()";
    return [code, javascriptGenerator.ORDER_FUNCTION_CALL];
  };

  // Gerador: Obter Contador
  javascriptGenerator.forBlock["obter_contador"] = function (block) {
    const code = "obterContador()";
    return [code, javascriptGenerator.ORDER_FUNCTION_CALL];
  };

  // Gerador: Alfabeto
  javascriptGenerator.forBlock["alfabeto"] = function (block) {
    const code = '"ABCDEFGHIJKLMNOPQRSTUVWXYZ"';
    return [code, javascriptGenerator.ORDER_ATOMIC];
  };

  // Gerador: Alfabeto Secreto
  javascriptGenerator.forBlock["alfabeto_secreto"] = function (block) {
    const code = '"QWERTYUIOPASDFGHJKLZXCVBNM"';
    return [code, javascriptGenerator.ORDER_ATOMIC];
  };

  // Gerador customizado: text_charAt (0-based, não subtrai 1)
  // Assume que todos os índices fornecidos já são 0-based (compatível com CONTADOR = 0)
  javascriptGenerator.forBlock["text_charAt"] = function (block) {
    const text =
      javascriptGenerator.valueToCode(
        block,
        "VALUE",
        javascriptGenerator.ORDER_MEMBER,
      ) || "''";
    const at =
      javascriptGenerator.valueToCode(
        block,
        "AT",
        javascriptGenerator.ORDER_NONE,
      ) || "0";
    const code = text + ".charAt(" + at + ")";
    return [code, javascriptGenerator.ORDER_MEMBER];
  };

  // Gerador customizado: text_indexOf (0-based, não adiciona 1)
  // Retorna o índice 0-based direto, compatível com charAt e arrays JavaScript
  javascriptGenerator.forBlock["text_indexOf"] = function (block) {
    const text =
      javascriptGenerator.valueToCode(
        block,
        "VALUE",
        javascriptGenerator.ORDER_MEMBER,
      ) || "''";
    const search =
      javascriptGenerator.valueToCode(
        block,
        "FIND",
        javascriptGenerator.ORDER_NONE,
      ) || "''";
    const code = text + ".indexOf(" + search + ")";
    return [code, javascriptGenerator.ORDER_MEMBER];
  };

  // ============ GERADORES PARA VARIÁVEIS CUSTOMIZADAS ============

  // Geradores: letra
  javascriptGenerator.forBlock["definir_letra"] = function (block) {
    const value =
      javascriptGenerator.valueToCode(
        block,
        "VALUE",
        javascriptGenerator.ORDER_ATOMIC,
      ) || "''";
    return "var letra = " + value + ";\n";
  };

  javascriptGenerator.forBlock["obter_letra"] = function (block) {
    return ["letra", javascriptGenerator.ORDER_ATOMIC];
  };

  // Geradores: posicao
  javascriptGenerator.forBlock["definir_posicao"] = function (block) {
    const value =
      javascriptGenerator.valueToCode(
        block,
        "VALUE",
        javascriptGenerator.ORDER_ATOMIC,
      ) || "0";
    return "var posicao = " + value + ";\n";
  };

  javascriptGenerator.forBlock["obter_posicao"] = function (block) {
    return ["posicao", javascriptGenerator.ORDER_ATOMIC];
  };

  // Geradores: nova_posicao
  javascriptGenerator.forBlock["definir_nova_posicao"] = function (block) {
    const value =
      javascriptGenerator.valueToCode(
        block,
        "VALUE",
        javascriptGenerator.ORDER_ATOMIC,
      ) || "0";
    return "var nova_posicao = " + value + ";\n";
  };

  javascriptGenerator.forBlock["obter_nova_posicao"] = function (block) {
    return ["nova_posicao", javascriptGenerator.ORDER_ATOMIC];
  };

  // Geradores: nova_letra
  javascriptGenerator.forBlock["definir_nova_letra"] = function (block) {
    const value =
      javascriptGenerator.valueToCode(
        block,
        "VALUE",
        javascriptGenerator.ORDER_ATOMIC,
      ) || "''";
    return "var nova_letra = " + value + ";\n";
  };

  javascriptGenerator.forBlock["obter_nova_letra"] = function (block) {
    return ["nova_letra", javascriptGenerator.ORDER_ATOMIC];
  };

  // Geradores: chave
  javascriptGenerator.forBlock["definir_chave"] = function (block) {
    const value =
      javascriptGenerator.valueToCode(
        block,
        "VALUE",
        javascriptGenerator.ORDER_ATOMIC,
      ) || "0";
    return "var chave = " + value + ";\n";
  };

  javascriptGenerator.forBlock["obter_chave"] = function (block) {
    return ["chave", javascriptGenerator.ORDER_ATOMIC];
  };

  // Geradores: letra_secreta
  javascriptGenerator.forBlock["definir_letra_secreta"] = function (block) {
    const value =
      javascriptGenerator.valueToCode(
        block,
        "VALUE",
        javascriptGenerator.ORDER_ATOMIC,
      ) || "''";
    return "var letra_secreta = " + value + ";\n";
  };

  javascriptGenerator.forBlock["obter_letra_secreta"] = function (block) {
    return ["letra_secreta", javascriptGenerator.ORDER_ATOMIC];
  };

  // Geradores: soma
  javascriptGenerator.forBlock["definir_soma"] = function (block) {
    const value =
      javascriptGenerator.valueToCode(
        block,
        "VALUE",
        javascriptGenerator.ORDER_ATOMIC,
      ) || "0";
    return "var soma = " + value + ";\n";
  };

  javascriptGenerator.forBlock["obter_soma"] = function (block) {
    return ["soma", javascriptGenerator.ORDER_ATOMIC];
  };
};
