import graphene
from graphene_django import DjangoObjectType
from typing import List, Dict, Type
from django.db import models
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
from django_relay_endpoint.configurators.object_types import DjangoClientIDMutation
from django_relay_endpoint.configurators.mutation_configurators.input_object_type_configurator import configure_input_object_type
from graphql_relay.node.node import from_global_id




def configure_update_mutation(
        input_object_type: Type[graphene.InputObjectType],
        abstract_mutation_type: Type[DjangoClientIDMutation], 
        conventional_name: str,
        input_field_name: str = None,
        return_field_name: str = None,
        success_keyword: str = None,
        ) -> Type[DjangoClientIDMutation]:
    """
    Configures a DjangoClientIDMutation class named <conventional_name>UpdateMutation.
    The mutation extends abstract_mutation_type and implements 'mutate_and_get_payload' method, as well as Input class with input_field.

    Args:
        input_object_type (Type[graphene.InputObjectType]): 
        the InputObjectType class implementation generated by input_object_type_configurator

        abstract_mutation_type (Type[DjangoClientIDMutation]): 
        the abstract DjangoClientIDMutation class implementation generated by abstract_mutation_class_configurator

        conventional_name (str): 
        the conventional name prefixed to the final DjangoClientIDMutation class name.

        input_field_name (str, optional): 
        the input field name. Defaults to "data".

        return_field_name (str, optional): 
        the return field name. Defaults to model._meta.model_name.

    Raises:
        ValidationError: a validation error if id is provided.

    Returns:
        Type[DjangoClientIDMutation]: the actual DjangoClientIDMutation type.
    """

    @classmethod
    def mutate_and_get_payload(cls, root, info, *args, **kwargs):
        """
        The `mutate_and_get_payload` classmethod, which gets id using `graphql_relay.node.node.from_global_id`.
        See <https://docs.graphene-python.org/projects/django/en/latest/mutations/#relay> for `mutate_and_get_payload`.
        """

        model = abstract_mutation_type.model
        data = kwargs.get(input_field_name or "data", None)
        client_mutation_id = kwargs.get("client_mutation_id", None)
        unresolved_id = data.get("id", None)
        # raise a specific error if no id was provided
        if not unresolved_id:
            raise ValidationError(_("You must provide the id of the instance being mutated."))
        id = from_global_id(unresolved_id).id
        instance = cls.get_node(info, id)
        cls.validate(data, instance, info)
        cls.update_instance(instance, data)
        instance.save()
        mutation_kwargs = {
            return_field_name or model._meta.model_name: instance,
            success_keyword or "success": True,
            'client_mutation_id': client_mutation_id
        }
        return cls(**mutation_kwargs)

    # add id as a required input field
    UpdateInputObjectType = type(input_object_type.__name__, (input_object_type,), {
        # add the id scalar field
        "id": graphene.ID(required=True)
    })

    Input = type("Input", (), {
        input_field_name or "data" : graphene.Field(UpdateInputObjectType)
    })

    # configure the UpdateMutation
    mutation = type(f'{conventional_name}UpdateMutation', (abstract_mutation_type,), {
        "Input": Input,
        "mutate_and_get_payload": mutate_and_get_payload,
    })
    return mutation



