"""Analyze subcommand for npkt CLI."""

import json
import sys
from pathlib import Path

import click

from ..analyzer.policy_analyzer import analyze_policies
from ..models.analysis import AnalysisReport, Issue, IssueType
from ..parsers.scp_parser import SCPParseError, load_policies_from_dir, load_policy


@click.command()
@click.argument("policy_path", type=click.Path(exists=True))
@click.option(
    "--format",
    "-f",
    "output_format",
    type=click.Choice(["text", "json"]),
    default="text",
    help="Output format (default: text)",
)
@click.option(
    "--cross-policy/--no-cross-policy",
    default=True,
    help="Enable cross-policy analysis (default: enabled)",
)
@click.option(
    "--strict",
    is_flag=True,
    help="Treat warnings as errors (exit with non-zero status)",
)
def analyze(
    policy_path: str,
    output_format: str,
    cross_policy: bool,
    strict: bool,
) -> None:
    """Analyze SCP policies for conflicts and redundancies.

    POLICY_PATH can be a single JSON file or a directory containing JSON files.
    When given a directory, performs cross-policy analysis to detect duplicates,
    shadows, and conflicts between policies.

    \b
    Examples:
      npkt analyze policy.json
      npkt analyze ./policies/
      npkt analyze ./policies/ --format json
      npkt analyze ./policies/ --no-cross-policy
    """
    path = Path(policy_path)

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

    # Run analysis
    report = analyze_policies(*policies)

    if output_format == "json":
        _output_json(report)
    else:
        _output_text(report)

    # Exit with appropriate code
    has_errors = any(issue.type == IssueType.CONFLICT for issue in report.issues)
    has_warnings = any(
        issue.type in (IssueType.SHADOW, IssueType.UNREACHABLE) for issue in report.issues
    )

    if has_errors:
        sys.exit(1)
    elif strict and has_warnings:
        sys.exit(1)
    else:
        sys.exit(0)


def _output_json(report: AnalysisReport) -> None:
    """Output results in JSON format."""
    output = {
        "summary": {
            "total_policies": report.total_policies,
            "total_statements": report.total_statements,
            "issue_count": len(report.issues),
        },
        "issues": [
            {
                "type": issue.type.value,
                "severity": _get_severity(issue.type),
                "message": issue.message,
                "policy1": issue.policy1,
                "statement1": issue.statement1,
                "policy2": issue.policy2,
                "statement2": issue.statement2,
            }
            for issue in report.issues
        ],
    }
    click.echo(json.dumps(output, indent=2))


def _get_severity(issue_type: IssueType) -> str:
    """Map issue type to severity level."""
    if issue_type == IssueType.CONFLICT:
        return "error"
    elif issue_type in (IssueType.SHADOW, IssueType.UNREACHABLE):
        return "warning"
    else:
        return "info"


def _output_text(report: AnalysisReport) -> None:
    """Output results in human-readable text format."""
    click.echo(click.style("\nSCP Policy Analysis Report", fg="cyan", bold=True))
    click.echo("=" * 60)

    # Summary
    click.echo(f"\nPolicies analyzed: {report.total_policies}")
    click.echo(f"Total statements:  {report.total_statements}")

    if not report.issues:
        click.echo(click.style("\n+ No issues found!", fg="green"))
        return

    # Group issues by type
    issues_by_type: dict[IssueType, list[Issue]] = {}
    for issue in report.issues:
        key = issue.type
        if key not in issues_by_type:
            issues_by_type[key] = []
        issues_by_type[key].append(issue)

    click.echo(f"\nIssues found: {len(report.issues)}")

    # Display issues by severity
    severity_order = [
        IssueType.CONFLICT,
        IssueType.SHADOW,
        IssueType.UNREACHABLE,
        IssueType.DUPLICATE_STATEMENT,
        IssueType.DUPLICATE_SID,
    ]

    for issue_type in severity_order:
        if issue_type not in issues_by_type:
            continue

        issues = issues_by_type[issue_type]
        color = _get_color(issue_type)
        icon = _get_icon(issue_type)

        click.echo(
            f"\n{click.style(f'{icon} {issue_type.value.upper()}', fg=color, bold=True)}"
            f" ({len(issues)})"
        )
        click.echo("-" * 40)

        for issue in issues:
            location = ""
            if issue.policy1:
                location = f"[{issue.policy1}"
                if issue.statement1:
                    location += f" / {issue.statement1}"
                location += "] "

            click.echo(f"  {location}{issue.message}")


def _get_color(issue_type: IssueType) -> str:
    """Get color for issue type."""
    colors = {
        IssueType.CONFLICT: "red",
        IssueType.SHADOW: "yellow",
        IssueType.UNREACHABLE: "yellow",
        IssueType.DUPLICATE_STATEMENT: "cyan",
        IssueType.DUPLICATE_SID: "cyan",
    }
    return colors.get(issue_type, "white")


def _get_icon(issue_type: IssueType) -> str:
    """Get icon for issue type."""
    icons = {
        IssueType.CONFLICT: "!",
        IssueType.SHADOW: "~",
        IssueType.UNREACHABLE: "?",
        IssueType.DUPLICATE_STATEMENT: "=",
        IssueType.DUPLICATE_SID: "=",
    }
    return icons.get(issue_type, "*")
