import logging

import requests

from core.analytics import OneShotAnalytics
from core.config.config import yeti_config
from core.errors import GenericYetiError, ObservableValidationError
from core.observables import Hash, Hostname, Url, Ip


class MalwaresApi(object):
    """
    https://www.malwares.com/about/api
    """

    settings = {
        "malwares_api_key": {
            "name": "Malwares API Key",
            "description": "API Key provided by malwares.com.",
        }
    }

    API_URL = "https://public.api.malwares.com/v3/"

    @staticmethod
    def fetch(observable, params, uri):
        try:
            url = MalwaresApi.API_URL + uri
            response = requests.get(url, params=params, proxies=yeti_config.get('proxy'))
            if not response.ok:
                raise GenericYetiError("Status code: ".format())
            return response.json()
        except Exception as e:
            raise GenericYetiError(
                "Hit an error checking {},{}".format(observable.value, e)
            )


class IpReport(OneShotAnalytics, MalwaresApi):
    """
    https://www.malwares.com/about/api#IPInfo
    """

    default_values = {
        "group": "Malwares",
        "name": "Malwares Ip Report",
        "description": "Perform a IP lookup.",
    }

    ACTS_ON = ["Ip"]

    @staticmethod
    def analyze(observable, results):
        links = set()
        context = {
            "source": "malwares.com",
        }
        params = {
            "api_key": results.settings["malwares_api_key"],
            "ip": observable.value,
        }

        json_result = MalwaresApi.fetch(observable, params, "ip/info")
        for key in (
            "undetected_communicating_file",
            "detected_downloaded_file",
            "undetected_downloaded_file",
            "detected_communicating_file",
        ):
            for item in json_result[key]["list"]:
                h = Hash.get_or_create(value=item["sha256"])
                links.update(h.active_link_to(observable, key, context["source"]))
                h.add_context({"source": context["source"], "firs_seen": item["date"]})
            context[key] = json_result[key]["total"]

        for key in ("detected_url", "undetected_url"):
            for item in json_result[key]["list"]:
                url = Url.get_or_create(value=item["url"])
                links.update(url.active_link_to(observable, key, context["source"]))
                url.add_context(
                    {"source": context["source"], "firs_seen": item["date"]}
                )

            context[key] = json_result[key]["total"]
        hostname_history = json_result["hostname_history"]

        for item in hostname_history["list"]:
            try:
                hostname = Hostname.get_or_create(value=item["hostname"])
                links.update(
                    hostname.active_link_to(
                        observable, "hostname_history", context["source"]
                    )
                )
                hostname.add_context(
                    {"source": context["source"], "firs_seen": item["date"]}
                )
            except ObservableValidationError:
                logging.error("%s is not a hostname valid" % item["hostname"])

        context["hostname_history"] = hostname_history["total"]

        observable.add_context(context)
        return links


class HostnameReport(OneShotAnalytics, MalwaresApi):
    """
    https://www.malwares.com/about/api#IPInfo
    """

    default_values = {
        "group": "Malwares",
        "name": "Hostname Report",
        "description": "Perform a Hostname lookup.",
    }

    ACTS_ON = ["Hostname"]

    @staticmethod
    def analyze(observable, results):
        links = set()
        context = {
            "source": "malwares.com",
        }
        params = {
            "api_key": results.settings["malwares_api_key"],
            "hostname": observable.value,
        }

        json_result = MalwaresApi.fetch(observable, params, "hostname/info")
        if json_result:
            context["raw"] = json_result
            for key in (
                "undetected_communicating_file",
                "detected_downloaded_file",
                "undetected_downloaded_file",
                "detected_communicating_file",
            ):
                item = json_result[key]

                for f in item["list"]:
                    new_hash = Hash.get_or_create(value=f["sha256"])

                    links.update(
                        new_hash.active_link_to(observable, key, "malwares.com")
                    )
                context[key] = item["total"]

            for host in json_result.get("hostname_history", {}).get("list", []):
                new_host = Hostname.get_or_create(value=host)
                new_host.tag(observable.get_tags())
                links.update(
                    new_host.active_link_to(observable, "hostname", "malwares.com")
                )

            for key in ("detected_url", "undetected_url"):
                item = json_result[key]

                for i in item["list"]:
                    try:
                        new_url = Url.get_or_create(value=i["url"])
                        new_url.tag(observable.get_tags())
                        links.update(
                            new_url.active_link_to(observable, key, "malwares.com")
                        )
                    except ObservableValidationError:
                        logging.error("Url is not valid %s" % i["url"])
                context[key] = item["total"]
            ip_history = json_result["ip_history"]

            for i in ip_history["list"]:
                ip = Ip.get_or_create(value=i["ip"])
                links.update(ip.active_link_to(observable, "ip_story", "malwares.com"))
            context["ip_story"] = ip_history["total"]
        return links


class HashReport(OneShotAnalytics, MalwaresApi):
    """
    https://www.malwares.com/about/api#AddInfo
    """

    default_values = {
        "group": "Malwares",
        "name": "Hash Report",
        "description": "Perform a Hash lookup.",
    }

    ACTS_ON = ["Hash"]

    @staticmethod
    def analyze(observable, results):
        params = {
            "api_key": results.settings["malwares_api_key"],
            "hash": observable.value,
        }

        json_result = MalwaresApi.fetch(observable, params, "file/mwsinfo")
        links = set()
        context = {
            "source": "malwares.com",
        }
        if json_result:
            context["raw"] = json_result
            observable.tag(json_result["taglist"])

            if observable.family != "md5":
                hash_md5 = Hash.get_or_create(value=json_result["md5"])
                links.update(hash_md5.active_link_to(observable, "md5", "malwares.com"))
            if observable.family != "sha1":
                hash_sha1 = Hash.get_or_create(value=json_result["sha1"])
                links.update(
                    hash_sha1.active_link_to(observable, "sha1", "malwares.com")
                )
            if observable.family != "sha256":
                hash_sha256 = Hash.get_or_create(value=json_result["sha256"])
                links.update(
                    hash_sha256.active_link_to(observable, "sha256", "malwares.com")
                )

            if "virustotal" in json_result:
                vt = json_result["virustotal"]
                context["vt"] = "%s/%s" % (vt["positives"], vt["total"])
                context["scan_date"] = vt["scan_date"]

            observable.add_context(context)

        return links
