"""CSAF Vulnerability model."""
from __future__ import annotations

from datetime import datetime
from enum import Enum
from typing import Annotated, List, Optional, Union, no_type_check

from pydantic import AnyUrl, BaseModel, Field, validator

from turvallisuusneuvonta.csaf.cvss.cvss import CVSS2, CVSS30, CVSS31
from turvallisuusneuvonta.csaf.definitions import Acknowledgments, Id, Notes, ProductGroupIds, Products, References
from turvallisuusneuvonta.csaf.product import ProductStatus


class Cwe(BaseModel):
    """
    Holds the MITRE standard Common Weakness Enumeration (CWE) for the weakness associated.
    """

    id: Annotated[
        str,
        Field(
            description='Holds the ID for the weakness associated.',
            examples=['CWE-22', 'CWE-352', 'CWE-79'],
            regex='^CWE-[1-9]\\d{0,5}$',
            title='Weakness ID',
        ),
    ]
    name: Annotated[
        str,
        Field(
            description='Holds the full name of the weakness as given in the CWE specification.',
            examples=[
                'Cross-Site Request Forgery (CSRF)',
                "Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')",
                "Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')",
            ],
            min_length=1,
            title='Weakness name',
        ),
    ]


class PartyCategory(Enum):
    """
    Defines the category of the involved party.
    """

    coordinator = 'coordinator'
    discoverer = 'discoverer'
    other = 'other'
    user = 'user'
    vendor = 'vendor'


class PartyStatus(Enum):
    """
    Defines contact status of the involved party.
    """

    completed = 'completed'
    contact_attempted = 'contact_attempted'
    disputed = 'disputed'
    in_progress = 'in_progress'
    not_contacted = 'not_contacted'
    open = 'open'


class Involvement(BaseModel):
    """
    Is a container, that allows the document producers to comment on the level of involvement (or engagement)
    of themselves or third parties in the vulnerability identification, scoping, and remediation process.
    """

    date: Annotated[
        Optional[datetime],
        Field(
            description='Holds the date and time of the involvement entry.',
            title='Date of involvement',
        ),
    ] = None
    party: Annotated[
        PartyCategory,
        Field(
            description='Defines the category of the involved party.',
            title='Party category',
        ),
    ]
    status: Annotated[
        PartyStatus,
        Field(
            description='Defines contact status of the involved party.',
            title='Party status',
        ),
    ]
    summary: Annotated[
        Optional[str],
        Field(
            description='Contains additional context regarding what is going on.',
            min_length=1,
            title='Summary of the involvement',
        ),
    ] = None


class RemediationCategory(Enum):
    """
    Specifies the category which this remediation belongs to.
    """

    mitigation = 'mitigation'
    no_fix_planned = 'no_fix_planned'
    none_available = 'none_available'
    vendor_fix = 'vendor_fix'
    workaround = 'workaround'


class Entitlement(BaseModel):
    __root__: Annotated[
        str,
        Field(
            description='Contains any possible vendor-defined constraints for obtaining fixed software or hardware'
            'that fully resolves the vulnerability.',
            min_length=1,
            title='Entitlement of the remediation',
        ),
    ]


class RestartRequiredCategory(Enum):
    """
    Specifies what category of restart is required by this remediation to become effective.
    """

    connected = 'connected'
    dependencies = 'dependencies'
    machine = 'machine'
    none = 'none'
    parent = 'parent'
    service = 'service'
    system = 'system'
    vulnerable_component = 'vulnerable_component'
    zone = 'zone'


class RestartRequired(BaseModel):
    """
    Provides information on category of restart is required by this remediation to become effective.
    """

    category: Annotated[
        RestartRequiredCategory,
        Field(
            description='Specifies what category of restart is required by this remediation to become effective.',
            title='Category of restart',
        ),
    ]
    details: Annotated[
        Optional[str],
        Field(
            description='Provides additional information for the restart. This can include details on procedures,'
            'scope or impact.',
            min_length=1,
            title='Additional restart information',
        ),
    ]


class ThreatCategory(Enum):
    """
    Categorizes the threat according to the rules of the specification.
    """

    exploit_status = 'exploit_status'
    impact = 'impact'
    target_set = 'target_set'


class AccessVectorType(Enum):
    network = 'NETWORK'
    adjacent_network = 'ADJACENT_NETWORK'
    local = 'LOCAL'


class AccessComplexityType(Enum):
    high = 'HIGH'
    medium = 'MEDIUM'
    low = 'LOW'


class AuthenticationType(Enum):
    multiple = 'MULTIPLE'
    single = 'SINGLE'
    none = 'NONE'


class CiaType(Enum):
    none = 'NONE'
    partial = 'PARTIAL'
    complete = 'COMPLETE'


class ExploitabilityType(Enum):
    unproven = 'UNPROVEN'
    proof_of_concept = 'PROOF_OF_CONCEPT'
    functional = 'FUNCTIONAL'
    high = 'HIGH'
    not_defined = 'NOT_DEFINED'


class RemediationLevelType(Enum):
    official_fix = 'OFFICIAL_FIX'
    temporary_fix = 'TEMPORARY_FIX'
    workaround = 'WORKAROUND'
    unavailable = 'UNAVAILABLE'
    not_defined = 'NOT_DEFINED'


class ReportConfidenceType(Enum):
    unconfirmed = 'UNCONFIRMED'
    uncorroborated = 'UNCORROBORATED'
    confirmed = 'CONFIRMED'
    not_defined = 'NOT_DEFINED'


class CollateralDamagePotentialType(Enum):
    none = 'NONE'
    low = 'LOW'
    low_medium = 'LOW_MEDIUM'
    medium_high = 'MEDIUM_HIGH'
    high = 'HIGH'
    not_defined = 'NOT_DEFINED'


class TargetDistributionType(Enum):
    none = 'NONE'
    low = 'LOW'
    medium = 'MEDIUM'
    high = 'HIGH'
    not_defined = 'NOT_DEFINED'


class CiaRequirementType(Enum):
    low = 'LOW'
    medium = 'MEDIUM'
    high = 'HIGH'
    not_defined = 'NOT_DEFINED'


class ScoreType(BaseModel):
    value: Annotated[float, Field(ge=0.0, le=10.0)]


class AttackVectorType(Enum):
    network = 'NETWORK'
    adjacent_network = 'ADJACENT_NETWORK'
    local = 'LOCAL'
    physical = 'PHYSICAL'


class ModifiedAttackVectorType(Enum):
    network = 'NETWORK'
    adjacent_network = 'ADJACENT_NETWORK'
    local = 'LOCAL'
    physical = 'PHYSICAL'
    not_defined = 'NOT_DEFINED'


class AttackComplexityType(Enum):
    high = 'HIGH'
    low = 'LOW'


class ModifiedAttackComplexityType(Enum):
    high = 'HIGH'
    low = 'LOW'
    not_defined = 'NOT_DEFINED'


class PrivilegesRequiredType(Enum):
    high = 'HIGH'
    low = 'LOW'
    none = 'NONE'


class ModifiedPrivilegesRequiredType(Enum):
    high = 'HIGH'
    low = 'LOW'
    none = 'NONE'
    not_defined = 'NOT_DEFINED'


class UserInteractionType(Enum):
    none = 'NONE'
    required = 'REQUIRED'


class ModifiedUserInteractionType(Enum):
    none = 'NONE'
    required = 'REQUIRED'
    not_defined = 'NOT_DEFINED'


class ScopeType(Enum):
    unchanged = 'UNCHANGED'
    changed = 'CHANGED'


class ModifiedScopeType(Enum):
    unchanged = 'UNCHANGED'
    changed = 'CHANGED'
    not_defined = 'NOT_DEFINED'


class CiaTypeModel(Enum):
    none = 'NONE'
    low = 'LOW'
    high = 'HIGH'


class ModifiedCiaType(Enum):
    none = 'NONE'
    low = 'LOW'
    high = 'HIGH'
    not_defined = 'NOT_DEFINED'


class ConfidenceType(Enum):
    unknown = 'UNKNOWN'
    reasonable = 'REASONABLE'
    confirmed = 'CONFIRMED'
    not_defined = 'NOT_DEFINED'


class ScoreTypeModel(ScoreType):
    pass


class SeverityType(Enum):
    none = 'NONE'
    low = 'LOW'
    medium = 'MEDIUM'
    high = 'HIGH'
    critical = 'CRITICAL'


class ScoreTypeModel1(ScoreType):
    pass


class Remediation(BaseModel):
    """
    Specifies details on how to handle (and presumably, fix) a vulnerability.
    """

    category: Annotated[
        RemediationCategory,
        Field(
            description='Specifies the category which this remediation belongs to.',
            title='Category of the remediation',
        ),
    ]
    date: Annotated[
        Optional[datetime],
        Field(
            description='Contains the date from which the remediation is available.',
            title='Date of the remediation',
        ),
    ]
    details: Annotated[
        str,
        Field(
            description='Contains a thorough human-readable discussion of the remediation.',
            min_length=1,
            title='Details of the remediation',
        ),
    ]
    entitlements: Annotated[
        Optional[List[Entitlement]],
        Field(
            description='Contains a list of entitlements.',
            min_items=1,
            title='List of entitlements',
        ),
    ]
    group_ids: Optional[ProductGroupIds]
    product_ids: Optional[Products]
    restart_required: Annotated[
        Optional[RestartRequired],
        Field(
            description='Provides information on category of restart is required by this remediation to'
            ' become effective.',
            title='Restart required by remediation',
        ),
    ]
    url: Annotated[
        Optional[AnyUrl],
        Field(
            description='Contains the URL where to obtain the remediation.',
            title='URL to the remediation',
        ),
    ]

    @no_type_check
    @validator('entitlements')
    def check_len(cls, v):
        if not v:
            raise ValueError('optional element present but empty')
        return v


class Threat(BaseModel):
    """
    Contains the vulnerability kinetic information. This information can change as the vulnerability ages and new
    information becomes available.
    """

    category: Annotated[
        ThreatCategory,
        Field(
            description='Categorizes the threat according to the rules of the specification.',
            title='Category of the threat',
        ),
    ]
    date: Annotated[
        Optional[datetime],
        Field(
            description='Contains the date when the assessment was done or the threat appeared.',
            title='Date of the threat',
        ),
    ]
    details: Annotated[
        str,
        Field(
            description='Represents a thorough human-readable discussion of the threat.',
            min_length=1,
            title='Details of the threat',
        ),
    ]
    group_ids: Optional[ProductGroupIds]
    product_ids: Optional[Products]


class Score(BaseModel):
    """
    specifies information about (at least one) score of the vulnerability and for
    which products the given value applies.
    """

    cvss_v2: Optional[CVSS2] = None
    cvss_v3: Optional[Union[CVSS30, CVSS31]] = None
    products: Products


class Vulnerability(BaseModel):
    """
    Is a container for the aggregation of all fields that are related to a single vulnerability in the document.
    """

    acknowledgments: Annotated[
        Optional[Acknowledgments],
        Field(
            description='Contains a list of acknowledgment elements associated with this vulnerability item.',
            title='Vulnerability acknowledgments',
        ),
    ]
    cve: Annotated[
        Optional[str],
        Field(
            description='Holds the MITRE standard Common Vulnerabilities and Exposures (CVE) tracking number for'
            ' the vulnerability.',
            regex='^CVE-[0-9]{4}-[0-9]{4,}$',
            title='CVE',
        ),
    ]
    cwe: Annotated[
        Optional[Cwe],
        Field(
            description='Holds the MITRE standard Common Weakness Enumeration (CWE) for the weakness associated.',
            title='CWE',
        ),
    ]
    discovery_date: Annotated[
        Optional[datetime],
        Field(
            description='Holds the date and time the vulnerability was originally discovered.',
            title='Discovery date',
        ),
    ]
    id: Annotated[
        Optional[Id],
        Field(
            description='Gives the document producer a place to publish a unique label or tracking ID for the'
            ' vulnerability (if such information exists).',
            title='ID',
        ),
    ]
    involvements: Annotated[
        Optional[List[Involvement]],
        Field(
            description='Contains a list of involvements.',
            min_items=1,
            title='List of involvements',
        ),
    ]
    notes: Annotated[
        Optional[Notes],
        Field(
            description='Holds notes associated with this vulnerability item.',
            title='Vulnerability notes',
        ),
    ]
    product_status: Annotated[
        Optional[ProductStatus],
        Field(
            description='Contains different lists of product_ids which provide details on the status of the'
            ' referenced product related to the current vulnerability. ',
            title='Product status',
        ),
    ]
    references: Annotated[
        Optional[References],
        Field(
            description='Holds a list of references associated with this vulnerability item.',
            title='Vulnerability references',
        ),
    ]
    release_date: Annotated[
        Optional[datetime],
        Field(
            description='Holds the date and time the vulnerability was originally released into the wild.',
            title='Release date',
        ),
    ]
    remediations: Annotated[
        Optional[List[Remediation]],
        Field(
            description='Contains a list of remediations.',
            min_items=1,
            title='List of remediations',
        ),
    ]
    scores: Annotated[
        Optional[List[Score]],
        Field(
            description='contains score objects for the current vulnerability.',
            min_items=1,
            title='List of scores',
        ),
    ]
    threats: Annotated[
        Optional[List[Threat]],
        Field(
            description='Contains information about a vulnerability that can change with time.',
            min_items=1,
            title='List of threats',
        ),
    ]
    title: Annotated[
        Optional[str],
        Field(
            description='Gives the document producer the ability to apply a canonical name or title to'
            ' the vulnerability.',
            min_length=1,
            title='Title',
        ),
    ]

    @no_type_check
    @validator('involvements', 'remediations', 'scores', 'threats')
    def check_len(cls, v):
        if not v:
            raise ValueError('optional element present but empty')
        return v
