from collections.abc import Callable
from typing import NamedTuple, NotRequired, TypedDict

from blends.models import (
    Graph,
    Language,
    NId,
    SyntaxGraph,
)


class SyntaxMetadata(TypedDict):
    class_path: list[str]
    scope_stack: list[NId]
    class_member_scope_stack: NotRequired[list[tuple[NId, str]]]
    binding_counter: NotRequired[dict[NId, int]]
    next_synthetic_id: NotRequired[int]
    root_nid: NotRequired[NId]
    is_classmethod: NotRequired[bool]


class SyntaxGraphArgs(NamedTuple):
    generic: Callable[["SyntaxGraphArgs"], NId]
    path: str
    language: Language
    ast_graph: Graph
    syntax_graph: SyntaxGraph
    n_id: NId
    metadata: SyntaxMetadata

    def fork_n_id(self, n_id: NId) -> "SyntaxGraphArgs":
        return SyntaxGraphArgs(
            generic=self.generic,
            path=self.path,
            language=self.language,
            ast_graph=self.ast_graph,
            syntax_graph=self.syntax_graph,
            n_id=n_id,
            metadata=self.metadata,
        )


SyntaxReader = Callable[[SyntaxGraphArgs], NId]
Dispatcher = dict[str, SyntaxReader]


class FileStructData(TypedDict):
    node: NId
    type: str
    data: dict[str, str] | str


class FileInstanceData(TypedDict):
    object: str
    source: str
    source_type: str


class ReaderLogicError(Exception):
    pass


class SyntaxCfgArgs(NamedTuple):
    generic: Callable[["SyntaxCfgArgs"], NId]
    graph: SyntaxGraph
    n_id: NId
    nxt_id: NId | None
    is_multifile: bool = False

    def fork(self, n_id: NId, nxt_id: NId | None) -> "SyntaxCfgArgs":
        return SyntaxCfgArgs(
            generic=self.generic,
            graph=self.graph,
            n_id=n_id,
            nxt_id=nxt_id,
            is_multifile=self.is_multifile,
        )


class MissingCfgBuilderError(Exception):
    pass
