#!/usr/bin/env python3
# For authorized testing in lab/CTF environments only
"""Red team exercise planning and ATT&CK technique tracking agent."""

import argparse
import json
import logging
from datetime import datetime
from dataclasses import dataclass, field, asdict
from typing import List

try:
    import requests
except ImportError:
    import sys; sys.exit("requests is required: pip install requests")

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

MITRE_ATTACK_URL = "https://raw.githubusercontent.com/mitre/cti/master/enterprise-attack/enterprise-attack.json"


@dataclass
class TechniqueExecution:
    technique_id: str
    technique_name: str
    tactic: str
    timestamp: str = ""
    status: str = "planned"
    detected: bool = False
    detection_time: str = ""
    notes: str = ""


@dataclass
class RedTeamOperation:
    operation_name: str
    target_org: str
    emulated_actor: str
    start_date: str
    objectives: List[str] = field(default_factory=list)
    techniques: List[TechniqueExecution] = field(default_factory=list)


def load_attack_techniques(cache_file: str = "attack_enterprise.json") -> dict:
    """Load MITRE ATT&CK Enterprise techniques from local cache or upstream."""
    import os
    if os.path.exists(cache_file):
        with open(cache_file) as f:
            return json.load(f)
    logger.info("Downloading ATT&CK Enterprise data...")
    resp = requests.get(MITRE_ATTACK_URL, timeout=60)
    resp.raise_for_status()
    data = resp.json()
    with open(cache_file, "w") as f:
        json.dump(data, f)
    return data


def get_actor_techniques(attack_data: dict, actor_name: str) -> List[dict]:
    """Extract techniques used by a specific threat actor from ATT&CK data."""
    actor_id = None
    for obj in attack_data.get("objects", []):
        if obj.get("type") == "intrusion-set" and actor_name.lower() in obj.get("name", "").lower():
            actor_id = obj["id"]
            break

    if not actor_id:
        logger.warning("Actor '%s' not found in ATT&CK data", actor_name)
        return []

    technique_refs = set()
    for obj in attack_data.get("objects", []):
        if obj.get("type") == "relationship" and obj.get("source_ref") == actor_id:
            if obj.get("relationship_type") == "uses":
                technique_refs.add(obj["target_ref"])

    techniques = []
    for obj in attack_data.get("objects", []):
        if obj.get("id") in technique_refs and obj.get("type") == "attack-pattern":
            ext_refs = obj.get("external_references", [])
            tech_id = next((r["external_id"] for r in ext_refs if r.get("source_name") == "mitre-attack"), "")
            kill_chain = obj.get("kill_chain_phases", [{}])
            tactic = kill_chain[0].get("phase_name", "") if kill_chain else ""
            techniques.append({"id": tech_id, "name": obj["name"], "tactic": tactic})

    logger.info("Found %d techniques for actor '%s'", len(techniques), actor_name)
    return techniques


def build_operation_plan(actor_name: str, target: str, objectives: List[str],
                         attack_data: dict) -> RedTeamOperation:
    """Create a red team operation plan based on emulated threat actor TTPs."""
    techniques = get_actor_techniques(attack_data, actor_name)
    executions = [
        TechniqueExecution(
            technique_id=t["id"], technique_name=t["name"],
            tactic=t["tactic"], status="planned",
        )
        for t in techniques
    ]
    return RedTeamOperation(
        operation_name=f"{actor_name} Emulation - {target}",
        target_org=target, emulated_actor=actor_name,
        start_date=datetime.utcnow().isoformat(),
        objectives=objectives, techniques=executions,
    )


def log_technique_execution(op: RedTeamOperation, technique_id: str,
                             detected: bool = False, notes: str = "") -> None:
    """Mark a technique as executed and record detection status."""
    for tech in op.techniques:
        if tech.technique_id == technique_id:
            tech.status = "executed"
            tech.timestamp = datetime.utcnow().isoformat()
            tech.detected = detected
            if detected:
                tech.detection_time = datetime.utcnow().isoformat()
            tech.notes = notes
            logger.info("Technique %s executed (detected=%s)", technique_id, detected)
            return
    logger.warning("Technique %s not found in operation plan", technique_id)


def generate_detection_gap_report(op: RedTeamOperation) -> dict:
    """Produce a detection gap analysis comparing executed vs detected techniques."""
    executed = [t for t in op.techniques if t.status == "executed"]
    detected = [t for t in executed if t.detected]
    missed = [t for t in executed if not t.detected]

    return {
        "operation": op.operation_name,
        "emulated_actor": op.emulated_actor,
        "total_techniques_planned": len(op.techniques),
        "techniques_executed": len(executed),
        "techniques_detected": len(detected),
        "techniques_missed": len(missed),
        "detection_rate": f"{len(detected)/len(executed)*100:.1f}%" if executed else "N/A",
        "detected_list": [asdict(t) for t in detected],
        "missed_list": [asdict(t) for t in missed],
        "recommendations": [
            f"Improve detection for {t.technique_id} ({t.technique_name})"
            for t in missed
        ],
    }


def main():
    parser = argparse.ArgumentParser(description="Red Team Exercise Agent")
    parser.add_argument("--actor", required=True, help="Threat actor to emulate (e.g., APT29)")
    parser.add_argument("--target", required=True, help="Target organization name")
    parser.add_argument("--objectives", nargs="+", default=["Demonstrate domain compromise"])
    parser.add_argument("--output", default="redteam_plan.json")
    args = parser.parse_args()

    attack_data = load_attack_techniques()
    op = build_operation_plan(args.actor, args.target, args.objectives, attack_data)
    report = {
        "operation": asdict(op),
        "technique_count": len(op.techniques),
        "tactics_covered": list(set(t.tactic for t in op.techniques if t.tactic)),
    }
    with open(args.output, "w") as f:
        json.dump(report, f, indent=2)
    logger.info("Operation plan saved to %s", args.output)
    print(json.dumps(report, indent=2))


if __name__ == "__main__":
    main()
