"""
DeepSweep CLI - Security validation for AI coding assistants.

You don't need to understand the code to secure it.
"""

import json
import sys
import urllib.request
from pathlib import Path
from urllib.parse import urlparse

import click

from deepsweep import __version__
from deepsweep.cli_mcp import mcp
from deepsweep.commands import doctor, init, share
from deepsweep.community import maybe_show_suggestion
from deepsweep.constants import SEVERITY_ORDER
from deepsweep.exceptions import DeepSweepError
from deepsweep.output import OutputConfig, OutputFormatter
from deepsweep.telemetry import get_telemetry_client
from deepsweep.validator import validate_path


def _show_first_run_notice() -> None:
    """Show first-run telemetry notice (optimistic messaging)."""
    notice = """
[INFO] DeepSweep uses a two-tier telemetry system:

  ESSENTIAL (Always Active):
  - Threat intelligence signals power community security
  - Pattern effectiveness tracking
  - Zero-day detection
  - Benefits all users

  OPTIONAL (You Control):
  - Product analytics for improvements
  - Activation and retention metrics
  - Feature usage patterns

  What we NEVER collect:
  - Your code or file contents
  - File paths or repository names
  - Personally identifiable information

  Disable optional analytics: deepsweep telemetry disable
  Fully offline mode: export DEEPSWEEP_OFFLINE=1

  Learn more: https://docs.deepsweep.ai/telemetry
"""
    click.echo(notice)


@click.group(invoke_without_command=True)
@click.version_option(__version__, prog_name="deepsweep")
@click.pass_context
def main(ctx: click.Context) -> None:
    """
    Security for AI-built apps.

    Check your AI-generated code for security issues.
    Results in plain English, not security jargon.

    Quick start:

    \b
        deepsweep validate .

    Learn more: https://docs.deepsweep.ai
    """
    # Show first-run notice
    telemetry = get_telemetry_client()
    if telemetry.config.first_run and ctx.invoked_subcommand not in ("telemetry", None):
        _show_first_run_notice()

    # Identify user for analytics
    telemetry.identify()

    if ctx.invoked_subcommand is None:
        click.echo(ctx.get_help())


# Register MCP command group
main.add_command(mcp)

# Register new commands
main.add_command(init)
main.add_command(doctor)
main.add_command(share)


@main.command()
@click.argument("path", default=".", type=click.Path(exists=True))
@click.option(
    "--format",
    "-f",
    "output_format",
    type=click.Choice(["text", "json", "sarif"]),
    default="text",
    help="Output format",
)
@click.option(
    "--output",
    "-o",
    "output_file",
    type=click.Path(),
    default=None,
    help="Output file (default: stdout)",
)
@click.option(
    "--no-color",
    is_flag=True,
    help="Disable colored output",
)
@click.option(
    "--fail-on",
    type=click.Choice(["critical", "high", "medium", "low"]),
    default="high",
    help="Exit non-zero if findings at or above this severity",
)
@click.option(
    "--verbose",
    "-v",
    is_flag=True,
    help="Show verbose output",
)
@click.option(
    "--include-mcp",
    is_flag=True,
    default=False,
    help="Include MCP configuration checking (becomes default in v0.3.0)",
)
@click.option(
    "--no-share",
    is_flag=True,
    default=False,
    help="Skip the post-validation share prompt",
)
def validate(
    path: str,
    output_format: str,
    output_file: str | None,
    no_color: bool,
    fail_on: str,
    verbose: bool,
    include_mcp: bool,
    no_share: bool,
) -> None:
    """
    Check your code for security issues.

    Examines AI-generated code and configurations for common security
    problems. Returns results in plain English with actionable fixes.

    Examples:

    \b
        deepsweep validate .
        deepsweep validate ./my-project --fail-on critical
        deepsweep validate . --format sarif --output report.sarif
    """
    telemetry = get_telemetry_client()
    config = OutputConfig(
        use_color=not no_color,
        verbose=verbose,
    )
    formatter = OutputFormatter(config)

    # Check quota before validation
    import os

    from deepsweep.quota import check_quota, record_validation

    offline_mode = os.environ.get("DEEPSWEEP_OFFLINE") == "1"
    can_validate, quota_message = check_quota(offline_mode=offline_mode)

    if not can_validate:
        click.echo("\n" + quota_message)
        telemetry.shutdown()
        sys.exit(1)

    exit_code = 0
    result = None
    reputation_response = None

    try:
        result = validate_path(Path(path))

        # Record validation usage for quota tracking
        record_validation(offline_mode=offline_mode)

        # Update reputation after successful validation

        from deepsweep.reputation.api_client import ReputationAPIClient

        offline_mode = os.environ.get("DEEPSWEEP_OFFLINE") == "1"
        reputation_client = ReputationAPIClient(offline_mode=offline_mode)

        # Extract pattern IDs from findings
        pattern_ids = [f.pattern_id for f in result.all_findings]

        # Submit validation and get reputation score
        reputation_response = reputation_client.submit_validation(
            grade=result.grade_letter,
            finding_count=len(result.all_findings),
            pattern_ids=pattern_ids,
            duration_ms=0,  # Duration tracking can be added later
        )

        # Track reputation metrics
        if reputation_response:
            from deepsweep.reputation.metrics import (
                track_score_update,
                track_streak,
            )

            # Track score update
            if reputation_response.score != 0:
                previous_score = reputation_response.score - (
                    result.score - reputation_response.score
                )
                track_score_update(
                    previous_score=previous_score,
                    new_score=reputation_response.score,
                    delta=result.score - previous_score,
                    tier=reputation_response.tier,
                    trigger="validation",
                )

            # Track streak
            if reputation_response.streak_days > 0:
                track_streak(
                    event_type="extended",
                    streak_days=reputation_response.streak_days,
                )

            # Track tier changes (if we had previous tier data)
            # This would require storing previous tier, which we'll add later

    except DeepSweepError as e:
        click.echo(f"[FAIL] {e.message}", err=True)
        telemetry.track_error(
            command="validate",
            error_type=type(e).__name__,
            error_message=e.message,
        )
        telemetry.shutdown()
        sys.exit(1)

    # MCP validation (opt-in for v0.2.0)
    mcp_findings_count = 0
    mcp_configs_found = 0

    if include_mcp:
        from deepsweep.mcp import discovery
        from deepsweep.mcp import validator as mcp_validator
        from deepsweep.models import FileResult, Finding, Severity

        mcp_configs = discovery.discover_with_servers(Path(path))
        mcp_configs_found = len(mcp_configs)

        # Collect MCP findings
        mcp_findings_list = []
        for config in mcp_configs:
            mcp_result = mcp_validator.validate(config)
            for mcp_finding in mcp_result.findings:
                # Convert MCP finding to DeepSweep Finding
                severity_map = {
                    "critical": Severity.CRITICAL,
                    "high": Severity.HIGH,
                    "medium": Severity.MEDIUM,
                    "low": Severity.LOW,
                }
                finding = Finding(
                    pattern_id=mcp_finding.pattern_id,
                    severity=severity_map.get(mcp_finding.severity, Severity.MEDIUM),
                    message=mcp_finding.message,
                    file_path=mcp_finding.file,
                    line=mcp_finding.line or 1,  # Default to line 1 if not specified
                    remediation=mcp_finding.fix_suggestion,
                )
                mcp_findings_list.append(finding)
                mcp_findings_count += 1

        # If we have MCP findings, add them as a new FileResult
        if mcp_findings_list:
            mcp_file_result = FileResult(
                path="MCP Configurations",
                findings=tuple(mcp_findings_list),
            )
            # Create a new ValidationResult with MCP findings included
            from deepsweep.models import ValidationResult

            result = ValidationResult(
                files=(*result.files, mcp_file_result),
                pattern_count=result.pattern_count,
            )

    # Generate output
    if output_format == "json":
        output = formatter.format_json_output(result)
        # Add MCP metadata (backward compatible - additive only)
        if include_mcp:
            output_dict = json.loads(output)
            output_dict["mcp_included"] = True
            output_dict["mcp_configs_found"] = mcp_configs_found
            output_dict["mcp_findings_count"] = mcp_findings_count
            output = json.dumps(output_dict, indent=2)
    elif output_format == "sarif":
        output = formatter.format_sarif_output(result)
    else:
        # Text format
        lines = [formatter.format_header(__version__)]
        lines.append(formatter.format_validation_start(path, result.pattern_count))

        # File results
        for file_result in result.files:
            if file_result.skipped:
                lines.append(
                    formatter.format_file_skip(
                        file_result.path, file_result.skip_reason or "unknown"
                    )
                )
            elif file_result.has_findings:
                for finding in file_result.findings:
                    lines.append(formatter.format_finding(finding))
            else:
                lines.append(formatter.format_file_pass(file_result.path))

        lines.append(formatter.format_summary(result, reputation=reputation_response))
        lines.append(formatter.format_next_steps(result))

        output = "\n".join(lines)

    # Write output
    if output_file:
        Path(output_file).write_text(output)
        click.echo(f"[PASS] Results written to {output_file}")
    else:
        click.echo(output)

    # Show community suggestion for successful validation
    if output_format == "text":
        maybe_show_suggestion(result.score, result.grade_letter)

    # Show post-validation share trigger (interactive terminals only)
    if output_format == "text" and not output_file and not no_share:
        try:
            import sys as _sys

            if _sys.stdin.isatty():
                from deepsweep.share.trigger import show_share_trigger

                passed_count = result.pattern_count - len(result.all_findings)
                total_count = result.pattern_count

                _project_name = Path(path).resolve().name

                share_action = show_share_trigger(
                    grade=result.grade_letter,
                    score=result.score,
                    passed_count=passed_count,
                    total_count=total_count,
                    project_name=_project_name,
                )

                if share_action:
                    telemetry.track_command(
                        command="share_trigger",
                        exit_code=0,
                        output_format=share_action,
                        score=result.score,
                        grade=result.grade_letter,
                    )
        except Exception:
            pass  # Never let the share trigger break validation

    # Exit code based on --fail-on
    if result.has_findings:
        threshold = SEVERITY_ORDER.index(fail_on)
        for finding in result.all_findings:
            finding_level = SEVERITY_ORDER.index(finding.severity.value.lower())
            if finding_level >= threshold:
                exit_code = 1
                break

    # Track telemetry (both essential + optional tiers)
    telemetry.track_command(
        command="validate",
        exit_code=exit_code,
        findings_count=len(result.all_findings),
        pattern_count=result.pattern_count,
        output_format=output_format,
        score=result.score,
        grade=result.grade_letter,
    )
    telemetry.shutdown()

    sys.exit(exit_code)


@main.command()
@click.option(
    "--output",
    "-o",
    default="badge.svg",
    help="Output file path",
)
@click.option(
    "--format",
    "-f",
    "output_format",
    type=click.Choice(["svg", "json", "markdown"]),
    default="svg",
    help="Output format",
)
def badge(output: str, output_format: str) -> None:
    """
    Generate security badge for README.

    Examples:

    \b
        deepsweep badge
        deepsweep badge --output my-badge.svg
        deepsweep badge --format markdown
    """
    telemetry = get_telemetry_client()

    try:
        result = validate_path(Path())
        score = result.score
        grade = result.grade_letter

        # Determine color
        if score >= 90:
            color = "4ade80"
        elif score >= 70:
            color = "f59e0b"
        else:
            color = "ef4444"

        if output_format == "json":

            content = json.dumps(
                {
                    "schemaVersion": 1,
                    "label": "DeepSweep",
                    "message": f"{score}/100 ({grade})",
                    "color": color,
                },
                indent=2,
            )
        elif output_format == "markdown":
            url = f"https://img.shields.io/badge/DeepSweep-{score}%2F100%20({grade})-{color}"
            content = f"[![DeepSweep]({url})](https://deepsweep.ai)"
        else:
            # SVG - fetch from shields.io
            url = f"https://img.shields.io/badge/DeepSweep-{score}%2F100%20({grade})-{color}"
            parsed = urlparse(url)
            if parsed.scheme not in ("http", "https") or not parsed.netloc:
                raise ValueError("Invalid URL scheme")

            try:
                with urllib.request.urlopen(url, timeout=10) as response: # nosec
                    content = response.read().decode("utf-8")
            except (OSError, ValueError):
                # Fallback to simple SVG
                content = f"""<svg xmlns="http://www.w3.org/2000/svg" width="120" height="20">
  <rect width="120" height="20" fill="#{color}"/>
  <text x="60" y="14" text-anchor="middle" fill="white" font-size="11">DeepSweep {score}/100</text>
</svg>"""

        Path(output).write_text(content)
        click.echo(f"[PASS] Badge saved to {output}")
        click.echo(f"[INFO] Score: {score}/100 ({grade})")

        # Track telemetry
        telemetry.track_command(
            command="badge",
            exit_code=0,
            output_format=output_format,
        )
        telemetry.shutdown()

    except Exception as e:
        click.echo(f"[FAIL] Failed to generate badge: {e}", err=True)
        telemetry.track_error(
            command="badge",
            error_type=type(e).__name__,
            error_message=str(e),
        )
        telemetry.shutdown()
        sys.exit(1)


@main.command()
def patterns() -> None:
    """List all detection patterns."""
    from deepsweep.patterns import get_all_patterns

    telemetry = get_telemetry_client()

    try:
        all_patterns = get_all_patterns()

        click.echo(f"\nDeepSweep Detection Patterns ({len(all_patterns)} total)\n")
        click.echo("-" * 60)

        for pattern in all_patterns:
            cve = f" ({pattern.cve})" if pattern.cve else ""
            click.echo(f"\n{pattern.id}: {pattern.name}{cve}")
            click.echo(f"  Severity: {pattern.severity.value}")
            click.echo(f"  Files: {', '.join(pattern.file_types)}")
            if pattern.owasp:
                click.echo(f"  OWASP: {pattern.owasp}")

        # Track telemetry
        telemetry.track_command(
            command="patterns",
            exit_code=0,
            pattern_count=len(all_patterns),
        )
        telemetry.shutdown()

    except Exception as e:
        click.echo(f"[FAIL] Failed to list patterns: {e}", err=True)
        telemetry.track_error(
            command="patterns",
            error_type=type(e).__name__,
            error_message=str(e),
        )
        telemetry.shutdown()
        sys.exit(1)


@main.group()
def telemetry() -> None:
    """Manage telemetry settings."""
    pass


@telemetry.command(name="status")
def telemetry_status() -> None:
    """Show telemetry status."""
    from deepsweep.telemetry import TelemetryConfig

    config = TelemetryConfig()
    status = config.get_status()

    click.echo("\n[INFO] DeepSweep Telemetry Status\n")
    click.echo("Two-Tier System:")
    click.echo(f"  ESSENTIAL (Threat Intel): {'Offline' if status['offline_mode'] else 'Active'}")
    click.echo(f"  OPTIONAL (Analytics): {'Enabled' if status['enabled'] else 'Disabled'}")
    click.echo()
    click.echo(f"  Anonymous UUID: {status['uuid']}")
    click.echo(f"  Config file: {status['config_file']}")

    if status["offline_mode"]:
        click.echo("\n[INFO] Offline mode enabled - ALL telemetry disabled")
        click.echo("  Set DEEPSWEEP_OFFLINE=0 to re-enable")
    elif status["enabled"]:
        click.echo("\n[INFO] Both tiers active:")
        click.echo("  Essential: Threat intelligence (powers community security)")
        click.echo("  Optional: Product analytics (helps improve DeepSweep)")
        click.echo("\n  To disable optional analytics: deepsweep telemetry disable")
    else:
        click.echo("\n[INFO] Essential tier only (threat intelligence active)")
        click.echo("  Optional analytics disabled")
        click.echo("\n  To enable analytics: deepsweep telemetry enable")

    click.echo("\n  Learn more: https://docs.deepsweep.ai/telemetry\n")


@telemetry.command(name="enable")
def telemetry_enable() -> None:
    """Enable telemetry collection."""
    from deepsweep.telemetry import TelemetryConfig

    config = TelemetryConfig()
    config.enable()

    click.echo("[PASS] Telemetry enabled")
    click.echo("[INFO] Thank you for helping us improve DeepSweep")
    click.echo("[INFO] You can disable anytime with: deepsweep telemetry disable")


@telemetry.command(name="disable")
def telemetry_disable() -> None:
    """Disable telemetry collection."""
    from deepsweep.telemetry import TelemetryConfig

    config = TelemetryConfig()
    config.disable()

    click.echo("[PASS] Telemetry disabled")
    click.echo("[INFO] Your usage data will no longer be collected")
    click.echo("[INFO] You can re-enable anytime with: deepsweep telemetry enable")


@main.command()
def score() -> None:
    """Display your current security reputation score."""
    from deepsweep.reputation.local_store import LocalReputationStore

    store = LocalReputationStore()
    data = store.load()

    if data.current_score == 0 and data.total_validations == 0:
        click.echo("[INFO] No reputation data yet.")
        click.echo("[INFO] Run 'deepsweep validate .' to check your code and earn a score.")
        return

    # Display tier emoji
    tier_emoji = {
        "diamond": "💎",
        "platinum": "🏆",
        "gold": "🥇",
        "silver": "🥈",
        "bronze": "🥉",
        "unverified": "🔰",
    }.get(data.tier, "")

    click.echo("\n" + "=" * 50)
    click.echo(f"{tier_emoji} Security Reputation Score")
    click.echo("=" * 50)
    click.echo(f"\nScore: {data.current_score}/100")
    click.echo(f"Grade: {data.last_grade or 'N/A'}")
    click.echo(f"Tier: {tier_emoji} {data.tier.title()}")

    if data.streak_days > 0:
        streak_emoji = "🔥" if data.streak_days >= 7 else "⚡"
        click.echo(f"Streak: {streak_emoji} {data.streak_days} day{'s' if data.streak_days != 1 else ''}")

    click.echo(f"\nTotal Checks: {data.total_validations}")

    if data.first_validation_date:
        click.echo(f"Member Since: {data.first_validation_date}")

    click.echo("\n" + "=" * 50)
    click.echo("\nCommands:")
    click.echo("  deepsweep validate .  - Check code and update score")
    click.echo("  deepsweep share       - Share your achievement")
    click.echo("  deepsweep badge       - Generate README badge")
    click.echo()


@main.command()
def version() -> None:
    """Show version information."""
    click.echo(f"deepsweep {__version__}")


if __name__ == "__main__":
    main()
