from __future__ import annotations

from dataclasses import dataclass, asdict
from typing import Any, Dict, List, Tuple


@dataclass
class GraphNode:
    id: str
    labels: List[str]
    name: str | None
    layer: str | None
    properties: Dict[str, Any]


@dataclass
class GraphEdge:
    id: str
    type: str
    source: str
    target: str
    properties: Dict[str, Any]


def _safe_get(obj: Any, attr: str, default: Any = None) -> Any:
    return getattr(obj, attr, default)


def map_to_graph(model: Any) -> Tuple[List[Dict[str, Any]], List[Dict[str, Any]]]:
    """
    Convert a pyArchimate model into a generic node/edge graph representation.

    The mapping is intentionally minimal and lossy on view-level information:

    - Elements -> Graph nodes
      - labels: ["ArchimateElement", element.type]
      - name: element.name
      - layer: element.layer (if present)
      - properties: element.properties (dict-like, if present)

    - Relationships -> Graph edges
      - type: relationship.type
      - source: relationship.source (element id)
      - target: relationship.target (element id)
      - properties: relationship.properties (dict-like, if present)
    """
    nodes: List[GraphNode] = []
    edges: List[GraphEdge] = []

    # Elements -> nodes
    for element in getattr(model, "elements", []):
        element_id = str(_safe_get(element, "id"))
        element_type = str(_safe_get(element, "type") or "Unknown")
        name = _safe_get(element, "name")
        layer = _safe_get(element, "layer")
        props = _safe_get(element, "properties", {}) or {}

        node = GraphNode(
            id=element_id,
            labels=["ArchimateElement", element_type],
            name=name,
            layer=layer,
            properties=dict(props),
        )
        nodes.append(asdict(node))

    # Relationships -> edges
    for rel in getattr(model, "relationships", []):
        rel_id = str(_safe_get(rel, "id"))
        rel_type = str(_safe_get(rel, "type") or "Unknown")
        source = str(_safe_get(rel, "source"))
        target = str(_safe_get(rel, "target"))
        props = _safe_get(rel, "properties", {}) or {}

        edge = GraphEdge(
            id=rel_id,
            type=rel_type,
            source=source,
            target=target,
            properties=dict(props),
        )
        edges.append(asdict(edge))

    return nodes, edges

