import pytest

import tests.functional.services.policy_engine.utils.api as policy_engine_api
from tests.functional.services.policy_engine.conftest import is_legacy_provider
from tests.functional.services.policy_engine.vulnerability_data_tests.conftest import (
    IMAGE_DIGEST_MAP,
)
from tests.functional.services.utils import http_utils


@pytest.mark.parametrize(
    "image_digest",
    [
        "sha256:80a31c3ce2e99c3691c27ac3b1753163214494e9b2ca07bfdccf29a5cca2bfbe",  # alpine-test
        "sha256:406413437f26223183d133ccc7186f24c827729e1b21adc7330dd43fcdc030b3",  # debian-test
        "sha256:fe3ca35038008b0eac0fa4e686bd072c9430000ab7d7853001bde5f5b8ccf60c",  # centos-test
    ],
)
class TestVulnerabilityScanner:
    @pytest.mark.skipif(
        not is_legacy_provider(),
        reason="Grype workflow no longer testing policy engine in isolation",
    )
    def test_image_load_schema(self, image_digest, ingress_image, schema_validator):
        # ingress image and check that response is 200
        image_load_resp: http_utils.APIResponse = ingress_image(image_digest)
        assert image_load_resp == http_utils.APIResponse(200)
        # check that response schema matches expected format
        ingress_schema_validator = schema_validator("ingress_image.schema.json")
        is_valid: bool = ingress_schema_validator.is_valid(image_load_resp.body)
        assert is_valid, "\n".join(
            [str(e) for e in ingress_schema_validator.iter_errors(image_load_resp.body)]
        )
        # check that there are no errors in response
        assert len(image_load_resp.body["problems"]) == 0, image_load_resp.body[
            "problems"
        ]

    def test_get_vulnerabilities_schema(
        self, image_digest, setup_image, schema_validator
    ):
        # ingress image
        setup_image(image_digest)
        # now that image is ingressed, can query vulnerabilities and assert response is 200
        image_id = IMAGE_DIGEST_MAP[image_digest]["image_id"]
        images_vuln_resp: http_utils.APIResponse = (
            policy_engine_api.users.get_image_vulnerabilities(image_id)
        )
        assert images_vuln_resp == http_utils.APIResponse(200)

    def test_get_vulnerabilities_content(
        self,
        image_digest,
        setup_image,
        expected_content,
        is_legacy_test,
    ):
        # ingress the image and get the image id
        setup_image(image_digest)
        image_id = IMAGE_DIGEST_MAP[image_digest]["image_id"]
        # get the vulnerabilities for the image
        images_vuln_resp: http_utils.APIResponse = (
            policy_engine_api.users.get_image_vulnerabilities(image_id)
        )
        assert images_vuln_resp == http_utils.APIResponse(200)
        actual_report = images_vuln_resp.body

        # load the expected results which is just a list of vulnerability matches
        folder = "legacy" if is_legacy_test else "grype"

        expected_results = expected_content(f"{folder}/{image_digest}")
        # impose some order
        expected_results.sort(
            key=lambda x: (
                x["vulnerability"]["vulnerability_id"],
                x["vulnerability"]["feed_group"],
                x["artifact"]["name"],
                x["artifact"]["location"],
            )
        )

        # compare only the vulnerability matches
        actual_results = actual_report["results"]
        # impose same order
        actual_results.sort(
            key=lambda x: (
                x["vulnerability"]["vulnerability_id"],
                x["vulnerability"]["feed_group"],
                x["artifact"]["name"],
                x["artifact"]["location"],
            )
        )

        # check number of results
        assert len(expected_results) == len(actual_results)

        # check the actual content
        for index in range(len(expected_results)):
            # compare only the vulnerability and artifacts, skip match as it contains a dynamic date
            assert (
                expected_results[index]["vulnerability"]
                == actual_results[index]["vulnerability"]
            )

            # sort cpe array before asserting
            expected_results[index]["artifact"]["cpes"].sort()
            actual_results[index]["artifact"]["cpes"].sort()

            assert (
                expected_results[index]["artifact"] == actual_results[index]["artifact"]
            )
