"""Simulate subcommand for npkt CLI."""

import sys
from datetime import datetime, timedelta, timezone
from pathlib import Path

import click

from ..analyzer.impact_analyzer import ImpactAnalyzer
from ..ingest.file_ingester import FileIngester
from ..parsers.scp_parser import SCPParseError, load_policies_from_dir, load_policy
from ..reporters.console_reporter import ConsoleReporter
from ..reporters.json_reporter import JSONReporter


@click.command()
@click.argument("policy_path", type=click.Path(exists=True))
@click.option(
    "--logs",
    "-l",
    "logs_path",
    type=click.Path(exists=True),
    required=True,
    help="Path to CloudTrail logs (file or directory)",
)
@click.option(
    "--format",
    "-f",
    "output_format",
    type=click.Choice(["text", "json"]),
    default="text",
    help="Output format (default: text)",
)
@click.option(
    "--output",
    "-o",
    "output_file",
    type=click.Path(),
    help="Write output to file instead of stdout",
)
@click.option(
    "--days",
    "-d",
    type=int,
    default=90,
    help="Number of days to analyze (default: 90)",
)
@click.option(
    "--quick",
    is_flag=True,
    help="Quick analysis with sampling (faster, less precise)",
)
@click.option(
    "--sample-size",
    type=int,
    default=1000,
    help="Sample size for quick analysis (default: 1000)",
)
@click.option(
    "--no-details",
    is_flag=True,
    help="Hide detailed denial list in output",
)
@click.option(
    "--context",
    "-c",
    "context_file",
    type=click.Path(exists=True),
    help="Path to external context JSON file (org ID, principal/resource tags, VPC mappings)",
)
@click.option(
    "--strict-conditions",
    is_flag=True,
    help="Treat unevaluable conditions as non-matching (worst-case upper-bound denial rate)",
)
def simulate(
    policy_path: str,
    logs_path: str,
    output_format: str,
    output_file: str | None,
    days: int,
    quick: bool,
    sample_size: int,
    no_details: bool,
    context_file: str | None,
    strict_conditions: bool,
) -> None:
    """Simulate SCP impact against CloudTrail events.

    Analyzes historical CloudTrail events to determine which would be
    denied if the specified SCP policy was applied.

    \b
    Examples:
      npkt simulate policy.json --logs ./cloudtrail/
      npkt simulate policy.json --logs events.json --format json
      npkt simulate policy.json --logs ./logs/ --days 30
      npkt simulate policy.json --logs ./logs/ --context context.json
    """
    policy_file = Path(policy_path)
    logs_dir = Path(logs_path)

    # Load policies
    try:
        if policy_file.is_file():
            policies = [load_policy(policy_file)]
        elif policy_file.is_dir():
            policies = load_policies_from_dir(policy_file)
            if not policies:
                click.echo(f"No valid SCP files found in {policy_file}", err=True)
                sys.exit(1)
        else:
            click.echo(f"Invalid policy path: {policy_file}", err=True)
            sys.exit(1)
    except (OSError, ValueError, SCPParseError) as e:
        click.echo(f"Error loading policies: {e}", err=True)
        sys.exit(1)

    # Initialize ingester
    try:
        ingester = FileIngester(logs_dir)
    except (OSError, ValueError) as e:
        click.echo(f"Error initializing CloudTrail ingester: {e}", err=True)
        sys.exit(1)

    # Load external context if provided
    external_context = None
    if context_file:
        try:
            from ..models.external_context import ExternalContext

            external_context = ExternalContext.from_file(Path(context_file))
            click.echo(f"Loaded external context from {context_file}", err=True)
        except (OSError, ValueError) as e:
            click.echo(f"Error loading context file: {e}", err=True)
            sys.exit(1)

    # Set time range
    end_time = datetime.now(timezone.utc)
    start_time = end_time - timedelta(days=days)

    # Initialize analyzer
    analyzer = ImpactAnalyzer(
        scp_policies=policies,
        cloudtrail_ingester=ingester,
        external_context=external_context,
        strict_conditions=strict_conditions,
    )

    if quick:
        # Quick analysis with sampling
        click.echo(f"Running quick analysis (sample size: {sample_size})...", err=True)
        summary = analyzer.quick_summary(start_time, end_time, sample_size=sample_size)

        if output_format == "json":
            import json

            click.echo(json.dumps(summary, indent=2))
        else:
            reporter = ConsoleReporter()
            reporter.print_quick_summary(summary)

        # Exit based on denial rate
        if summary["estimated_denial_rate"] > 10:
            sys.exit(2)  # High risk
        elif summary["denied_count"] > 0:
            sys.exit(1)  # Some denials
        else:
            sys.exit(0)

    # Full analysis
    def progress_callback(count: int, message: str) -> None:
        click.echo(f"\r{message}", nl=False, err=True)

    click.echo(f"Analyzing CloudTrail events from last {days} days...", err=True)

    try:
        report = analyzer.analyze(
            start_time=start_time,
            end_time=end_time,
            progress_callback=progress_callback,
        )
    except Exception as e:
        click.echo(f"\nError during analysis: {e}", err=True)
        sys.exit(1)

    click.echo("", err=True)  # Newline after progress

    # Output results
    if output_format == "json":
        json_reporter = JSONReporter()
        if output_file:
            json_reporter.report(report, output_file)
            click.echo(f"Report written to {output_file}", err=True)
        else:
            click.echo(json_reporter.to_string(report))
    else:
        console_reporter = ConsoleReporter()
        console_reporter.report(report, show_details=not no_details)

        if output_file:
            # Also write JSON to file if requested
            json_reporter = JSONReporter()
            json_reporter.report(report, output_file)
            click.echo(f"\nJSON report also written to {output_file}", err=True)

    # Exit based on risk level
    risk_level = report.get_risk_level()
    if risk_level in ("HIGH", "CRITICAL"):
        sys.exit(2)
    elif risk_level in ("MEDIUM",):
        sys.exit(1)
    else:
        sys.exit(0)
