"""
MolPortWebServices.py - library for accessing MolPort web services

Use of this code is subject to the terms of the StarDrop License Agreement and
use, copying or redistribution of this file for use by other persons or
organisations is prohibited

Copyright(C) Optibrium Ltd 2026
support@optibrium.com
"""

import requests

BASE_URL = "https://api.molport.com/api/"
VERIFY_CERT = 'gdig2.crt.pem'


class SearchTypes:
    """
    An "enum" class which defines several enumeration values and some static
    helper functions for use with this type
    """
    SUBSTRUCTURE, SUPERSTRUCTURE, EXACT, SIMILARITY, PERFECT, \
        EXACT_FRAGMENT = range(1, 7)

    def __init__(self):
        """
        Defined for PEP8, this class should not be instantiated at any point
        """
        raise RuntimeError("Enum class SearchTypes instantiated")

    @staticmethod
    def is_search_type(val):
        """
        This function determines whether a particular value is compatible with a
        SearchTypes value.

        Usage:
        >>> val1 = 1 # Equivalent to SearchTypes.Substructure
        >>> valid = SearchTypes.is_search_type(val1)
        >>> print valid # True
        >>> val3 = 9
        >>> valid = SearchTypes.is_search_type(val3)
        >>> print valid # False
        >>> val4 = list()
        >>> valid = SearchTypes.is_search_type(val4)
        >>> print valid # False

        :param val: The value to test to see if it is compatible with
                    SearchTypes
        :return: True if the value is compatible, false otherwise
        :rtype: Boolean
        """
        if val in range(1, 7):
            return True
        else:
            return False


class MolPortWebServices:
    def __init__(self, username, authentication_code):
        """
        Create a new instance of MolPortWebServices with the provided
        credentials

        :param username:            The username to use while accessing the API
        :param authentication_code: The password to use while accessing the API
        """
        self.username = username
        self.authentication_code = authentication_code
        self.session = requests.Session()

    def load_molecule(self, appui, molecule_id):
        """
        Load a molecule with the provided MolPort molecule ID

        (API method "Load molecule" - 3.1)

        :param appui:               The StarDrop appui object
        :param molecule_id: MolPort molecule ID e.g. MolPort compound ID 2325020 / MolPort-002-325-020
        :return: Detailed information about the specified molecule
        """
        if not self.authentication_code:
            return None

        result = None
        try:
            url = BASE_URL + f"molecule/load?molecule={molecule_id}" \
                             f"&username={self.username}&authenticationcode={self.authentication_code}"
            request = self.session.get(url, verify=VERIFY_CERT)
            result = result = request.json()
        except Exception as e:
            message = f'Unexpected error for MolPort load molecule request: {repr(e)}'
            print(message)
            appui.show(message)

        return result

    def chemical_search(self,
                        appui,
                        smiles: str,
                        search_type: int,
                        max_search_time: int,
                        max_result_count: int,
                        similarity_index: float):
        """
        Perform a chemical search based on the provided input SMILES using the
        specified search strategy. The input may be a full structure or a
        fragment based on the particular search type specified.

        :param appui:               The StarDrop appui object
        :param smiles:              The structure/fragment to search for
        :param search_type:         The chemical search type to use
        :param max_search_time:     Maximum time in milliseconds the search may run
        :param max_result_count:    Maximum number of results, capped at 1000 (default 1000)
        :param similarity_index:    If using a similarity search then this is
                                    used to specify similarity in the range 0-1
        :return: search result if any
        """
        if not self.authentication_code:
            return None

        result = None
        try:
            request_data = {
                "User Name": self.username,
                "Authentication Code": self.authentication_code,
                "Structure": smiles,
                "Search Type": search_type,
                "Maximum Search Time": max_search_time,
                "Maximum Result Count": max_result_count,
                "Chemical Similarity Index": similarity_index
            }

            url = BASE_URL + "chemical-search/search"
            request = self.session.post(url, json=request_data, verify=VERIFY_CERT)
            result = request.json()
        except Exception as e:
            message = f'Unexpected error processing chemical search: {repr(e)}'
            print(message)
            appui.show(message)

        return result
