#!/usr/bin/env python3
"""Vulnerability Scanning Workflow Agent - Automates scan orchestration and prioritization."""

import json
import logging
import os
import argparse
from datetime import datetime

import requests
import nmap

logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
logger = logging.getLogger(__name__)


def run_nmap_vuln_scan(targets, ports="1-1024"):
    """Run Nmap vulnerability scan against target hosts."""
    scanner = nmap.PortScanner()
    logger.info("Starting Nmap vuln scan on %s ports %s", targets, ports)
    scanner.scan(hosts=targets, ports=ports, arguments="-sV --script=vulners,vulscan")
    results = []
    for host in scanner.all_hosts():
        for proto in scanner[host].all_protocols():
            for port in scanner[host][proto]:
                service = scanner[host][proto][port]
                results.append({
                    "host": host,
                    "port": port,
                    "protocol": proto,
                    "state": service["state"],
                    "service": service.get("name", ""),
                    "version": service.get("version", ""),
                    "scripts": service.get("script", {}),
                })
    logger.info("Nmap scan complete: %d service entries", len(results))
    return results


def fetch_cisa_kev():
    """Fetch the CISA Known Exploited Vulnerabilities catalog."""
    url = "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json"
    resp = requests.get(url, timeout=30)
    resp.raise_for_status()
    kev_data = resp.json()
    kev_set = {v["cveID"] for v in kev_data["vulnerabilities"]}
    logger.info("Loaded %d CISA KEV entries", len(kev_set))
    return kev_set


def launch_nessus_scan(nessus_url, api_keys, scan_name, targets):
    """Launch a vulnerability scan via Nessus REST API."""
    headers = {"X-ApiKeys": api_keys, "Content-Type": "application/json"}
    scan_config = {
        "uuid": "advanced",
        "settings": {
            "name": scan_name,
            "text_targets": targets,
            "launch": "ON_DEMAND",
            "enabled": True,
        },
    }
    resp = requests.post(
        f"{nessus_url}/scans", headers=headers, json=scan_config,
        verify=not os.environ.get("SKIP_TLS_VERIFY", "").lower() == "true", timeout=30  # Set SKIP_TLS_VERIFY=true for self-signed certs in lab environments
    )
    resp.raise_for_status()
    scan_id = resp.json()["scan"]["id"]
    logger.info("Nessus scan created: ID %d", scan_id)

    requests.post(
        f"{nessus_url}/scans/{scan_id}/launch", headers=headers,
        verify=not os.environ.get("SKIP_TLS_VERIFY", "").lower() == "true", timeout=30  # Set SKIP_TLS_VERIFY=true for self-signed certs in lab environments
    )
    logger.info("Nessus scan %d launched", scan_id)
    return scan_id


def prioritize_vulnerabilities(vulns, kev_set, asset_criticality_map):
    """Apply risk-based prioritization to vulnerability findings."""
    for vuln in vulns:
        cvss = vuln.get("cvss", 0.0)
        cve = vuln.get("cve", "")
        host = vuln.get("host", "")
        criticality = asset_criticality_map.get(host, 1.0)

        risk_score = cvss * criticality
        if cve in kev_set:
            risk_score *= 1.5
            vuln["kev"] = True
        else:
            vuln["kev"] = False

        vuln["risk_score"] = round(risk_score, 1)
        vuln["priority"] = (
            "P1" if risk_score >= 13.5 else
            "P2" if risk_score >= 7.0 else
            "P3" if risk_score >= 4.0 else
            "P4"
        )
    vulns.sort(key=lambda x: x["risk_score"], reverse=True)
    return vulns


def create_servicenow_ticket(snow_url, token, vuln):
    """Create a ServiceNow incident ticket for a high-priority vulnerability."""
    headers = {"Content-Type": "application/json", "Authorization": f"Bearer {token}"}
    ticket = {
        "short_description": f"[VULN] {vuln.get('cve', 'N/A')} on {vuln['host']}",
        "description": (
            f"CVE: {vuln.get('cve', 'N/A')}\n"
            f"CVSS: {vuln.get('cvss', 0)}\n"
            f"Host: {vuln['host']}\n"
            f"Risk Score: {vuln['risk_score']}\n"
            f"CISA KEV: {'YES' if vuln.get('kev') else 'NO'}"
        ),
        "urgency": "1" if vuln.get("kev") else "2",
        "category": "Vulnerability",
    }
    resp = requests.post(
        f"{snow_url}/api/now/table/incident", headers=headers, json=ticket, timeout=30
    )
    logger.info("ServiceNow ticket created: %s", resp.json().get("result", {}).get("number"))
    return resp.json()


def generate_report(vulns):
    """Generate vulnerability scan summary report."""
    p1 = [v for v in vulns if v.get("priority") == "P1"]
    p2 = [v for v in vulns if v.get("priority") == "P2"]
    lines = [
        "VULNERABILITY SCAN REPORT",
        "=" * 40,
        f"Date: {datetime.utcnow().strftime('%Y-%m-%d %H:%M UTC')}",
        f"Total Findings: {len(vulns)}",
        f"P1 Critical: {len(p1)}",
        f"P2 High: {len(p2)}",
        "",
        "TOP PRIORITY FINDINGS:",
    ]
    for v in p1[:10]:
        lines.append(f"  {v.get('cve', 'N/A'):20s} {v['host']:15s} Score: {v['risk_score']}")
    print("\n".join(lines))
    return lines


def main():
    parser = argparse.ArgumentParser(description="Vulnerability Scanning Workflow Agent")
    parser.add_argument("--targets", required=True, help="Target hosts/CIDR")
    parser.add_argument("--ports", default="1-1024", help="Port range to scan")
    parser.add_argument("--nessus-url", help="Nessus API URL")
    parser.add_argument("--nessus-keys", help="Nessus API keys (accessKey;secretKey)")
    parser.add_argument("--output", default="vuln_report.json")
    args = parser.parse_args()

    kev_set = fetch_cisa_kev()
    results = run_nmap_vuln_scan(args.targets, args.ports)

    if args.nessus_url and args.nessus_keys:
        launch_nessus_scan(args.nessus_url, args.nessus_keys, "Agent Scan", args.targets)

    prioritized = prioritize_vulnerabilities(results, kev_set, {})
    generate_report(prioritized)

    with open(args.output, "w") as f:
        json.dump(prioritized, f, indent=2)
    logger.info("Report saved to %s", args.output)


if __name__ == "__main__":
    main()
