import hashlib

import pytest

from anchore_engine.db.entities.policy_engine import AnalysisArtifact, Image
from anchore_engine.services.policy_engine.engine.policy.gate import ExecutionContext
from anchore_engine.services.policy_engine.engine.policy.gates import malware

image_id = "1"
user = "admin"
digest = "1"

signature = "Unix.Trojan.MSShellcode-40"
signature2 = "Unix.Trojan.SomeMadeupValue-1"

findings = [
    {"path": "/elf_payload1", "signature": signature},
    {"path": "/home/someuser/file2", "signature": signature},
    {"path": "/var/lib/somebadlib/corrupted", "signature": signature2},
]


@pytest.fixture()
def image(monkeypatch):
    monkeypatch.setattr(
        Image, "analysis_artifacts", MockAnalysisArtifacts(), raising=True
    )

    img = Image()
    img.id = image_id
    img.digest = digest
    img.user_id = user
    return img


class MockAnalysisArtifacts:
    def __init__(
        self,
    ):
        artifact1 = AnalysisArtifact()
        artifact1.analyzer_id = "malware"
        artifact1.analyzer_artifact = "malware"
        artifact1.artifact_key = "clamav"
        artifact1.analyzer_type = "base"
        artifact1.image_id = image_id
        artifact1.image_user_id = user

        artifact1.json_value = {
            "name": "clamav",
            "findings": [],
            "metadata": {"db_version": {"daily": "1", "main": "1", "bytecode": "1"}},
        }

        self.artifacts = [artifact1]

    def __call__(self, *args, **kwargs):
        return self.artifacts

    def __iter__(self):
        return self.artifacts.__iter__()

    def filter(self, *args, **kwargs):
        a = self.artifacts

        class A:
            def all(self):
                return a

        return A()


@pytest.fixture()
def malware_gate():
    return malware.MalwareGate()


@pytest.fixture()
def scan_trigger(malware_gate):
    trigger = malware.ScanFindingsTrigger(parent_gate_cls=malware_gate.__class__)
    return trigger


@pytest.fixture()
def noscan_trigger(malware_gate):
    trigger = malware.ScanNotRunTrigger(parent_gate_cls=malware_gate.__class__)
    return trigger


@pytest.fixture()
def exec_context():
    return ExecutionContext(db_session=None, configuration={})


@pytest.mark.parametrize("finding", findings)
def test_malware_gate_single_finding(
    malware_gate, scan_trigger, exec_context, image, finding
):
    image.analysis_artifacts()[0].json_value["findings"] = [finding]

    malware_gate.prepare_context(image, exec_context)
    assert scan_trigger.execute(image, exec_context)
    assert scan_trigger.did_fire
    assert len(scan_trigger.fired) == 1
    assert scan_trigger.fired[0].id == "clamav+" + finding.get("signature") + "+" + str(
        hashlib.new(
            "md5", bytes(finding.get("path"), "utf-8"), usedforsecurity=False
        ).hexdigest()
    )


def test_malware_gate_multifinding(malware_gate, scan_trigger, exec_context, image):
    image.analysis_artifacts()[0].json_value["findings"] = findings

    malware_gate.prepare_context(image, exec_context)
    assert scan_trigger.execute(image, exec_context)
    assert scan_trigger.did_fire
    assert len(scan_trigger.fired) == len(findings)


def test_malware_gate_nofinding(malware_gate, scan_trigger, exec_context, image):
    image.analysis_artifacts.artifacts = []

    malware_gate.prepare_context(image, exec_context)
    assert scan_trigger.execute(image, exec_context)
    assert scan_trigger.did_fire is False


def test_malware_gate_nofinding_populated_context(
    malware_gate, scan_trigger, exec_context, image
):
    """
    Tests specific condition (issue-992) where the gate was using context incorrectly and could error out when
    no scans present but gate exec context had other keys present

    """
    image.analysis_artifacts.artifacts = []

    exec_context.data["something"] = ["foobar", "foo"]
    malware_gate.prepare_context(image, exec_context)
    assert scan_trigger.execute(image, exec_context)
    assert scan_trigger.did_fire is False


def test_malware_gate_noscan_trigger(malware_gate, noscan_trigger, exec_context, image):
    image.analysis_artifacts.artifacts = []

    malware_gate.prepare_context(image, exec_context)
    assert noscan_trigger.execute(image, exec_context)
    assert noscan_trigger.did_fire is True


def test_malware_gate_noscan_trigger_populated_context(
    malware_gate, noscan_trigger, exec_context, image
):
    image.analysis_artifacts.artifacts = []

    exec_context.data["something"] = ["foobar", "foo"]
    malware_gate.prepare_context(image, exec_context)
    assert noscan_trigger.execute(image, exec_context)
    assert noscan_trigger.did_fire is True


@pytest.mark.parametrize("finding", findings)
def test_malware_gate_noscan_trigger_with_findings(
    malware_gate, noscan_trigger, exec_context, image, finding
):
    image.analysis_artifacts()[0].json_value["findings"] = [finding]

    malware_gate.prepare_context(image, exec_context)
    assert noscan_trigger.execute(image, exec_context)
    assert noscan_trigger.did_fire is False
