
import json
import gzip
from io import BytesIO

from django.db.models import Q
import requests

from api.models import Vulnerability
from api.lib import script_logger

API_URL = "https://nvd.nist.gov/feeds/json/cve/1.1/nvdcve-1.1-modified.json.gz"



class NoVulnernabilitiesError(Exception):
    pass


def get_feed_data():
    response = requests.get(API_URL)
    response.raise_for_status()
    buffer = BytesIO(response.content)
    deflatedContent = gzip.GzipFile(fileobj=buffer)
    data = json.loads(deflatedContent.read())

    deflatedContent.close()
    buffer.close()
    return data


def vulnerability_is_rejected(description:str) -> bool:
    return "**reject**" in description.lower().replace(" ", "")


def refresh_db_data(logger=None):
    logger = logger or script_logger.create_logger("vulnerabilities")
    logger.info(f"(updating vulnerability data) -> Fetching data from {API_URL}")

    existing_cve_ids = set(Vulnerability.objects.values_list("cve_identifier", flat=True))

    try:
        data = get_feed_data()
    except Exception as e:
        logger.error(f"Could not fetch data {e}")
        raise

    new_items = []
    existing_items = set()
    for cve in data['CVE_Items']:

        cve_id = cve['cve']['CVE_data_meta']['ID']
        if cve_id in existing_cve_ids:
            existing_items.add(cve_id)
            continue

        description = " ".join(dd['value'] for dd in cve['cve']['description']['description_data'])
        if vulnerability_is_rejected(description):
            continue

        new_items.append(Vulnerability(
            displayed_at=None,
            cve_identifier=cve_id,
            description=description
        ))

    logger.info(f"adding {len(new_items)} vulnerability items")
    Vulnerability.objects.bulk_create(new_items)

    cves_to_delete = Vulnerability.objects.filter(
        Q(displayed_once=True)
        & ~Q(cve_identifier__in=existing_items))


    if cves_to_delete.exists():
        logger.info(f"deleting {cves_to_delete.count()} vulnerability items")
        cves_to_delete.delete()


def select_random_vulnerability_item():
    if not Vulnerability.objects.exists():
        raise NoVulnernabilitiesError()

    if not Vulnerability.objects.filter(displayed_at__isnull=True).exists():
        Vulnerability.objects.update(displayed_at=None)

    return Vulnerability.objects.filter(displayed_at__isnull=True).first()
