from __future__ import annotations

import shortuuid
from typing_extensions import TypedDict

from solutions.graph.models import (
    EdgeGroupId,
    EdgeGroupMetadata,
    EdgeViewConfiguration,
    GraphMetadata,
    NodeGroupId,
    NodeGroupMetadata,
    NodeViewConfiguration,
    validate_edge_group,
    validate_node_group,
)
from stores.graph_metadata_store import GraphMetadataStore

from .group_mutation_models import (
    AddOrUpdateEdgeParams,
    AddOrUpdateNodeParams,
    to_edge_group_definition_metadata,
    to_node_group_definition_metadata,
)


class AddGroupResult(TypedDict):
    graph_meta: GraphMetadata
    version_token: str
    group_id: NodeGroupId | EdgeGroupId
    view_config: NodeViewConfiguration | EdgeViewConfiguration


def add_node_group(store: GraphMetadataStore, params: AddOrUpdateNodeParams) -> AddGroupResult:
    """
    Raises:
        ModelValidationError
        GraphDoesNotExistError
        ConcurrentUpdateCollisionError
        GraphMetadataStoreError
    """
    graph_id = params.graph_id
    graph_metadata = store.get(graph_id)

    # Map to model, validate and persist new configuration.
    node_id = shortuuid.uuid()[:6]
    node_group = params.meta.node_group

    # Generate an id for new definitions.
    for definition in params.meta.definitions:
        definition.definition_id = shortuuid.uuid()[:6]

    group_metadata = NodeGroupMetadata(
        node_id=node_id,
        node_group=node_group,
        definitions=to_node_group_definition_metadata(params.meta),
    )
    view_config = NodeViewConfiguration(
        color=params.view_config.color, size=params.view_config.size, icon=params.view_config.icon
    )
    validate_node_group(graph_metadata, group_metadata)

    graph_metadata["nodes"][node_id] = group_metadata
    graph_metadata["nodes_view"][node_id] = view_config
    new_graph_metadata = store.update(graph_metadata, params.ref_version_token)

    return AddGroupResult(
        group_id=node_id,
        version_token=new_graph_metadata["version_token"],
        graph_meta=graph_metadata,
        view_config=view_config,
    )


def add_edge_group(store: GraphMetadataStore, params: AddOrUpdateEdgeParams) -> AddGroupResult:
    """
    Raises:
        ModelValidationError
        GraphDoesNotExistError
        ConcurrentUpdateCollisionError
        GraphMetadataStoreError
    """
    graph_id = params.graph_id
    graph_metadata = store.get(graph_id)

    # Map to model, validate and persist new configuration.
    edge_id = shortuuid.uuid()[:6]

    # Generate an id for new definitions.
    for definition in params.meta.definitions:
        definition.definition_id = shortuuid.uuid()[:6]

    source_node_id = params.meta.source_node_id
    target_node_id = params.meta.target_node_id
    group_metadata = EdgeGroupMetadata(
        edge_id=edge_id,
        edge_group=params.meta.edge_group,
        source_node_id=source_node_id,
        target_node_id=target_node_id,
        definitions=to_edge_group_definition_metadata(params.meta),
    )
    edge_view = EdgeViewConfiguration(color=params.view_config.color, size=params.view_config.size)
    validate_edge_group(graph_metadata, group_metadata)

    graph_metadata["edges"][edge_id] = group_metadata
    graph_metadata["edges_view"][edge_id] = edge_view
    new_graph_metadata = store.update(graph_metadata, params.ref_version_token)

    return AddGroupResult(
        group_id=edge_id,
        version_token=new_graph_metadata["version_token"],
        graph_meta=graph_metadata,
        view_config=edge_view,
    )
