from neo4jclient.AbsClient import AbstractClient


class WebCheckerClient(AbstractClient):
    def __init__(self, password, **kwargs):
        super().__init__(password=password, **kwargs)

    def get_ip_and_domain_names(self):
        """
        Gets all IPs and corresponding domain names from database.

        :return: IPs and domain names in JSON-like form
        """
        return self._run_query("MATCH(n:IP)-[:RESOLVES_TO]-(y:DomainName {tag: \'A/AAAA\'}) "
                               "RETURN { IP: n.address , Domain: y.domain_name } AS entry")

    def upload_cert_errors(self, path):
        """
        Upload cert errors to db.
        1. Find corresponding Domain names which are defined in json file.
        2. Find IPs connected to such Domain names.
        3. If there is no SecurityEvent with parameters from json file - Event is created with values from file.
            Otherwise, only event detection_time and confirmed attributes are updated.
        4. Relationship between IP and Sec event is created if it doesn't exists.

        :param path: Path to the json with information about certs.
        :return: None
        """

        path = f'file:///{path}'

        query = "CALL apoc.load.json($path) " \
                "YIELD value " \
                "UNWIND value.data AS data " \
                "WITH value.time AS time, data.description AS description, data.hostname AS domainName, " \
                "data.type AS type, data.confirmed AS confirmed " \
                "MATCH(dom:DomainName {domain_name:domainName, tag: \'A/AAAA\'}) " \
                "MATCH(ip:IP)-[:RESOLVES_TO]->(dom) " \
                "MERGE(sec:SecurityEvent {type:type, description:description, " \
                "detection_time: datetime(time), confirmed:confirmed, hostname: domainName}) " \
                "MERGE (ip)-[:SOURCE_OF]->(sec) " \
                "RETURN \'success\'"

        params = {'path': path}

        self._run_query(query, **params)

    def upload_hostnames(self, path):
        """
        Upload hostnames to db.
        1. Find corresponding Domain names which are defined in json file.
            For every domain name:
                a) if such domain name is in database, check if connected IP is valid, update if not.
                b) if it is not, add it to db and connect to IP address.

        :param path: Path to the json file with information about hostnames.
        :return: None
        """

        path = f'file:///{path}'

        query = "CALL apoc.load.json($path) " \
                "YIELD value " \
                "UNWIND value.data AS tuple " \
                "WITH tuple.hostname AS hostname, tuple.ip AS ipaddress, value.time AS time " \
                "MERGE (domain:DomainName {domain_name: hostname, tag: \'A/AAAA\'}) " \
                "MERGE (a:IP { address: ipaddress }) " \
                "MERGE (a)-[r:RESOLVES_TO]-(domain) " \
                "ON MATCH SET r.last_detected = datetime(time) " \
                "ON CREATE SET r.last_detected = datetime(time)"

        params = {'path': path}

        self._run_query(query, **params)

    def get_active_domains(self):
        """
        Get active domains from database.
        Active domain is every entry which was detected in last 24 hours (based on last_detected).
        Only considering domains generated by webchecker component.

        :return: List with domain names.
        """

        query = "WITH datetime() - duration('PT24H') AS poptime " \
                "MATCH (:IP)-[n:RESOLVES_TO]->(domain:DomainName {tag: 'A/AAAA'})  " \
                "WHERE n.last_detected > poptime " \
                "RETURN domain"

        return self._run_query(query)
