from __future__ import annotations

from dataclasses import dataclass
from typing import Any, Optional
from pathlib import Path

from neurotron.lang.frontend.lexer.lexer import tokenize as lex_tokenize
from neurotron.lang.frontend.parser.parser import parse as parse_source
from neurotron.lang.frontend.ir_builder import build_ir

from neurotron.lang.backend.validate_ir import validate_ir
from neurotron.lang.holodeck.vm import HolodeckVM


@dataclass
class ProgramResult:
    ok: bool
    value: Any = None
    errors: Optional[list[str]] = None
    ir: Optional[Any] = None
    ast: Optional[Any] = None


class ProgramadorV01:
    def __init__(
        self,
        ctx,
        debug: bool = False,
        vm_trace: bool = False,
    ):
        self.ctx = ctx
        self.debug = debug
        self.vm_trace = vm_trace

        self.base_dir = ctx.lang_dir
        self.examples_dir = ctx.lang_examples_dir

    def run_file(self, path: str) -> ProgramResult:
        """
        Executa um ficheiro .nl.

        Regras:
        - path absoluto → usado diretamente
        - path relativo  → resolvido a partir de ctx.lang_examples_dir
        """

        p = Path(path)

        if p.is_absolute():
            source_path = p
        else:
            # primeiro tenta relativo aos exemplos
            candidate = self.examples_dir / p
            if candidate.exists():
                source_path = candidate
            else:
                # fallback: relativo ao runtime base
                candidate = self.base_dir / p
                source_path = candidate

        if not source_path.exists():
            return ProgramResult(
                False,
                errors=[f"[FS] Neuro source not found: {source_path}"],
            )

        try:
            source = source_path.read_text(encoding="utf-8")
        except Exception as e:
            return ProgramResult(
                False,
                errors=[f"[FS] Failed to read {source_path}: {e}"],
            )

        return self.run_source(source, filename=str(source_path))


    def run_source(self, source: str, *, filename: str = "<memory>") -> ProgramResult:
        # 1) Lex (útil para debug; o parser já chama Lexer internamente, mas ok)
        try:
            tokens = lex_tokenize(source)
        except Exception as e:
            return ProgramResult(False, errors=[f"[LEX] {e}"])

        # 2) Parse -> AST
        try:
            ast = parse_source(source)
        except Exception as e:
            return ProgramResult(False, errors=[f"[PARSE] {e}"])

        # 3) AST -> IR
        try:
            module_ir = build_ir(ast)
        except Exception as e:
            return ProgramResult(False, ast=ast, errors=[f"[IR] {e}"])

        # 4) Validate IR
        status, ir_errors = validate_ir(module_ir)
        if status != "IR_VALID":
            return ProgramResult(
                False,
                ast=ast,
                ir=module_ir,
                errors=[str(err) for err in ir_errors],
            )

        # 5) Run VM
        try:
            vm = HolodeckVM(module_ir)
            # o teu ModuleIR.entrypoint é "__main__" e o vm.py default também
            value = vm.run(entrypoint=module_ir.entrypoint, args=[])
            return ProgramResult(True, value=value, ast=ast, ir=module_ir)
        except Exception as e:
            return ProgramResult(False, ast=ast, ir=module_ir, errors=[f"[VM] {e}"])
