"""CLI entry point: python -m attacks <command> [args]."""

from __future__ import annotations

import argparse
import json
import logging
import sys
from pathlib import Path

from attacks.collection.import_logs import BenignLogImporter
from attacks.config import load_campaign, validate_campaign
from attacks.modules.base import AttackModuleRegistry
from attacks.orchestrator import CampaignOrchestrator
from security_gym.data.composer import StreamComposer

# Trigger module registration
import attacks.modules  # noqa: F401


def cmd_run(args: argparse.Namespace) -> int:
    """Execute or dry-run a campaign."""
    path = Path(args.campaign)
    if not path.exists():
        print(f"Error: campaign file not found: {path}")
        return 1

    config = load_campaign(path)

    orchestrator = CampaignOrchestrator(config)

    if args.dry_run:
        plan = orchestrator.dry_run()
        print(json.dumps(plan, indent=2, default=str))
        return 0

    if args.collect_only:
        print("Error: --collect-only requires a previous run (not yet supported in CLI)")
        return 1

    campaign_id = orchestrator.run()
    print(f"\nCampaign complete: {campaign_id}")
    print(f"Database: {config.collection.db_path}")
    return 0


def cmd_validate(args: argparse.Namespace) -> int:
    """Validate a campaign YAML file."""
    path = Path(args.campaign)
    if not path.exists():
        print(f"Error: campaign file not found: {path}")
        return 1

    errors = validate_campaign(path)
    if errors:
        print(f"Validation FAILED for {path}:")
        for err in errors:
            print(f"  - {err}")
        return 1

    print(f"Valid: {path}")
    return 0


def cmd_import_logs(args: argparse.Namespace) -> int:
    """Import benign log files into EventStore."""
    path = Path(args.path)
    if not path.exists():
        print(f"Error: path not found: {path}")
        return 1

    db_path = Path(args.db)
    db_path.parent.mkdir(parents=True, exist_ok=True)

    importer = BenignLogImporter(db_path, source_host=args.host)
    try:
        total = importer.import_path(path)
    except (FileNotFoundError, ValueError) as e:
        print(f"Error: {e}")
        return 1

    print(f"\nImported {total} benign events into {db_path}")
    return 0


def cmd_compose(args: argparse.Namespace) -> int:
    """Compose a mixed benign+attack stream from a YAML config."""
    path = Path(args.config)
    if not path.exists():
        print(f"Error: config file not found: {path}")
        return 1

    composer = StreamComposer()
    try:
        stats = composer.compose(path, dry_run=args.dry_run)
    except (ValueError, FileNotFoundError) as e:
        print(f"Error: {e}")
        return 1

    prefix = "[DRY RUN] " if args.dry_run else ""
    print(f"\n{prefix}Composed {stats.total} events "
          f"({stats.simulated_days:.0f} days, {stats.attack_campaigns} campaigns)")
    print(f"  Benign: {stats.benign_count}")
    print(f"  Attack: {stats.attack_count}")
    if stats.attack_types_used:
        print("  Attack types:")
        for atype, count in sorted(stats.attack_types_used.items()):
            print(f"    {atype}: {count} campaigns")
    return 0


def cmd_list_modules(args: argparse.Namespace) -> int:
    """List available attack modules."""
    modules = AttackModuleRegistry.available()
    print("Available attack modules:")
    for name in modules:
        module = AttackModuleRegistry.get(name)
        doc = module.__class__.__doc__ or ""
        first_line = doc.strip().split("\n")[0] if doc else "(no description)"
        print(f"  {name:20s} {first_line}")
    return 0


def main() -> int:
    parser = argparse.ArgumentParser(
        prog="attacks",
        description="Security-gym attack campaign framework",
    )
    parser.add_argument(
        "-v", "--verbose",
        action="store_true",
        help="Enable debug logging",
    )
    subparsers = parser.add_subparsers(dest="command", required=True)

    # run
    run_parser = subparsers.add_parser("run", help="Execute a campaign")
    run_parser.add_argument("campaign", help="Path to campaign YAML")
    run_parser.add_argument("--dry-run", action="store_true", help="Preview without executing")
    run_parser.add_argument("--collect-only", action="store_true", help="Re-collect logs only")
    run_parser.set_defaults(func=cmd_run)

    # validate
    val_parser = subparsers.add_parser("validate", help="Validate campaign YAML")
    val_parser.add_argument("campaign", help="Path to campaign YAML")
    val_parser.set_defaults(func=cmd_validate)

    # import-logs
    imp_parser = subparsers.add_parser("import-logs", help="Import benign logs into EventStore")
    imp_parser.add_argument("path", help="Path to tarball or directory of log files")
    imp_parser.add_argument("--db", default="data/campaigns.db", help="EventStore database path")
    imp_parser.add_argument("--host", default=None, help="Source hostname label (e.g. hopper, isildur)")
    imp_parser.set_defaults(func=cmd_import_logs)

    # compose
    compose_parser = subparsers.add_parser("compose", help="Compose a mixed stream from YAML config")
    compose_parser.add_argument("config", help="Path to composition YAML config")
    compose_parser.add_argument("--dry-run", action="store_true", help="Preview without writing output DB")
    compose_parser.set_defaults(func=cmd_compose)

    # list-modules
    list_parser = subparsers.add_parser("list-modules", help="List attack modules")
    list_parser.set_defaults(func=cmd_list_modules)

    args = parser.parse_args()

    # Configure logging
    level = logging.DEBUG if args.verbose else logging.INFO
    logging.basicConfig(
        level=level,
        format="%(asctime)s %(levelname)-8s %(name)s: %(message)s",
        datefmt="%H:%M:%S",
    )

    result: int = args.func(args)
    return result


if __name__ == "__main__":
    sys.exit(main())
