"""Transpilador que genera código ensamblador compatible con NASM."""

from cobra.core.ast_nodes import (
    NodoAsignacion,
    NodoCondicional,
    NodoBucleMientras,
    NodoFuncion,
    NodoLlamadaFuncion,
    NodoHolobit,
    NodoFor,
    NodoLista,
    NodoDiccionario,
    NodoClase,
    NodoMetodo,
    NodoValor,
    NodoRetorno,
    NodoOperacionBinaria,
    NodoOperacionUnaria,
    NodoIdentificador,
    NodoInstancia,
    NodoLlamadaMetodo,
    NodoAtributo,
    NodoHilo,
    NodoTryCatch,
    NodoThrow,
    NodoImport,
    NodoImprimir,
    NodoPara,
    NodoUsar,
)
from cobra.core import TipoToken
from cobra.transpilers.common.utils import BaseTranspiler
from core.optimizations import optimize_constants, remove_dead_code, inline_functions
from cobra.macro import expandir_macros

from cobra.transpilers.transpiler.asm_nodes.asignacion import visit_asignacion as _visit_asignacion
from cobra.transpilers.transpiler.asm_nodes.condicional import visit_condicional as _visit_condicional
from cobra.transpilers.transpiler.asm_nodes.bucle_mientras import visit_bucle_mientras as _visit_bucle_mientras
from cobra.transpilers.transpiler.asm_nodes.funcion import visit_funcion as _visit_funcion
from cobra.transpilers.transpiler.asm_nodes.llamada_funcion import visit_llamada_funcion as _visit_llamada_funcion
from cobra.transpilers.transpiler.asm_nodes.llamada_metodo import visit_llamada_metodo as _visit_llamada_metodo
from cobra.transpilers.transpiler.asm_nodes.hilo import visit_hilo as _visit_hilo
from cobra.transpilers.transpiler.asm_nodes.imprimir import visit_imprimir as _visit_imprimir
from cobra.transpilers.transpiler.asm_nodes.retorno import visit_retorno as _visit_retorno
from cobra.transpilers.transpiler.asm_nodes.holobit import visit_holobit as _visit_holobit
from cobra.transpilers.transpiler.asm_nodes.for_ import visit_for as _visit_for
from cobra.transpilers.transpiler.asm_nodes.para import visit_para as _visit_para
from cobra.transpilers.transpiler.asm_nodes.lista import visit_lista as _visit_lista
from cobra.transpilers.transpiler.asm_nodes.diccionario import visit_diccionario as _visit_diccionario
from cobra.transpilers.transpiler.asm_nodes.clase import visit_clase as _visit_clase
from cobra.transpilers.transpiler.asm_nodes.metodo import visit_metodo as _visit_metodo
from cobra.transpilers.transpiler.asm_nodes.try_catch import visit_try_catch as _visit_try_catch
from cobra.transpilers.transpiler.asm_nodes.throw import visit_throw as _visit_throw
from cobra.transpilers.transpiler.asm_nodes.importar import visit_import as _visit_import
from cobra.transpilers.transpiler.asm_nodes.instancia import visit_instancia as _visit_instancia
from cobra.transpilers.transpiler.asm_nodes.atributo import visit_atributo as _visit_atributo
from cobra.transpilers.transpiler.asm_nodes.operacion_binaria import (
    visit_operacion_binaria as _visit_operacion_binaria,
)
from cobra.transpilers.transpiler.asm_nodes.operacion_unaria import (
    visit_operacion_unaria as _visit_operacion_unaria,
)
from cobra.transpilers.transpiler.asm_nodes.valor import visit_valor as _visit_valor
from cobra.transpilers.transpiler.asm_nodes.identificador import visit_identificador as _visit_identificador
from cobra.transpilers.transpiler.asm_nodes.usar import visit_usar as _visit_usar
from cobra.transpilers.transpiler.asm_nodes.romper import visit_romper as _visit_romper
from cobra.transpilers.transpiler.asm_nodes.continuar import visit_continuar as _visit_continuar
from cobra.transpilers.transpiler.asm_nodes.pasar import visit_pasar as _visit_pasar


class TranspiladorASM(BaseTranspiler):
    """Transpila el AST de Cobra a ensamblador NASM básico."""

    def __init__(self):
        self.data: list[str] = []
        self.main: list[str] = []
        self.functions: list[str] = []
        self.current = self.main
        self.indent = 0
        self.string_count = 0

    def generate_code(self, ast):
        self.__init__()
        self.transpilar(ast)
        codigo = ["section .data", *self.data, "", "section .text", "global _start", "_start:"]
        codigo.extend(self.main)
        codigo.extend(
            [
                "    mov rax, 60",  # syscall: exit
                "    xor rdi, rdi",
                "    syscall",
                "",
            ]
        )
        codigo.extend(self.functions)
        return "\n".join(codigo)

    def agregar_linea(self, linea: str) -> None:
        self.current.append("    " * self.indent + linea)

    def nueva_cadena(self, texto: str) -> str:
        """Guarda una cadena en la sección de datos y devuelve su etiqueta."""
        etiqueta = f"msg{self.string_count}"
        self.string_count += 1
        escapado = texto.replace("\"", r"\"")
        self.data.append(f'{etiqueta}: db "{escapado}", 10')
        self.data.append(f"{etiqueta}_len: equ $ - {etiqueta}")
        return etiqueta

    def obtener_valor(self, nodo):
        if isinstance(nodo, NodoValor):
            return str(nodo.valor)
        elif isinstance(nodo, NodoAtributo):
            obj = self.obtener_valor(nodo.objeto)
            return f"{obj}.{nodo.nombre}"
        elif isinstance(nodo, NodoInstancia):
            args = ", ".join(self.obtener_valor(a) for a in nodo.argumentos)
            return f"NEW {nodo.nombre_clase}({args})"
        elif isinstance(nodo, NodoIdentificador):
            return nodo.nombre
        elif isinstance(nodo, NodoOperacionBinaria):
            izq = self.obtener_valor(nodo.izquierda)
            der = self.obtener_valor(nodo.derecha)
            op_map = {TipoToken.AND: "AND", TipoToken.OR: "OR"}
            op = op_map.get(nodo.operador.tipo, nodo.operador.valor)
            return f"{izq} {op} {der}"
        elif isinstance(nodo, NodoOperacionUnaria):
            val = self.obtener_valor(nodo.operando)
            op = "NOT" if nodo.operador.tipo == TipoToken.NOT else nodo.operador.valor
            return f"{op} {val}" if op == "NOT" else f"{op}{val}"
        elif isinstance(nodo, NodoLista):
            elems = ", ".join(self.obtener_valor(e) for e in nodo.elementos)
            return f"[{elems}]"
        elif isinstance(nodo, NodoDiccionario):
            pares = ", ".join(
                f"{self.obtener_valor(k)}:{self.obtener_valor(v)}"
                for k, v in nodo.elementos
            )
            return f"{{{pares}}}"
        else:
            return str(getattr(nodo, "valor", nodo))

    def transpilar(self, nodos):
        nodos = expandir_macros(nodos)
        nodos = remove_dead_code(inline_functions(optimize_constants(nodos)))
        for nodo in nodos:
            nodo.aceptar(self)
        return ""


# Asignar los visitantes externos a la clase
TranspiladorASM.visit_asignacion = _visit_asignacion
TranspiladorASM.visit_condicional = _visit_condicional
TranspiladorASM.visit_bucle_mientras = _visit_bucle_mientras
TranspiladorASM.visit_funcion = _visit_funcion
TranspiladorASM.visit_llamada_funcion = _visit_llamada_funcion
TranspiladorASM.visit_llamada_metodo = _visit_llamada_metodo
TranspiladorASM.visit_hilo = _visit_hilo
TranspiladorASM.visit_imprimir = _visit_imprimir
TranspiladorASM.visit_retorno = _visit_retorno
TranspiladorASM.visit_holobit = _visit_holobit
TranspiladorASM.visit_for = _visit_for
TranspiladorASM.visit_para = _visit_para
TranspiladorASM.visit_lista = _visit_lista
TranspiladorASM.visit_diccionario = _visit_diccionario
TranspiladorASM.visit_clase = _visit_clase
TranspiladorASM.visit_metodo = _visit_metodo
TranspiladorASM.visit_try_catch = _visit_try_catch
TranspiladorASM.visit_throw = _visit_throw
TranspiladorASM.visit_import = _visit_import
TranspiladorASM.visit_instancia = _visit_instancia
TranspiladorASM.visit_atributo = _visit_atributo
TranspiladorASM.visit_operacion_binaria = _visit_operacion_binaria
TranspiladorASM.visit_operacion_unaria = _visit_operacion_unaria
TranspiladorASM.visit_valor = _visit_valor
TranspiladorASM.visit_identificador = _visit_identificador
TranspiladorASM.visit_usar = _visit_usar
TranspiladorASM.visit_romper = _visit_romper
TranspiladorASM.visit_continuar = _visit_continuar
TranspiladorASM.visit_pasar = _visit_pasar
