"""Defines the common interface for a vulnerability scanner"""
# This file is part of hoppr-cop
#
# Licensed under the MIT License;
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://opensource.org/licenses/MIT
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

# Copyright (c) 2022 Lockheed Martin Corporation
# pylint: disable=deprecated-module
import distutils.spawn
import os
from typing import Optional, Union

import typer
from hoppr_cyclonedx_models.cyclonedx_1_3 import (
    CyclonedxSoftwareBillOfMaterialSpecification as Bom_1_3,
)
from hoppr_cyclonedx_models.cyclonedx_1_4 import (
    CyclonedxSoftwareBillOfMaterialsStandard as Bom_1_4,
)
from hoppr_cyclonedx_models.cyclonedx_1_4 import Vulnerability
from packageurl import PackageURL


class VulnerabilityMeta(type):
    """a meta class to define interface expectations of a vulnerability scanner"""

    def __instancecheck__(cls, instance):
        # pylint: disable=no-value-for-parameter
        return cls.__subclasscheck__(type(instance))

    def __subclasscheck__(cls, subclass):
        return (
            hasattr(subclass, "get_vulnerabilities_by_purl")
            and callable(subclass.get_vulnerabilities_by_purl)
            and hasattr(subclass, "get_vulnerabilities_by_sbom")
            and callable(subclass.get_vulnerabilities_by_sbom)
            and hasattr(subclass, "should_activate")
            and callable(subclass.should_activate)
        )


class VulnerabilitySuper(metaclass=VulnerabilityMeta):
    """A super class that defines the expected interface for a vulnerability scanner"""

    required_tools_on_path = []
    required_environment_variables = []

    def get_vulnerabilities_by_purl(
        self, purls: list[PackageURL]
    ) -> dict[str, Optional[list[Vulnerability]]]:
        """Get the vulnerabilities for a list of package URLS (purls)
        This function will return a dictionary of package URL to vulnerabilities or none if no vulnerabilities are found
        """

    def get_vulnerabilities_by_sbom(
        self, bom: [Union[Bom_1_4, Bom_1_3]]
    ) -> dict[str, Optional[list[Vulnerability]]]:
        """Accepts a cyclone dx compatible BOM and returns a list of vulnerabilities "
        This function will return a dictionary of package URL to vulnerabilities or none if no vulnerabilities are found
        """
        purls = []
        for component in bom.components:
            if component.purl is not None and component.purl != "":
                purls.append(PackageURL.from_string(component.purl))
        return self.get_vulnerabilities_by_purl(purls)

    def should_activate(self) -> bool:
        """checks if the vulnerability scanner should activate based on it's requirements"""
        activate = True
        for tool in self.required_tools_on_path:
            if not distutils.spawn.find_executable(tool):
                activate = False
                typer.echo(
                    f"{type(self).__name__} is not activated because {tool} is not on the PATH"
                )
        for env in self.required_environment_variables:
            if env not in os.environ:
                activate = False
                typer.echo(
                    f"{type(self).__name__} is not activated because required environment variable {env} is not set"
                )
        return activate
