
# **********************************************************************
# This file has been auto-generated by a code generation tool.
# Version:   4.2.0
# Date/time: 22 Nov 2024 12:56:20
# Template:  templates/pythonpws/materials.razor.
# **********************************************************************

import urllib
import uuid

from marshmallow import EXCLUDE, fields, post_load, Schema, ValidationError

from .entities import Material, MaterialComponentData
from .entity_schemas import MaterialSchema, MaterialComponentDataSchema
from .utilities import get_request, post_request, put_request, get_materials_api_target, get_client_alias_id


class MaterialInfo:
    """
    MaterialInfo class.

    Attributes
    ----------
    id : uuid
        A unique identifier of the material.
    name : str
        The name of the material.
    """

    def __init__(self, id: uuid, name: str) -> None:
        """Initialises a new instance of the MaterialInfo class."""

        self.id = id
        self.name = name

class MaterialCasIdInfo:
    """
    MaterialCasIdInfo class.

    Attributes
    ----------
    id : uuid
        A unique identifier of the material.
    name : str
        The name of the material.
    cas_id : str
        The CAS ID of the material.
    """

    def __init__(self, id: uuid, name: str, cas_id: str) -> None:
        """Initialises a new instance of the MaterialInfo class."""

        self.id = id
        self.name = name
        self.cas_id = cas_id

class MaterialInfoSchema(Schema):
    """
    MaterialInfo schema.
    
    Attributes
    ----------
    id : uuid
        A unique identifier of the material.
    name : str
        The name of the material.
    
    Methods
    -------
    make_material_info(data, **kwargs)
        Post-processing of the MaterialInfoSchema class.
    """

    class Meta:
        unknown = EXCLUDE  # Exclude unknown fields

    id = fields.UUID()
    name = fields.Str(data_key='displayName')

    @post_load
    def make_material_info(self, data, **kwargs):
        """
        Post-processing of the MaterialInfoSchema class.
        
        Parameters
        ----------
        data

        Returns
        -------
        MaterialInfo
        """

        return MaterialInfo(**data)

class MaterialCasIdInfoSchema(Schema):
    """
    MaterialCasIdInfo schema.

    Attributes
    ----------
    id : uuid
        A unique identifier of the material.
    name : str
        The name of the material.
    cas_id : str
        The CAS ID of the material.

    Methods
    -------
    make_material_info(data, **kwargs)
        Post-processing of the MaterialInfoWithCasIdSchema class.
    """

    class Meta:
        unknown = EXCLUDE  # Exclude unknown fields

    id = fields.UUID()
    name = fields.Str()
    cas_id = fields.Str(data_key='casId')

    @post_load
    def make_material_info(self, data, **kwargs):
        """
        Post-processing of the MaterialInfoWithCasIdSchema class.

        Parameters
        ----------
        data

        Returns
        -------
        MaterialInfoWithCasId
        """

        return MaterialCasIdInfo(**data)

class MaterialEntityDescriptor:
    """
    MaterialEntityDescriptor class.

    Attributes
    ----------
    id : uuid
        A unique identifier of the material.
    name : str
        The name of the material.
    display_name : str
        The display name of the material.
    cas_id : str
        The CAS ID of the material.
    """

    def __init__(self, id: uuid, name: str, cas_id: str) -> None:
        """Initialises a new instance of the MaterialEntityDescriptor class."""

        self.id = id
        self.name = name
        self.display_name = name
        self.cas_id = cas_id

class MaterialEntityDescriptorSchema(Schema):
    """
    MaterialEntityDescriptor schema.

    Attributes
    ----------
    id : uuid
        A unique identifier of the material.
    name : str
        The name of the material.
    extended_properties : dict
        The extended properties of the material.

    Methods
    -------
    make_material_info(data, **kwargs)
        Post-processing of the MaterialEntityDescriptorSchema class.
    """

    class Meta:
        unknown = EXCLUDE  # Exclude unknown fields

    id = fields.UUID()
    name = fields.Str(data_key='displayName')
    extended_properties = fields.Dict(data_key='extendedProperties')

    @post_load
    def make_material_info(self, data, **kwargs):
        """
        Post-processing of the MaterialEntityDescriptorSchema class.

        Parameters
        ----------
        data

        Returns
        -------
        MaterialEntityDescriptor
        """

        # Add the cas_id to the data dictionary.
        data['cas_id'] = data['extended_properties']['casId']

        # Remove the extended_properties from the data dictionary.
        data.pop('extended_properties')

        # Return a new instance of the MaterialEntityDescriptor class.
        return MaterialEntityDescriptor(**data)

def get_data_from_url(url: str, schema, many=False):
    response = get_request(url)

    if not response.ok:
        raise ValueError('Failed to get data', response.status_code, response.text)

    try:
        data_schema = schema(many=many)
        data = data_schema.loads(response.text)

        return data

    except ValidationError as err:
        raise ValueError(f'Error retrieving data: {err.messages}')

# CAS Controller
def get_all_cas_ids() -> list[MaterialCasIdInfo]:
    """
    Gets a list containing the names of all available materials and their CAS IDs.

    Parameters
    ----------
    None

    Raises
    ------
    ValueError: Any communication error with the PWS APIs.

    Returns
    -------
    List[MaterialCasIdInfo]: The list of available materials names and their CAS IDs.
    """

    url = f'{get_materials_api_target()}cas?clientId={get_client_alias_id()}'
    return get_data_from_url(url, MaterialCasIdInfoSchema, many=True)

def get_material_by_cas_id(cas_id: str) -> Material:
    """
    Gets a material by CAS ID.

    Parameters
    ----------
    cas_id : str
        The CAS ID to search for.

    Raises
    ------
    ValueError: Any communication error with the PWS APIs or if a material with the supplied CAS ID cannot be found.

    Returns
    -------
    Material: The material which has the supplied CAS ID.
    """

    url = f'{get_materials_api_target()}materials/casid={cas_id}?clientId={get_client_alias_id()}'
    return get_data_from_url(url, MaterialSchema)

def get_components(source: int) -> list[MaterialComponentData]:
    url = f'{get_materials_api_target()}components?clientId={get_client_alias_id()}&sources={source}'
    return get_data_from_url(url, MaterialEntityDescriptorSchema, many=True)

def get_dnv_components() -> list[MaterialComponentData]:
    """
    Gets all of the DNV components.

    Parameters
    ----------
    None

    Raises
    ------
    ValueError: Any communication error with the PWS APIs.

    Returns
    -------
    list[MaterialComponentData]: A list of MaterialComponentData objects for each of the DNV components.
    """

    return get_components(1)

def get_dippr_components() -> list[MaterialComponentData]:
    """
    Gets all of the DIPPR components.

    Parameters
    ----------
    None

    Raises
    ------
    ValueError: Any communication error with the PWS APIs.

    Returns
    -------
    list[MaterialComponentData]: A list of MaterialComponentData objects for each of the DIPPR components.
    """

    return get_components(2)

def get_user_components() -> list[MaterialComponentData]:
    """
    Gets all of the user created components.

    Parameters
    ----------
    None

    Raises
    ------
    ValueError: Any communication error with the PWS APIs.

    Returns
    -------
    list[MaterialComponentData]: A list of MaterialComponentData objects for each of the user created components.
    """

    return get_components(3)

def get_component_by_id(id: uuid) -> MaterialComponentData:
    """
    Gets a material component using the id supplied.

    Parameters
    ----------
    id : str
        The id of the component

    Raises
    ------
    ValueError: Any communication error with the PWS APIs or if a component with the supplied id cannot be found.

    Returns
    -------
    MaterialComponentData: The material component with the supplied id.
    """

    url = f'{get_materials_api_target()}components/id={id}?clientId={get_client_alias_id()}'
    return get_data_from_url(url, MaterialComponentDataSchema)

def get_component_by_name(name: str) -> MaterialComponentData:
    """
    Gets a material component using the name supplied.

    Parameters
    ----------
    name : str
        The name of the component
        
    Raises
    ------
    ValueError: Any communication error with the PWS APIs or if a component with the supplied name cannot be found.

    Returns
    -------
    MaterialComponentData: The material component with the supplied name.
    """

    url = f'{get_materials_api_target()}components/name={urllib.parse.quote(name)}?clientId={get_client_alias_id()}'
    return get_data_from_url(url, MaterialComponentDataSchema)

def get_component_by_cas_id(cas_id: str) -> MaterialComponentData:
    """
    Gets a material component using the cas_id supplied.

    Parameters
    ----------
    cas_id : str
        The cas_id of the component

    Raises
    ------
    ValueError: Any communication error with the PWS APIs or if a component with the supplied cas_id cannot be found.

    Returns
    -------
    MaterialComponentData: The material component with the supplied cas_id.
    """

    url = f'{get_materials_api_target()}components/casid={urllib.parse.quote(cas_id)}?clientId={get_client_alias_id()}'
    return get_data_from_url(url, MaterialComponentDataSchema)

def store_data_and_get_response(url: str, data: MaterialComponentData, schema: Schema):
    """
    Stores the data and returns the response.

    Parameters
    ----------
    url : str
        The URL to store the data.
    data : MaterialComponentData
        The data to store.
    schema : Schema
        The schema to use.

    Raises
    ------
    ValueError: Any communication error with the PWS APIs.

    Returns
    -------
    MaterialComponentData: The stored data.
    """

    json_text = schema.dumps(data)
    response = post_request(url, json_text)

    if response.ok and response.status_code == 201:
        materialUrl = response.headers['location']
        response = get_request(materialUrl)

        if response.ok:
            return schema.loads(response.text)
    else:
        raise ValueError('Failed to store data', response.status_code, response.text)

def store_material_component_data(materialComponentData: MaterialComponentData) -> MaterialComponentData:
    """
    Stores the material component data.

    Parameters
    ----------
    materialComponentData : MaterialComponentData
        The material component data to store.

    Raises
    ------
    ValueError: Any communication error with the PWS APIs.

    Returns
    -------
    MaterialComponentData: The stored material component data.
    """

    url = f'{get_materials_api_target()}components?clientId={get_client_alias_id()}'
    return store_data_and_get_response(url, materialComponentData, MaterialComponentDataSchema())

def store_material_component_and_create_material(materialComponentData: MaterialComponentData) -> Material:
    """
    Stores the material component data and creates a material.

    Parameters
    ----------
    materialComponentData : MaterialComponentData
        The material component data to store.

    Raises
    ------
    ValueError: Any communication error with the PWS APIs.

    Returns
    -------
    Material: The stored material.
    """

    url = f'{get_materials_api_target()}components/material?clientId={get_client_alias_id()}'
    return store_data_and_get_response(url, materialComponentData, MaterialSchema())

def update_material_component(materialComponentData: MaterialComponentData) -> bool:
    """
    Updates the provided material component.

    Parameters
    ----------
    materialComponentData : MaterialComponentData
        The material component data to update.

    Raises
    ------
    ValueError: Any communication error with the PWS APIs.

    Returns
    -------
    If successful True otherwise False.
    """

    url = f'{get_materials_api_target()}components?clientId={get_client_alias_id()}'

    material_component_data_schema = MaterialComponentDataSchema()
    json_text = material_component_data_schema.dumps(materialComponentData)
    response = put_request(url, json_text)

    if response.ok and response.status_code == 204:
        return True
    else:
        raise ValueError('Failed to update material component', response.status_code, response.text)

def get_materials() -> list[Material]:
    """
    Gets the full detais of all materials.

    Parameters
    ----------
    None

    Raises
    ------
    ValueError: Any communication error with the PWS APIs.

    Returns
    -------
    List[Material]: The list of materials.
    """

    url = f'{get_materials_api_target()}materials?clientId={get_client_alias_id()}'
    return get_data_from_url(url, MaterialSchema, many=True)

def get_material_by_id(id: uuid) -> Material:
    """
    Gets a material by ID.

    Parameters
    ----------
    id : str
        The ID to search for.

    Raises
    ------
    ValueError: Any communication error with the PWS APIs or if a material with the supplied ID cannot be found.

    Returns
    -------
    Material: The material which has the supplied ID.
    """

    url = f'{get_materials_api_target()}materials/id={id}?clientId={get_client_alias_id()}'
    return get_data_from_url(url, MaterialSchema)

def get_material_by_name(name: str) -> Material:
    """
    Gets a material by name.

    Parameters
    ----------
    name : str
        The name to search for.

    Raises
    ------
    ValueError: Any communication error with the PWS APIs or if a material with the supplied name cannot be found.

    Returns
    -------
    Material: The material which has the supplied name.
    """

    url = f'{get_materials_api_target()}materials/name={name}?clientId={get_client_alias_id()}'
    return get_data_from_url(url, MaterialSchema)

def get_material_names() -> list[MaterialInfo]:
    """
    Gets all of the available material names and IDs.

    Parameters
    ----------
    None

    Raises
    ------
    ValueError: Any communication error with the PWS APIs.

    Returns
    -------
    list[MaterialInfo]: A list of MaterialInfo objects for all available materials.
    """

    url = f'{get_materials_api_target()}materials/descriptors?clientId={get_client_alias_id()}'
    return get_data_from_url(url, MaterialInfoSchema, many=True)