import requests
from urllib.parse import urlencode
from .exceptions import (
    BNVDAPIError,
    BNVDConnectionError,
    BNVDInvalidResponse
)


class BNVDClient:
    BASE_URL = "https://bnvd.org/api/v1"

    def __init__(self, timeout=10, user_agent="bnvd-python-client/1.1"):
        self.timeout = timeout
        self.session = requests.Session()
        self.session.headers.update({
            "User-Agent": user_agent,
            "Accept": "application/json"
        })

    # =========================
    # Core helpers
    # =========================

    def _handle_response(self, response):
        try:
            response.raise_for_status()
            payload = response.json()
        except requests.exceptions.HTTPError as e:
            raise BNVDAPIError(str(e), response.status_code)
        except requests.exceptions.RequestException as e:
            raise BNVDConnectionError(str(e))
        except ValueError:
            raise BNVDInvalidResponse("Resposta não é um JSON válido")

        if payload.get("status") != "success":
            raise BNVDAPIError(
                payload.get("message", "Erro desconhecido"),
                payload.get("code", response.status_code)
            )

        return payload

    def _get(self, endpoint, params=None):
        url = f"{self.BASE_URL}{endpoint}"
        if params:
            query = urlencode({k: v for k, v in params.items() if v is not None})
            url = f"{url}?{query}"

        response = self.session.get(url, timeout=self.timeout)
        return self._handle_response(response)

    # =========================
    # API Info
    # =========================

    def get_endpoints(self):
        """Retorna informações gerais da API"""
        return self._get("/")

    # =========================
    # Vulnerabilidades
    # =========================

    def get_vulnerabilities(
        self,
        page=1,
        per_page=20,
        year=None,
        severity=None,
        vendor=None,
        include_pt=True
    ):
        return self._get("/vulnerabilities", {
            "page": page,
            "per_page": min(per_page, 100),
            "year": year,
            "severity": severity,
            "vendor": vendor,
            "include_pt": str(include_pt).lower()
        })

    def get_vulnerability(self, cve_id):
        return self._get(f"/vulnerabilities/{cve_id}")

    def get_recent_vulnerabilities(self, days=None, page=1, per_page=20):
        return self._get("/search/recent", {
            "days": days,
            "page": page,
            "per_page": min(per_page, 100)
        })

    def get_recent_limit(self, limit=5):
        return self._get(f"/search/recent/{limit}")

    def get_by_year(self, year):
        return self._get(f"/search/year/{year}")

    def get_by_severity(self, severity):
        severity = severity.upper()
        if severity not in {"LOW", "MEDIUM", "HIGH", "CRITICAL"}:
            raise ValueError(
                "Severidade inválida. Use: LOW, MEDIUM, HIGH, CRITICAL"
            )
        return self._get(f"/search/severity/{severity}")

    def get_by_vendor(self, vendor):
        return self._get(f"/search/vendor/{vendor}")

    # =========================
    # Estatísticas
    # =========================

    def get_stats(self):
        return self._get("/stats")

    def get_stats_by_year(self):
        return self._get("/stats/years")

    # =========================
    # Notícias de Cibersegurança
    # =========================

    def get_news(self, page=1, per_page=20):
        return self._get("/noticias", {
            "page": page,
            "per_page": min(per_page, 100)
        })

    def get_recent_news(self, limit=5):
        return self._get("/noticias/recentes", {
            "limit": limit
        })

    def get_news_by_slug(self, slug):
        return self._get(f"/noticias/{slug}")

    # =========================
    # MITRE ATT&CK
    # =========================

    def get_mitre_info(self):
        return self._get("/mitre")

    def get_mitre_matrices(self):
        return self._get("/mitre/matrices")

    def get_mitre_matrix(self, matrix_type, translate=False):
        return self._get(f"/mitre/matrix/{matrix_type}", {
            "translate": str(translate).lower()
        })

    def get_mitre_techniques(
        self,
        matrix=None,
        tactic=None,
        platform=None,
        translate=False
    ):
        return self._get("/mitre/techniques", {
            "matrix": matrix,
            "tactic": tactic,
            "platform": platform,
            "translate": str(translate).lower()
        })

    def get_mitre_technique(self, technique_id, translate=False):
        return self._get(f"/mitre/technique/{technique_id}", {
            "translate": str(translate).lower()
        })

    def get_mitre_subtechniques(self, matrix=None, translate=False):
        return self._get("/mitre/subtechniques", {
            "matrix": matrix,
            "translate": str(translate).lower()
        })

    def get_mitre_groups(self, matrix=None, translate=False):
        return self._get("/mitre/groups", {
            "matrix": matrix,
            "translate": str(translate).lower()
        })

    def get_mitre_group(self, group_id, translate=False):
        return self._get(f"/mitre/group/{group_id}", {
            "translate": str(translate).lower()
        })

    def get_mitre_mitigations(self, matrix=None, translate=False):
        return self._get("/mitre/mitigations", {
            "matrix": matrix,
            "translate": str(translate).lower()
        })

    def get_mitre_mitigation(self, mitigation_id, translate=False):
        return self._get(f"/mitre/mitigation/{mitigation_id}", {
            "translate": str(translate).lower()
        })
