#!/usr/bin/env python3
"""Greenbone/OpenVAS Vulnerability Management agent - creates scan targets, executes scans, and parses reports via python-gvm GMP protocol"""

import argparse
import json
from collections import Counter, defaultdict
from datetime import datetime
from pathlib import Path

try:
    from gvm.connections import UnixSocketConnection, TLSConnection
    from gvm.protocols.gmp import Gmp
    from gvm.transforms import EtreeTransform
    HAS_GVM = True
except ImportError:
    HAS_GVM = False


def load_data(path):
    return json.loads(Path(path).read_text(encoding="utf-8"))


def connect_gvm(host, port=9390, socket_path="/run/gvmd/gvmd.sock", use_tls=False, username="admin", password="admin"):
    """Connect to GVM daemon and authenticate via GMP."""
    if not HAS_GVM:
        return None, "python-gvm not installed"
    transform = EtreeTransform()
    if use_tls:
        conn = TLSConnection(hostname=host, port=port)
    else:
        conn = UnixSocketConnection(path=socket_path)
    gmp = Gmp(connection=conn, transform=transform)
    gmp.authenticate(username, password)
    version_resp = gmp.get_version()
    version = version_resp.xpath("version/text()")[0] if version_resp.xpath("version/text()") else "unknown"
    return gmp, version


def create_target(gmp, name, hosts, port_list_id="33d0cd82-57c6-11e1-8ed1-406186ea4fc5"):
    """Create a scan target. Default port list is 'All IANA assigned TCP'."""
    resp = gmp.create_target(name=name, hosts=hosts, port_list_id=port_list_id)
    target_id = resp.get("id", "")
    return target_id


def create_and_start_task(gmp, task_name, target_id, config_id="daba56c8-73ec-11df-a475-002264764cea", scanner_id="08b69003-5fc2-4037-a479-93b440211c73"):
    """Create scan task and start it. Default config is 'Full and fast', default scanner is 'OpenVAS Default'."""
    resp = gmp.create_task(name=task_name, config_id=config_id, target_id=target_id, scanner_id=scanner_id)
    task_id = resp.get("id", "")
    gmp.start_task(task_id)
    return task_id


def parse_report_xml(report_xml):
    """Parse GMP report XML into structured findings."""
    findings = []
    results = report_xml.findall(".//result") if report_xml is not None else []
    for result in results:
        host_el = result.find("host")
        host = host_el.text if host_el is not None else ""
        name_el = result.find("name")
        name = name_el.text if name_el is not None else ""
        threat_el = result.find("threat")
        threat = threat_el.text if threat_el is not None else "Log"
        severity_el = result.find("severity")
        cvss = float(severity_el.text) if severity_el is not None and severity_el.text else 0.0
        nvt = result.find("nvt")
        oid = nvt.get("oid", "") if nvt is not None else ""
        cve_el = nvt.find("cve") if nvt is not None else None
        cve = cve_el.text if cve_el is not None and cve_el.text != "NOCVE" else ""
        desc_el = result.find("description")
        desc = (desc_el.text or "")[:200] if desc_el is not None else ""
        sev_label = "critical" if cvss >= 9.0 else "high" if cvss >= 7.0 else "medium" if cvss >= 4.0 else "low"
        findings.append({
            "host": host,
            "vulnerability": name,
            "severity": sev_label,
            "cvss": cvss,
            "cve": cve,
            "nvt_oid": oid,
            "description": desc,
        })
    return findings


def analyze_offline_report(data):
    """Analyze pre-exported GVM report data (JSON format)."""
    findings = []
    results = data.get("results", data.get("vulnerabilities", []))
    if isinstance(data, list):
        results = data
    for r in results:
        cvss = r.get("cvss", r.get("severity_score", 0.0))
        if isinstance(cvss, str):
            try:
                cvss = float(cvss)
            except ValueError:
                cvss = 0.0
        sev_label = "critical" if cvss >= 9.0 else "high" if cvss >= 7.0 else "medium" if cvss >= 4.0 else "low"
        findings.append({
            "host": r.get("host", r.get("ip", "")),
            "vulnerability": r.get("name", r.get("vulnerability", "")),
            "severity": sev_label,
            "cvss": cvss,
            "cve": r.get("cve", r.get("cves", "")),
            "nvt_oid": r.get("nvt_oid", r.get("oid", "")),
            "description": (r.get("description", r.get("summary", "")) or "")[:200],
        })
    return findings


def generate_report(input_path):
    data = load_data(input_path)
    findings = analyze_offline_report(data)
    sev = Counter(f["severity"] for f in findings)
    host_vulns = defaultdict(int)
    for f in findings:
        host_vulns[f["host"]] += 1
    cve_list = [f["cve"] for f in findings if f["cve"]]
    findings.sort(key=lambda x: x["cvss"], reverse=True)
    return {
        "report": "greenbone_vulnerability_management",
        "generated_at": datetime.utcnow().isoformat() + "Z",
        "total_vulnerabilities": len(findings),
        "severity_summary": dict(sev),
        "hosts_scanned": len(host_vulns),
        "host_vulnerability_counts": dict(host_vulns),
        "unique_cves": len(set(cve_list)),
        "top_10_findings": findings[:10],
        "findings": findings,
    }


def main():
    ap = argparse.ArgumentParser(description="Greenbone Vulnerability Management Agent")
    ap.add_argument("--input", required=True, help="Input JSON with GVM scan results")
    ap.add_argument("--output", help="Output JSON report path")
    ap.add_argument("--host", help="GVM host for live scan (requires python-gvm)")
    ap.add_argument("--username", default="admin", help="GMP username")
    ap.add_argument("--password", default="admin", help="GMP password")
    args = ap.parse_args()
    report = generate_report(args.input)
    out = json.dumps(report, indent=2)
    if args.output:
        Path(args.output).write_text(out, encoding="utf-8")
        print(f"Report written to {args.output}")
    else:
        print(out)


if __name__ == "__main__":
    main()
