import json
import sys
from pathlib import Path

import click

from brox import __version__
from brox.model import Property


EXIT_SUCCESS = 0
EXIT_POLICY_FAIL = 2
EXIT_ERROR = 3


@click.group()
@click.version_option(version=__version__)
def main():
    pass


@main.command()
@click.option(
    "--repo",
    type=click.Path(exists=True, file_okay=False, dir_okay=True),
    required=True,
    help="Path to repository to scan",
)
@click.option(
    "--out",
    type=click.Path(),
    required=True,
    help="Output file path for AI-BOM JSON",
)
def scan(repo: str, out: str):
    try:
        from brox.scan.scanner import scan_repository

        click.echo(f"Scanning repository: {repo}")
        aibom = scan_repository(repo)

        bom_hash = aibom.calculate_hash()
        aibom.metadata.properties.append(
            Property(name="brox.bom.hash", value=bom_hash)
        )

        output_path = Path(out)
        output_path.parent.mkdir(parents=True, exist_ok=True)
        with output_path.open("w") as f:
            f.write(aibom.to_json())

        click.echo(f"AI-BOM written to: {out}")
        click.echo(f"Components: {len(aibom.components)}")
        click.echo(f"Services: {len(aibom.services)}")
        sys.exit(EXIT_SUCCESS)

    except Exception as e:
        click.echo(f"Error during scan: {e}", err=True)
        if "--debug" in sys.argv:
            import traceback

            traceback.print_exc()
        sys.exit(EXIT_ERROR)


@main.command()
@click.option(
    "--base",
    type=click.Path(exists=True, dir_okay=False),
    required=True,
    help="Path to base AI-BOM JSON",
)
@click.option(
    "--head",
    type=click.Path(exists=True, dir_okay=False),
    required=True,
    help="Path to head AI-BOM JSON",
)
@click.option(
    "--out",
    type=click.Path(),
    required=True,
    help="Output file path for capability diff JSON",
)
@click.option(
    "--md",
    type=click.Path(),
    help="Optional output file path for capability diff Markdown",
)
def diff(base: str, head: str, out: str, md: str):
    try:
        from brox.diff.diff import compute_diff
        from brox.emit import emit_markdown_diff

        click.echo(f"Computing diff: {base} -> {head}")

        with open(base) as f:
            base_bom = json.load(f)
        with open(head) as f:
            head_bom = json.load(f)

        cap_diff = compute_diff(base_bom, head_bom)

        output_path = Path(out)
        output_path.parent.mkdir(parents=True, exist_ok=True)
        with output_path.open("w") as f:
            f.write(cap_diff.to_json())

        click.echo(f"Capability diff written to: {out}")
        click.echo(f"Risk level: {cap_diff.risk.level.upper()}")
        click.echo(f"Assets added: {len(cap_diff.assets_added)}")
        click.echo(f"Capabilities added: {len(cap_diff.capabilities_added)}")
        click.echo(f"Capabilities widened: {len(cap_diff.capabilities_widened)}")

        if md:
            md_content = emit_markdown_diff(cap_diff)
            md_path = Path(md)
            md_path.parent.mkdir(parents=True, exist_ok=True)
            with md_path.open("w") as f:
                f.write(md_content)
            click.echo(f"Markdown diff written to: {md}")

        sys.exit(EXIT_SUCCESS)

    except Exception as e:
        click.echo(f"Error during diff: {e}", err=True)
        if "--debug" in sys.argv:
            import traceback

            traceback.print_exc()
        sys.exit(EXIT_ERROR)


@main.command()
@click.option(
    "--diff",
    "diff_file",
    type=click.Path(exists=True, dir_okay=False),
    required=True,
    help="Path to capability diff JSON",
)
@click.option(
    "--policy",
    type=click.Path(exists=True, dir_okay=False),
    required=True,
    help="Path to policy YAML file",
)
def gate(diff_file: str, policy: str):
    try:
        from brox.policy.engine import evaluate_policy

        click.echo(f"Evaluating policy: {policy}")
        click.echo(f"Against diff: {diff_file}")

        with open(diff_file) as f:
            diff_data = json.load(f)

        result = evaluate_policy(diff_data, policy)

        if result["triggered_rules"]:
            click.echo("\nTriggered rules:")
            for rule in result["triggered_rules"]:
                if rule["action"] == "block":
                    action_symbol = "Fail"
                else:
                    action_symbol = "Warning"
                click.echo(f"  {action_symbol} [{rule['id']}] {rule['message']}")

        if result["decision"] == "block":
            click.echo("\n BLOCKED by policy", err=True)
            sys.exit(EXIT_POLICY_FAIL)
        elif result["decision"] == "warn":
            click.echo("\n WARNINGS present, but passing")
            sys.exit(EXIT_SUCCESS)
        else:
            click.echo("\n PASSED policy checks")
            sys.exit(EXIT_SUCCESS)

    except Exception as e:
        click.echo(f"Error during policy evaluation: {e}", err=True)
        if "--debug" in sys.argv:
            import traceback

            traceback.print_exc()
        sys.exit(EXIT_ERROR)


if __name__ == "__main__":
    main()
