# -*- coding: utf-8 -*-
# pylint: disable=line-too-long,missing-docstring,reimported,unused-import,unused-variable
import re

import orjson
import pytest
from pydantic.error_wrappers import ValidationError

import turvallisuusneuvonta.csaf.product as product
import turvallisuusneuvonta.csaf.vulnerability as vulnerability
from tests import conftest
from turvallisuusneuvonta.csaf.cvss.cvss import CVSS31, SeverityType, Version as CvssVersion

CVSS31_BASE_SEVERITY_LOG4J = SeverityType.critical


def test_vulnerability_empty():
    assert isinstance(vulnerability.Vulnerability(), vulnerability.Vulnerability)


def test_vulnerability_text():
    message = '__init__() takes 1 positional argument but 3 were given'
    with pytest.raises(TypeError, match=re.escape(message)):
        _ = vulnerability.Vulnerability('some', 'text')  # type: ignore


def test_vulnerability_score_text():
    message = '__init__() takes 1 positional argument but 2 were given'
    with pytest.raises(TypeError, match=re.escape(message)):
        _ = vulnerability.Score('text')  # type: ignore


def test_vulnerability_score_cvss31_log4j_cve_2021_44228():
    data = {
        'version': CvssVersion.three_wun,
        'vector_string': conftest.CVSS31_VECTOR_STRING_LOG4J,
        'base_score': conftest.CVSS31_BASE_SCORE_LOG4J,
        'base_severity': CVSS31_BASE_SEVERITY_LOG4J,
    }
    cvss_cve_2021_44228 = CVSS31(**data)
    message = '1 validation error for Score'
    with pytest.raises(ValidationError, match=message) as err:
        _ = vulnerability.Score(cvss_v3=cvss_cve_2021_44228, products=[])  # type: ignore
    assert '\nproducts -> __root__\n  ensure this value has at least 1 items' in str(err.value)


def test_vulnerability_score_cvss31_log4j_cve_2021_44228_product_foo():
    data = {
        'version': CvssVersion.three_wun,
        'vector_string': conftest.CVSS31_VECTOR_STRING_LOG4J,
        'base_score': conftest.CVSS31_BASE_SCORE_LOG4J,
        'base_severity': CVSS31_BASE_SEVERITY_LOG4J,
    }
    # pr_ref = product.ReferenceTokenForProductInstance(value='log4j-123')
    products = product.Products(__root__=['log4j-123'])
    cvss_cve_2021_44228 = CVSS31(**data)
    score = vulnerability.Score(cvss_v3=cvss_cve_2021_44228, products=products)
    assert score.cvss_v3 == cvss_cve_2021_44228
    assert score.products == products


def test_vulnerability_score_loads_cvss31_log4j_cve_2021_44228_product_foo():
    score = vulnerability.Score(**conftest.VULNERABILITY_SCORE_LOG4J)
    assert orjson.loads(score.json()) == conftest.VULNERABILITY_SCORE_LOG4J


def test_vulnerability_cwe_text():
    message = '__init__() takes 1 positional argument but 2 were given'
    with pytest.raises(TypeError, match=re.escape(message)):
        _ = vulnerability.Cwe('text')  # type: ignore


def test_vulnerability_cwe_352():
    data = {
        'id': conftest.CWE_ID_352,
        'name': conftest.CWE_NAME_352,
    }
    cwe_352 = vulnerability.Cwe(**data)
    assert cwe_352.id == conftest.CWE_ID_352
    assert cwe_352.name == conftest.CWE_NAME_352


def test_vulnerability_entitlement_none():
    message = '1 validation error for Entitlement'
    with pytest.raises(ValidationError, match=message) as err:
        _ = vulnerability.Entitlement()  # type: ignore
    assert '\n__root__\n  field required' in str(err.value)


def test_vulnerability_entitlement_empty():
    message = '1 validation error for Entitlement'
    with pytest.raises(ValidationError, match=message) as err:
        _ = vulnerability.Entitlement(__root__='')
    assert '\n__root__\n  ensure this value has at least 1 characters' in str(err.value)


def test_vulnerability_entitlement_with_value():
    value = 'All people on this planet'
    all_people_entitled = vulnerability.Entitlement(__root__=value)
    assert all_people_entitled.__root__ == value


def test_vulnerability_restart_req_by_rem_none():
    message = '1 validation error for RestartRequired'
    with pytest.raises(ValidationError, match=message) as err:
        _ = vulnerability.RestartRequired()  # type: ignore
    assert '\ncategory\n  field required' in str(err.value)


def test_vulnerability_restart_req_by_rem_empty_category():
    message = '1 validation error for RestartRequired'
    with pytest.raises(ValidationError, match=message) as err:
        _ = vulnerability.RestartRequired(category='')  # type: ignore
    assert '\ncategory\n  value is not a valid enumeration member; permitted:' in str(err.value)


def test_vulnerability_restart_req_by_rem_valid_category():
    category = vulnerability.RestartRequiredCategory.none
    restart_req_none = vulnerability.RestartRequired(category=category)
    assert restart_req_none.category == category


def test_vulnerability_restart_req_by_rem_valid_category_details_empty():
    category = vulnerability.RestartRequiredCategory.none
    message = '1 validation error for RestartRequired'
    with pytest.raises(ValidationError, match=message) as err:
        _ = vulnerability.RestartRequired(category=category, details='')
    assert '\ndetails\n  ensure this value has at least 1 characters' in str(err.value)


def test_vulnerability_restart_req_by_rem_valid_category_details_ok():
    category = vulnerability.RestartRequiredCategory.none
    details = 'And that is all I can say about that.'
    restart_req_none = vulnerability.RestartRequired(category=category, details=details)
    assert restart_req_none.category == category
    assert restart_req_none.details == details


def test_vulnerability_remediation_none():
    message = '2 validation errors for Remediation'
    with pytest.raises(ValidationError, match=message) as err:
        _ = vulnerability.Remediation()
    assert '\ncategory\n  field required' in str(err.value)
    assert '\ndetails\n  field required' in str(err.value)


def test_vulnerability_remediation_empty_category():
    message = '2 validation errors for Remediation'
    with pytest.raises(ValidationError, match=message) as err:
        _ = vulnerability.Remediation(category='')  # type: ignore
    expected = (
        '\ncategory\n  value is not a valid enumeration member;'
        " permitted: 'mitigation', 'no_fix_planned', 'none_available', 'vendor_fix', 'workaround'"
    )
    assert expected in str(err.value)
    assert '\ndetails\n  field required' in str(err.value)


def test_vulnerability_remediation_valid_category_details_empty():
    category = vulnerability.RemediationCategory.none_available
    message = '1 validation error for Remediation'
    with pytest.raises(ValidationError, match=message) as err:
        _ = vulnerability.Remediation(category=category, details='')
    assert '\ndetails\n  ensure this value has at least 1 characters' in str(err.value)


def test_vulnerability_remediation_req_by_rem_valid_category_details_ok():
    category = vulnerability.RemediationCategory.none_available
    details = 'And that is all I can say about that.'
    remediation_none = vulnerability.Remediation(category=category, details=details)
    assert remediation_none.category == category
    assert remediation_none.details == details


def test_vulnerability_threat_none():
    message = '2 validation errors for Threat'
    with pytest.raises(ValidationError, match=message) as err:
        _ = vulnerability.Threat()
    assert '\ncategory\n  field required' in str(err.value)
    assert '\ndetails\n  field required' in str(err.value)


def test_vulnerability_threat_empty_category():
    message = '2 validation errors for Threat'
    with pytest.raises(ValidationError, match=message) as err:
        _ = vulnerability.Threat(category='')  # type: ignore
    expected = (
        "\ncategory\n  value is not a valid enumeration member; permitted: 'exploit_status', 'impact', 'target_set'"
    )
    assert expected in str(err.value)
    assert '\ndetails\n  field required' in str(err.value)


def test_vulnerability_threat_valid_category_details_empty():
    category = vulnerability.ThreatCategory.exploit_status
    message = '1 validation error for Threat'
    with pytest.raises(ValidationError, match=message) as err:
        _ = vulnerability.Threat(category=category, details='')
    assert '\ndetails\n  ensure this value has at least 1 characters' in str(err.value)


def test_vulnerability_threat_req_by_rem_valid_category_details_ok():
    category = vulnerability.ThreatCategory.exploit_status
    details = 'And that is all I can say about that.'
    threat_exploit = vulnerability.Threat(category=category, details=details)
    assert threat_exploit.category == category
    assert threat_exploit.date is None
    assert threat_exploit.details == details
    assert threat_exploit.group_ids is None
    assert threat_exploit.product_ids is None
