from __future__ import annotations

import time
from typing import List

import dataiku
import shortuuid
from dataikuapi.dss.project import DSSProject
from dataikuapi.dss.recipe import DSSRecipeListItem
from flask import Blueprint, request
from pydantic import BaseModel

from backends.editor.utils.api import use_graph_metadata_store, use_graph_snapshot_store
from backends.editor.utils.webapp_config import webapp_config
from backends.utils import return_ok
from solutions.graph.models import (
    FeatureUnavailableError,
    GraphDoesNotExistError,
    GraphMetadataSnapshot,
    SnapshotDoesNotExistError,
)
from stores.graph_metadata_snapshot_store import GraphMetadataSnapshotStore
from stores.graph_metadata_store import GraphMetadataStore

graph_snapshot = Blueprint("graph_snapshot", __name__, url_prefix="/snapshots")


class CreateSnapshotParams(BaseModel):
    name: str
    comment: str


@graph_snapshot.route("", methods=["GET"])
@use_graph_metadata_store
@use_graph_snapshot_store
def get_snapshots(graph_store: GraphMetadataStore, snapshot_store: GraphMetadataSnapshotStore):
    if not snapshot_store:
        raise FeatureUnavailableError(
            "Saved configuration feature is not available. You need to configure the saved configuration dataset."
        )

    graph_id = request.args.get("graphId", None)

    if not graph_id or not graph_store.exists(graph_id):
        raise GraphDoesNotExistError(graph_id)

    snapshots = snapshot_store.get_all_by_graph_id(graph_id)

    project = DSSProject(dataiku.api_client(), webapp_config.default_project_key)
    existing_recipes: List[DSSRecipeListItem] = project.list_recipes(as_type="listitems")  # type: ignore
    existing_recipe_names: List[str] = [r.name for r in existing_recipes]
    for snapshot in snapshots:
        recipe_name = f"build-graph-{snapshot['id']}"
        snapshot["is_published"] = recipe_name in existing_recipe_names  # type: ignore

    return return_ok({"snapshots": snapshots})


@graph_snapshot.route("", methods=["POST"])
@use_graph_metadata_store
@use_graph_snapshot_store
def create_snapshot(graph_store: GraphMetadataStore, snapshot_store: GraphMetadataSnapshotStore):
    if not snapshot_store:
        raise FeatureUnavailableError(
            "Saved configuration feature is not available. You need to configure the saved configurations dataset."
        )

    graph_id = request.args.get("graphId", None)

    if not graph_id or not (graph_meta := graph_store.get(graph_id)):
        raise GraphDoesNotExistError(graph_id)

    params = CreateSnapshotParams(**request.get_json())

    snapshot = GraphMetadataSnapshot(
        id=shortuuid.uuid()[:6],
        name=params.name,
        comment=params.comment,
        graph_id=graph_meta["id"],
        graph_name=graph_meta["name"],
        epoch_ms=int(time.time() * 1000),
        nodes={**graph_meta["nodes"]},
        edges={**graph_meta["edges"]},
        nodes_view={**graph_meta["nodes_view"]},
        edges_view={**graph_meta["edges_view"]},
        cypher_queries=[{**q} for q in graph_meta["cypher_queries"]],
    )

    snapshot_store.save(snapshot)

    return return_ok({"snapshot": snapshot})


@graph_snapshot.route("<id>", methods=["DELETE"])
@use_graph_snapshot_store
def delete_snapshot(snapshot_store: GraphMetadataSnapshotStore, id: str):
    if not snapshot_store:
        raise FeatureUnavailableError(
            "Saved configuration feature is not available. You need to configure the saved configurations dataset."
        )

    if not id or not snapshot_store.get_by_id(id):
        raise SnapshotDoesNotExistError(id)

    snapshot_store.delete(id)

    return return_ok()
