"""
The tool to check the availability or syntax of domains, IPv4, IPv6 or URL.

::


    ██████╗ ██╗   ██╗███████╗██╗   ██╗███╗   ██╗ ██████╗███████╗██████╗ ██╗     ███████╗
    ██╔══██╗╚██╗ ██╔╝██╔════╝██║   ██║████╗  ██║██╔════╝██╔════╝██╔══██╗██║     ██╔════╝
    ██████╔╝ ╚████╔╝ █████╗  ██║   ██║██╔██╗ ██║██║     █████╗  ██████╔╝██║     █████╗
    ██╔═══╝   ╚██╔╝  ██╔══╝  ██║   ██║██║╚██╗██║██║     ██╔══╝  ██╔══██╗██║     ██╔══╝
    ██║        ██║   ██║     ╚██████╔╝██║ ╚████║╚██████╗███████╗██████╔╝███████╗███████╗
    ╚═╝        ╚═╝   ╚═╝      ╚═════╝ ╚═╝  ╚═══╝ ╚═════╝╚══════╝╚═════╝ ╚══════╝╚══════╝

Provides the API core interface.

Author:
    Nissar Chababy, @funilrys, contactTATAfunilrysTODTODcom

Special thanks:
    https://pyfunceble.github.io/special-thanks.html

Contributors:
    https://pyfunceble.github.io/contributors.html

Project link:
    https://github.com/funilrys/PyFunceble

Project documentation:
    https://pyfunceble.readthedocs.io/en/dev/

Project homepage:
    https://pyfunceble.github.io/

License:
::


    MIT License

    Copyright (c) 2017, 2018, 2019, 2020 Nissar Chababy

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all
    copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    SOFTWARE.
"""

import PyFunceble

from .cli import CLICore


class APICore:
    """
    Provides the logic and interface for the tests from the API.

    :param str subject: The element we are testing.
    :param bool complete:
        Activate the return of a dictionnary with significant - if not all -
        data about the test.

    :param dict configuration:
        The custom configuration to load.

        .. note::
            This will let you overwrite any of the previously given
            configuration index.
    """

    # The subject we are working with.
    subject = None
    # Tell us if we have to return all possible data.
    complete = False
    # Saves the configuration.
    configuration = None

    def __init__(
        self,
        subject,
        complete=False,
        configuration=None,
        db_file_name="api_call",
        is_parent=True,
    ):
        # We share the subject.
        self.subject = subject

        # We share the complete option.
        self.complete = complete

        # We share the given configuration.
        self.configuration = configuration

        # We share the db file name.
        self.db_file_name = db_file_name

        # We load the global configuration
        # if it was not alreay done.
        PyFunceble.load_config(
            generate_directory_structure=False, custom=self.configuration
        )

        if (
            "api_config_loaded" not in PyFunceble.INTERN
            or self.configuration != PyFunceble.INTERN["api_config_loaded"]
        ):
            # We update the configuration with the given
            # configuration.
            preset = PyFunceble.cconfig.Preset()
            preset.init_all()
            preset.api()

            PyFunceble.INTERN["api_config_loaded"] = (
                self.configuration.copy()
                if isinstance(self.configuration, dict)
                else dict()
            )

        # We create an instance of the whois database.
        self.whois_db = PyFunceble.database.Whois(parent_process=is_parent)

        # We create an instance of the inactive database.
        self.inactive_db = PyFunceble.database.Inactive(
            db_file_name, parent_process=is_parent
        )

    def __inactive_database_management(self, subject, status):
        """
        Given the subject and status, we add or remove the subject
        from the inactive database.
        """

        if self.inactive_db.authorized:
            # We are authorized to operate with the
            # inactive database.s

            if status.lower() in PyFunceble.STATUS.list.up:
                # The status is in the list of UP status.

                # We remove it from the database.
                self.inactive_db.remove(subject)
            else:
                # The status is not in the list of UP status.

                # We add it into the database.
                self.inactive_db.add(subject, status)

    def reputation(self, subject_type):
        """
        Make a reputation check.

        :param str subject_type:
            Should be one of the following.

            - :code:`domain`

            - :code:`url`
        """

        if isinstance(self.subject, list):
            # The given subject is a list of subjects.

            # We initiate a variable which save our result.
            result = {}

            for subject in self.subject:
                # We loop through the list of subject.

                result[subject] = APICore(
                    subject, complete=self.complete, configuration=self.configuration
                ).reputation(subject_type)

            # We return our local result.
            return result

        if "url" in subject_type:
            data = PyFunceble.status.UrlReputation(
                self.subject, whois_db=self.whois_db, inactive_db=self.inactive_db
            ).get()
        else:
            data = PyFunceble.status.DomainAndIPReputation(
                self.subject, whois_db=self.whois_db, inactive_db=self.inactive_db
            ).get()

        self.__inactive_database_management(self.subject, data["status"])
        CLICore.save_into_database(data, self.db_file_name)

        if self.complete:
            # The user want a copy of the compelte data.

            # We return them
            return data

        # We only return the status.
        return data["status"]

    def availability(self, subject_type):
        """
        Make an availability check.

        :param str subject_type:
            Should be one of the following.

            - :code:`domain`

            - :code:`url`
        """

        if "url" in subject_type:
            return self.url()
        return self.domain_and_ip()

    def syntax(self, subject_type):
        """
        Make a syntax check.

        :param str subject_type:
            Should be one of the following.

            - :code:`domain`

            - :code:`url`
        """

        if isinstance(self.subject, list):
            # The given subject is a list of subjects.

            # We initiate a variable which save our result.
            result = {}

            for subject in self.subject:
                # We loop through the list of subject.

                result[subject] = APICore(
                    subject, complete=self.complete, configuration=self.configuration
                ).syntax(subject_type)

            # We return our local result.
            return result

        if "url" in subject_type:
            data = PyFunceble.status.UrlSyntax(
                self.subject, whois_db=self.whois_db, inactive_db=self.inactive_db
            ).get()
        else:
            data = PyFunceble.status.DomainAndIpSyntax(
                self.subject, whois_db=self.whois_db, inactive_db=self.inactive_db
            ).get()

        self.__inactive_database_management(self.subject, data["status"])
        CLICore.save_into_database(data, self.db_file_name)

        if self.complete:
            # The user want a copy of the compelte data.

            # We return them
            return data

        # We only return the status.
        return data["status"]

    def domain_and_ip(self):
        """
        Run a domain/IP availability check over the given subject.
        """

        if isinstance(self.subject, list):
            # The given subject is a list of subjects.

            # We initiate a variable which save our result.
            result = {}

            for subject in self.subject:
                # We loop through the list of subject.

                result[subject] = APICore(
                    subject, complete=self.complete, configuration=self.configuration
                ).domain_and_ip()

            # We return our local result.
            return result

        # We get the status of the given subject.
        data = PyFunceble.status.DomainAndIpAvailability(
            self.subject, whois_db=self.whois_db, inactive_db=self.inactive_db
        ).get()

        self.__inactive_database_management(self.subject, data["status"])
        CLICore.save_into_database(data, self.db_file_name)

        if self.complete:
            # The user want a copy of the compelte data.

            # We return them
            return data

        # We only return the status.
        return data["status"]

    def domain_syntax(self):
        """
        Run a domain syntax check over the given subject.
        """

        if isinstance(self.subject, list):
            # The given subject is a list of subject.

            # We return the validity of each subjects.
            return {
                subject: APICore(
                    subject, complete=self.complete, configuration=self.configuration
                ).domain_syntax()
                for subject in self.subject
            }

        # We return the validity of the the given subject.
        return PyFunceble.Check(self.subject).is_domain()

    def subdomain_syntax(self):
        """
        Run a subdomain syntax check over the given subject.
        """

        if isinstance(self.subject, list):
            # The given subjet is a list of subject.

            # We return the validity of each subjects.
            return {
                subject: APICore(
                    subject, complete=self.complete, configuration=self.configuration
                ).subdomain_syntax()
                for subject in self.subject
            }

        # We return the validity of the given subject.
        return PyFunceble.Check(self.subject).is_subdomain()

    def ipv4_syntax(self):
        """
        Run an IPv4 syntax check over the given subject.
        """

        if isinstance(self.subject, list):
            # The given subjet is a list of subject.

            # We return the validity of each subjects.
            return {
                subject: APICore(
                    subject, complete=self.complete, configuration=self.configuration
                ).ipv4_syntax()
                for subject in self.subject
            }

        # We return the validity of the given subject.
        return PyFunceble.Check(self.subject).is_ipv4()

    def ipv6_syntax(self):
        """
        Run an IPv6 syntax check over the given subject.
        """

        if isinstance(self.subject, list):
            # The given subject is a list of subject.

            # We return the validity of each subjects.
            return {
                subject: APICore(
                    subject, complete=self.complete, configuration=self.configuration
                ).ipv6_syntax()
                for subject in self.subject
            }

        # We return the validity of the given subject.
        return PyFunceble.Check(self.subject).is_ipv6()

    def ip_syntax(self):
        """
        Run an IP syntax check over the given subject.
        """

        if isinstance(self.subject, list):
            # The given subject is a list of subject.

            # We return the validity of each subjects.
            return {
                subject: APICore(
                    subject, complete=self.complete, configuration=self.configuration
                ).ip_syntax()
                for subject in self.subject
            }

        # We return the validity of the given subject.
        return PyFunceble.Check(self.subject).is_ip()

    def ipv4_range_syntax(self):
        """
        Run an IPv4 range syntax check over the given subject.
        """

        if isinstance(self.subject, list):
            # The given subjet is a list of subject.

            # We return the validity of each subjects.
            return {
                subject: APICore(
                    subject, complete=self.complete, configuration=self.configuration
                ).ipv4_range_syntax()
                for subject in self.subject
            }

        # We return the validity of the given subject.
        return PyFunceble.Check(self.subject).is_ipv4_range()

    def ipv6_range_syntax(self):
        """
        Run an IPv6 range syntax check over the given subject.
        """

        if isinstance(self.subject, list):
            # The given subjet is a list of subject.

            # We return the validity of each subjects.
            return {
                subject: APICore(
                    subject, complete=self.complete, configuration=self.configuration
                ).ipv6_range_syntax()
                for subject in self.subject
            }

        # We return the validity of the given subject.
        return PyFunceble.Check(self.subject).is_ipv6_range()

    def ip_range_syntax(self):
        """
        Run an IP range syntax check over the given subject.
        """

        if isinstance(self.subject, list):
            # The given subjet is a list of subject.

            # We return the validity of each subjects.
            return {
                subject: APICore(
                    subject, complete=self.complete, configuration=self.configuration
                ).ip_range_syntax()
                for subject in self.subject
            }

        # We return the validity of the given subject.
        return PyFunceble.Check(self.subject).is_ip_range()

    def url(self):
        """
        Run an URL availability check over the given subject.
        """

        if isinstance(self.subject, list):
            # The given subjet is a list of subject.

            # We initiate a local variable which will save
            # what we are going to return.
            result = {}

            for subject in self.subject:
                # We loop through the list of subjects.

                result[subject] = APICore(
                    subject, complete=self.complete, configuration=self.configuration
                ).url()

            # We return the result of each subjects.
            return result

        # We get the complete data about the status of the subject.
        data = PyFunceble.status.UrlAvailability(
            self.subject, whois_db=self.whois_db, inactive_db=self.inactive_db
        ).get()

        self.__inactive_database_management(self.subject, data["status"])
        CLICore.save_into_database(data, self.db_file_name)

        if self.complete:
            # The user want a complete copy of the data.

            # We return them.
            return data

        # We return the result of each subjects.
        return data["status"]

    def url_syntax(self):
        """
        Run an IPv4 syntax check over the given subject.
        """

        if isinstance(self.subject, list):
            # The given subjet is a list of subject.

            # We return the validity of each subjects.
            return {
                subject: APICore(
                    subject, complete=self.complete, configuration=self.configuration
                ).url_syntax()
                for subject in self.subject
            }

        # We return the validity of the subject.
        return PyFunceble.Check(self.subject).is_url()
