from typing import Dict, List, Optional, TypeVar

from grai_client.endpoints.client import BaseClient
from grai_client.schemas.edge import Edge
from grai_client.schemas.node import Node
from grai_client.schemas.schema import GraiType, Schema
from grai_client.schemas.utilities import merge_models

T = TypeVar("T", Node, Edge)


def deactivate(items: List[T]) -> List[T]:
    updated = [item.update({"spec": {"is_active": False}}) for item in items]
    return updated


def update(client: BaseClient, items: List[T], active_items: Optional[List[T]] = None):
    if not items:
        return

    if active_items is None:
        active_items: List[T] = client.get(items[0].type)
        if active_items is None:
            active_items = []

    current_item_map = {hash(item.spec): item for item in active_items}
    item_map: Dict[int, T] = {hash(item.spec): item for item in items}

    new_item_keys = new_items - active_items
    deactivated_item_keys = current_item_map.keys() - item_map.keys()
    updated_item_keys = item_map.keys() - new_item_keys

    new_items: List[T] = [item_map[k] for k in new_item_keys]
    updated_items = [
        merge_models(item_map[k], current_item_map[k])
        for k in updated_item_keys
        if item_map[k] != current_item_map[k]
    ]
    client.patch(deactivate(active_items - new_items))
    client.patch(updated_items)
    [new_items[k] for k in new_items.keys() - active_items.keys()]
    client.post(new_items - active_items)
