#!/usr/bin/env python3
"""Vulnerability dashboard builder using DefectDojo API.

Queries DefectDojo REST API v2 for findings, products, and engagements
to build vulnerability management dashboards and metrics.
"""

import json
import datetime
import os
import collections

try:
    import requests
    HAS_REQUESTS = True
except ImportError:
    HAS_REQUESTS = False


class DefectDojoClient:
    """Client for DefectDojo REST API v2."""

    def __init__(self, url=None, api_key=None):
        self.url = (url or os.environ.get("DEFECTDOJO_URL", "http://localhost:8080")).rstrip("/")
        self.api_key = api_key or os.environ.get("DEFECTDOJO_API_KEY", "")
        self.headers = {
            "Authorization": "Token " + self.api_key,
            "Content-Type": "application/json",
        }

    def _get(self, endpoint, params=None):
        if not HAS_REQUESTS or not self.api_key:
            return {"error": "requests not available or no API key"}
        try:
            resp = requests.get(
                self.url + "/api/v2/" + endpoint,
                headers=self.headers, params=params, timeout=15
            )
            if resp.status_code == 200:
                return resp.json()
            return {"error": "HTTP {}".format(resp.status_code)}
        except Exception as e:
            return {"error": str(e)}

    def get_findings(self, severity=None, active=True, limit=100):
        params = {"active": active, "limit": limit}
        if severity:
            params["severity"] = severity
        return self._get("findings/", params)

    def get_products(self, limit=100):
        return self._get("products/", {"limit": limit})

    def get_engagements(self, product_id=None, limit=100):
        params = {"limit": limit}
        if product_id:
            params["product"] = product_id
        return self._get("engagements/", params)

    def get_finding_count_by_severity(self):
        result = {}
        for sev in ["Critical", "High", "Medium", "Low", "Info"]:
            data = self._get("findings/", {"severity": sev, "active": True, "limit": 1})
            if isinstance(data, dict) and "count" in data:
                result[sev] = data["count"]
        return result

    def import_scan(self, product_id, engagement_id, scan_type, file_path):
        if not HAS_REQUESTS or not self.api_key:
            return {"error": "requests not available or no API key"}
        try:
            with open(file_path, "rb") as f:
                resp = requests.post(
                    self.url + "/api/v2/import-scan/",
                    headers={"Authorization": "Token " + self.api_key},
                    data={
                        "product": product_id,
                        "engagement": engagement_id,
                        "scan_type": scan_type,
                        "active": True,
                        "verified": False,
                    },
                    files={"file": f},
                    timeout=60,
                )
            if resp.status_code in (200, 201):
                return resp.json()
            return {"error": "HTTP {}".format(resp.status_code)}
        except Exception as e:
            return {"error": str(e)}


def build_dashboard_data(findings):
    """Build dashboard metrics from findings list."""
    if not isinstance(findings, dict) or "results" not in findings:
        return {"error": "Invalid findings data"}

    results = findings["results"]
    severity_counts = collections.Counter()
    product_counts = collections.Counter()
    age_sum = 0
    overdue_count = 0
    now = datetime.datetime.now(datetime.timezone.utc)

    for f in results:
        severity_counts[f.get("severity", "Unknown")] += 1
        product_counts[f.get("test", {}).get("engagement", {}).get("product", {}).get("name", "Unknown")] += 1
        if f.get("date"):
            try:
                created = datetime.datetime.fromisoformat(f["date"])
                if created.tzinfo is None:
                    created = created.replace(tzinfo=datetime.timezone.utc)
                age = (now - created).days
                age_sum += age
                sla = {"Critical": 7, "High": 30, "Medium": 90, "Low": 180}.get(f.get("severity", ""), 999)
                if age > sla:
                    overdue_count += 1
            except ValueError:
                pass

    total = len(results)
    return {
        "total_active_findings": total,
        "by_severity": dict(severity_counts),
        "by_product": dict(product_counts.most_common(10)),
        "avg_age_days": round(age_sum / max(total, 1), 1),
        "overdue_count": overdue_count,
        "sla_compliance_pct": round((total - overdue_count) / max(total, 1) * 100, 1),
    }


SUPPORTED_SCAN_TYPES = [
    "Nessus Scan", "Qualys Scan", "Burp REST API",
    "ZAP Scan", "Trivy Scan", "Snyk Scan",
    "Semgrep JSON Report", "SARIF", "Generic Findings Import",
    "Anchore Grype", "Nuclei Scan", "Checkov Scan",
]


if __name__ == "__main__":
    print("=" * 60)
    print("Vulnerability Dashboard with DefectDojo")
    print("REST API v2 queries, severity metrics, SLA tracking")
    print("=" * 60)
    print("  requests available: {}".format(HAS_REQUESTS))

    client = DefectDojoClient()

    print("\n--- Supported Scan Types ---")
    for st in SUPPORTED_SCAN_TYPES:
        print("  - {}".format(st))

    print("\n--- API Endpoints ---")
    endpoints = [
        ("GET", "/api/v2/findings/", "List findings"),
        ("GET", "/api/v2/products/", "List products"),
        ("GET", "/api/v2/engagements/", "List engagements"),
        ("POST", "/api/v2/import-scan/", "Import scan results"),
        ("POST", "/api/v2/reimport-scan/", "Re-import scan results"),
    ]
    for method, path, desc in endpoints:
        print("  {} {:30s} {}".format(method, path, desc))

    demo_findings = {
        "count": 5,
        "results": [
            {"severity": "Critical", "title": "SQL Injection", "date": "2025-01-10", "test": {"engagement": {"product": {"name": "WebApp"}}}},
            {"severity": "High", "title": "XSS", "date": "2025-01-15", "test": {"engagement": {"product": {"name": "WebApp"}}}},
            {"severity": "Medium", "title": "Missing Headers", "date": "2024-12-01", "test": {"engagement": {"product": {"name": "API"}}}},
            {"severity": "Low", "title": "Cookie flag", "date": "2025-02-01", "test": {"engagement": {"product": {"name": "API"}}}},
            {"severity": "Critical", "title": "RCE", "date": "2025-02-20", "test": {"engagement": {"product": {"name": "WebApp"}}}},
        ],
    }

    dashboard = build_dashboard_data(demo_findings)
    print("\n--- Dashboard ---")
    for k, v in dashboard.items():
        print("  {}: {}".format(k, v))

    print("\n" + json.dumps({"findings_analyzed": demo_findings["count"]}, indent=2))
