from dataclasses import dataclass
from typing import List, Optional, Union
from edgescan.data.types.object import Object

import hodgepodge.pattern_matching
import hodgepodge.time
import datetime


@dataclass(frozen=True)
class Vulnerability(Object):
    altered_score: bool
    asset_id: int
    asset_name: str
    confidence: int
    created_at: datetime.datetime
    updated_at: datetime.datetime
    cves: List[str]
    cvss_score: Optional[float]
    cvss_vector: Optional[str]
    cvss_version: Optional[str]
    cvss_v2_score: Optional[float]
    cvss_v2_vector: Optional[str]
    date_opened: datetime.datetime
    date_closed: datetime.datetime
    definition_id: int
    id: int
    label: Optional[str]
    last_pci_exception: Optional[str]
    layer: str
    location: str
    location_specifier_id: Optional[int]
    name: str
    pci_compliance_status: str
    risk: int
    severity: int
    status: str
    threat: int

    @property
    def create_time(self) -> datetime.datetime:
        return self.created_at

    @property
    def update_time(self) -> datetime.datetime:
        return self.updated_at

    @property
    def open_time(self) -> datetime.datetime:
        return self.date_opened

    @property
    def close_time(self) -> datetime.datetime:
        return self.date_closed

    def affects_pci_compliance(self) -> bool:
        return self.pci_compliance_status == 'fail'

    def is_network_layer_vulnerability(self) -> bool:
        return self.layer == 'network'

    def is_application_layer_vulnerability(self) -> bool:
        return self.layer == 'application'

    def matches(
            self,
            ids: Optional[List[int]] = None,
            names: Optional[List[str]] = None,
            cve_ids: Optional[List[str]] = None,
            locations: Optional[List[str]] = None,
            affects_pci_compliance: Optional[bool] = None,
            include_application_layer_vulnerabilities: Optional[bool] = None,
            include_network_layer_vulnerabilities: Optional[bool] = None,
            min_create_time: Optional[Union[str, int, float, datetime.datetime, datetime.date]] = None,
            max_create_time: Optional[Union[str, int, float, datetime.datetime, datetime.date]] = None,
            min_update_time: Optional[Union[str, int, float, datetime.datetime, datetime.date]] = None,
            max_update_time: Optional[Union[str, int, float, datetime.datetime, datetime.date]] = None,
            min_open_time: Optional[Union[str, int, float, datetime.datetime, datetime.date]] = None,
            max_open_time: Optional[Union[str, int, float, datetime.datetime, datetime.date]] = None,
            min_close_time: Optional[Union[str, int, float, datetime.datetime, datetime.date]] = None,
            max_close_time: Optional[Union[str, int, float, datetime.datetime, datetime.date]] = None,
            asset_ids: Optional[List[int]] = None):

        if ids and self.id not in ids:
            return False

        if names and not hodgepodge.pattern_matching.str_matches_glob(self.name, names):
            return False

        if asset_ids and self.asset_id not in asset_ids:
            return False

        if cve_ids and not hodgepodge.pattern_matching.str_matches_glob(self.cves, cve_ids):
            return False

        if locations and not hodgepodge.pattern_matching.str_matches_glob(self.location, locations):
            return False

        if affects_pci_compliance is not None and affects_pci_compliance != self.affects_pci_compliance():
            return False

        if include_application_layer_vulnerabilities is False and self.is_application_layer_vulnerability():
            return False

        if include_network_layer_vulnerabilities is False and self.is_network_layer_vulnerability():
            return False

        for timestamp, min_timestamp, max_timestamp in (
            (self.create_time, min_create_time, max_create_time),
            (self.update_time, min_update_time, max_update_time),
            (self.open_time, min_open_time, max_open_time),
            (self.close_time, min_close_time, max_close_time),
        ):
            if (min_timestamp or max_timestamp) and \
                    not hodgepodge.time.in_range(timestamp, min_timestamp, max_timestamp):
                return False
        return True

    def __hash__(self):
        return self.id
