from blends.models import (
    NId,
)
from blends.stack.attributes import (
    STACK_GRAPH_IMPORT_MODULE_STEM,
    STACK_GRAPH_IMPORT_SOURCE_SYMBOL,
    STACK_GRAPH_IS_DEFINITION,
    STACK_GRAPH_IS_EXPORTED,
    STACK_GRAPH_IS_IMPORT_BINDING,
    STACK_GRAPH_IS_REFERENCE,
    STACK_GRAPH_KIND,
    STACK_GRAPH_PRECEDENCE,
    STACK_GRAPH_SCOPE,
    STACK_GRAPH_SCOPE_TYPE,
    STACK_GRAPH_SYMBOL,
)
from blends.stack.node_kinds import (
    StackGraphNodeKind,
)
from blends.stack.scope_types import ScopeType

StackGraphNodeAttributes = dict[str, object]


def push_symbol_node_attributes(
    *, symbol: str, precedence: int = 0, is_reference: bool = True
) -> StackGraphNodeAttributes:
    return {
        STACK_GRAPH_KIND: StackGraphNodeKind.PUSH_SYMBOL.value,
        STACK_GRAPH_SYMBOL: symbol,
        STACK_GRAPH_SCOPE: None,
        STACK_GRAPH_PRECEDENCE: precedence,
        STACK_GRAPH_IS_EXPORTED: False,
        STACK_GRAPH_IS_REFERENCE: is_reference,
        STACK_GRAPH_IS_DEFINITION: False,
    }


def push_scoped_symbol_node_attributes(
    *,
    symbol: str,
    scope: NId,
    precedence: int = 0,
    is_reference: bool = True,
) -> StackGraphNodeAttributes:
    return {
        STACK_GRAPH_KIND: StackGraphNodeKind.PUSH_SCOPED_SYMBOL.value,
        STACK_GRAPH_SYMBOL: symbol,
        STACK_GRAPH_SCOPE: scope,
        STACK_GRAPH_PRECEDENCE: precedence,
        STACK_GRAPH_IS_EXPORTED: False,
        STACK_GRAPH_IS_REFERENCE: is_reference,
        STACK_GRAPH_IS_DEFINITION: False,
    }


def pop_symbol_node_attributes(
    *,
    symbol: str,
    precedence: int = 0,
    is_definition: bool = True,
    is_exported: bool = False,
) -> StackGraphNodeAttributes:
    return {
        STACK_GRAPH_KIND: StackGraphNodeKind.POP_SYMBOL.value,
        STACK_GRAPH_SYMBOL: symbol,
        STACK_GRAPH_SCOPE: None,
        STACK_GRAPH_PRECEDENCE: precedence,
        STACK_GRAPH_IS_EXPORTED: is_exported,
        STACK_GRAPH_IS_REFERENCE: False,
        STACK_GRAPH_IS_DEFINITION: is_definition,
    }


def pop_scoped_symbol_node_attributes(
    *,
    symbol: str,
    precedence: int = 0,
    is_definition: bool = True,
    is_import_binding: bool = False,
    import_module_stem: str | None = None,
) -> StackGraphNodeAttributes:
    attrs: StackGraphNodeAttributes = {
        STACK_GRAPH_KIND: StackGraphNodeKind.POP_SCOPED_SYMBOL.value,
        STACK_GRAPH_SYMBOL: symbol,
        STACK_GRAPH_SCOPE: None,
        STACK_GRAPH_PRECEDENCE: precedence,
        STACK_GRAPH_IS_EXPORTED: False,
        STACK_GRAPH_IS_REFERENCE: False,
        STACK_GRAPH_IS_DEFINITION: is_definition,
        STACK_GRAPH_IS_IMPORT_BINDING: is_import_binding,
    }
    if import_module_stem is not None:
        attrs[STACK_GRAPH_IMPORT_MODULE_STEM] = import_module_stem
    return attrs


def import_binding_node_attributes(
    *,
    symbol: str,
    precedence: int,
    import_module_stem: str,
    import_source_symbol: str | None = None,
) -> StackGraphNodeAttributes:
    attrs = pop_scoped_symbol_node_attributes(
        symbol=symbol,
        precedence=precedence,
        is_import_binding=True,
        import_module_stem=import_module_stem,
    )
    if import_source_symbol is not None:
        attrs[STACK_GRAPH_IMPORT_SOURCE_SYMBOL] = import_source_symbol
    return attrs


def scope_node_attributes(
    *,
    is_exported: bool,
    scope_type: ScopeType | None = None,
) -> StackGraphNodeAttributes:
    attrs: StackGraphNodeAttributes = {
        STACK_GRAPH_KIND: StackGraphNodeKind.SCOPE.value,
        STACK_GRAPH_SYMBOL: None,
        STACK_GRAPH_SCOPE: None,
        STACK_GRAPH_PRECEDENCE: 0,
        STACK_GRAPH_IS_EXPORTED: is_exported,
        STACK_GRAPH_IS_REFERENCE: False,
        STACK_GRAPH_IS_DEFINITION: False,
    }
    if scope_type is not None:
        attrs[STACK_GRAPH_SCOPE_TYPE] = scope_type.value
    return attrs


def root_node_attributes() -> StackGraphNodeAttributes:
    return {
        STACK_GRAPH_KIND: StackGraphNodeKind.ROOT.value,
        STACK_GRAPH_SYMBOL: None,
        STACK_GRAPH_SCOPE: None,
        STACK_GRAPH_PRECEDENCE: 0,
        STACK_GRAPH_IS_EXPORTED: False,
        STACK_GRAPH_IS_REFERENCE: False,
        STACK_GRAPH_IS_DEFINITION: False,
    }


def jump_to_node_attributes() -> StackGraphNodeAttributes:
    return {
        STACK_GRAPH_KIND: StackGraphNodeKind.JUMP_TO.value,
        STACK_GRAPH_SYMBOL: None,
        STACK_GRAPH_SCOPE: None,
        STACK_GRAPH_PRECEDENCE: 0,
        STACK_GRAPH_IS_EXPORTED: False,
        STACK_GRAPH_IS_REFERENCE: False,
        STACK_GRAPH_IS_DEFINITION: False,
    }


def drop_scopes_node_attributes() -> StackGraphNodeAttributes:
    return {
        STACK_GRAPH_KIND: StackGraphNodeKind.DROP_SCOPES.value,
        STACK_GRAPH_SYMBOL: None,
        STACK_GRAPH_SCOPE: None,
        STACK_GRAPH_PRECEDENCE: 0,
        STACK_GRAPH_IS_EXPORTED: False,
        STACK_GRAPH_IS_REFERENCE: False,
        STACK_GRAPH_IS_DEFINITION: False,
    }
