from urllib.parse import quote

from ..baseapi import BaseApi


class Metadata(BaseApi):
    def __init__(self, *args, **kwargs):
        """
        Initialize the endpoint.
        """
        self.endpoint = 'metadata'
        super().__init__(*args, **kwargs)

    def get(self, *args, **kwargs):
        """
        Get an entity. e.g. GET /path/to/endpoint/key
        """
        # Getting metadata not supported, use list/get_controlled_vocabulary instead
        from .. import WidenAttributeError
        raise WidenAttributeError

    def exists(self, *args, **kwargs):
        """
        Check if an entity exists.
        """
        # Checking if metadata exists not supported, use get_controlled_vocabulary instead
        from .. import WidenAttributeError
        raise WidenAttributeError

    def get_viewable_fields(self, field_types: str = 'all', display_name_after: str = None, display_name_starts_with: str = None, limit: int = 100) -> dict:
        """
        List all metadata fields that you have permission to see.

        :param field_types: The field type that you want to query.
        :param display_name_after: A filter that only returns metadata fields with display names alphabetically greater than this value.
        :param display_name_starts_with: A filter that only returns metadata fields with display names that start with this value.
        :param limit: The max number of metadata fields returned by this query. Must be between 1 and 100.
        """
        queryparams = {'field_types': field_types}

        if display_name_after is not None:
            queryparams.update({'display_name_after': display_name_after})
        if display_name_starts_with is not None:
            queryparams.update({'display_name_starts_with': display_name_starts_with})
        if limit is not None:
            queryparams.update({'limit': limit})

        response_json, _ = self._client._get(self._build_path(property='fields/viewable'))
        return response_json

    def list_controlled_vocabulary_values(self, display_key: str) -> dict:
        """
        List all values from the controlled vocabulary for a metadata field.

        :param display_key: Display key of the controlled vocabulary metadata field.
        """
        response_json, _ = self._client._get(self._build_path(property=f'{display_key}/vocabulary'))
        return response_json

    def add_controlled_vocabulary_value(self, display_key: str, value: str, index: int = None) -> dict:
        """
        Add a new value to the controlled vocabulary for a metadata field.

        :param display_key: Display key of the controlled vocabulary metadata field.
        :param value: The new controlled vocabulary value you wish to add.
        :param index: Optional index of where to insert the new controlled vocabulary value. If omitted, the new value will be added to the end of the list
        """
        data = {'value': value}

        if index is not None:
            data.update({'index': index})

        response_json, _ = self._client._post(self._build_path(property=f'{display_key}/vocabulary'), data=data)
        return response_json

    def get_controlled_vocabulary_value(self, display_key: str, existing_value: str) -> dict:
        """
        Get the existing value from the controlled vocabulary for a metadata field.
        :param display_key: Display key of the controlled vocabulary metadata field.
        :param existing_value: The controlled vocabulary value.
        """
        existing_value = quote(existing_value, safe='')  # encode possible slashes in vocabulary value
        response_json, _ = self._client._get(self._build_path(property=f'{display_key}/vocabulary/{existing_value}'))
        return response_json

    def get_controlled_vocabulary_value_exists(self, display_key: str, value: str) -> bool:
        """
        Check if value exists as a controlled vocabulary for a metadata field.
        :param display_key: Display key of the controlled vocabulary metadata field.
        :param value: The controlled vocabulary value.
        """
        from .. import WidenError
        from requests.exceptions import RequestException

        value = quote(value, safe='')  # encode possible slashes in vocabulary value

        try:
            _, status_code = self._client._get(self._build_path(property=f'{display_key}/vocabulary/{value}'))
        except (WidenError, RequestException):
            return False
        else:
            return status_code == 200

    def update_controlled_vocabulary_value(self, display_key: str, existing_value: str, value: str = None, index: int = None) -> dict:
        """
        Update an existing value from the controlled vocabulary for a metadata field.

        :param display_key: Display key of the controlled vocabulary metadata field.
        :param existing_value: The controlled vocabulary value you wish to update.
        :param value: Optional new display name for this controlled vocabulary value. Either value, index, or both must be provided.
        :param index: Optional index of where to re-order the updated controlled vocabulary value. Either value, index, or both must be provided.
        """
        data = {}

        if value is None and index is None:
            raise ValueError('Either value, index, or both must be provided.')
        if value is not None:
            data.update({'value': value})
        if index is not None:
            data.update({'index': index})

        existing_value = quote(existing_value, safe='')  # encode possible slashes in vocabulary value
        response_json, _ = self._client._put(self._build_path(property=f'{display_key}/vocabulary/{existing_value}'), data=data)
        return response_json

    def delete_controlled_vocabulary_value(self, display_key: str, existing_value: str) -> dict:
        """
        Delete an existing value from the controlled vocabulary for a metadata field.
        :param display_key: Display key of the controlled vocabulary metadata field.
        :param existing_value: The controlled vocabulary value you wish to delete.
        """
        existing_value = quote(existing_value, safe='')  # encode possible slashes in vocabulary value
        response_json, _ = self._client._delete(self._build_path(property=f'{display_key}/vocabulary/{existing_value}'))
        return response_json
