"""Report and CI-related CLI commands for glreview."""

from __future__ import annotations

import subprocess  # noqa: S404
import sys
from pathlib import Path

import click

from .discovery import sync_registry
from .git import count_lines, get_current_commit
from .registry import ModuleStatus, load_registry, save_registry
from .review_service import ReviewService
from .review_state import ReviewStateChecker


def register_commands(main: click.Group) -> None:
    """Register report/CI commands with the main CLI group."""
    main.add_command(report)
    main.add_command(check)
    main.add_command(ci_config)
    main.add_command(sync)


@click.command()
@click.pass_context
def sync(ctx: click.Context) -> None:
    """Sync registry with filesystem."""
    config = ctx.obj["config"]
    registry = load_registry(config.registry_path)

    added, removed = sync_registry(
        config.root,
        config.sources,
        config.exclude,
        registry.modules,
    )

    if not added and not removed:
        click.echo("Registry is in sync with filesystem.")
        return

    if added:
        click.echo(f"Adding {len(added)} new modules:")
        for path in added:
            lines = count_lines(path, cwd=config.root)

            registry.set(
                ModuleStatus(
                    path=path,
                    status="needs_review",
                    lines=lines,
                )
            )
            click.echo(f"  + {path}")

    if removed:
        click.echo(f"Removing {len(removed)} deleted modules:")
        for path in removed:
            registry.remove(path)
            click.echo(f"  - {path}")

    commit = get_current_commit(cwd=config.root)
    save_registry(registry, config.registry_path, commit=commit)

    click.secho("✓ Registry synced.", fg="green")


@click.command()
@click.option("--base", default="origin/main", help="Base branch to compare against")
@click.pass_context
def check(ctx: click.Context, base: str) -> None:
    """CI check for changes to reviewed files."""
    config = ctx.obj["config"]
    registry = load_registry(config.registry_path)

    # Use ReviewStateChecker for consistent stale detection
    state_checker = ReviewStateChecker(config)
    state = state_checker.get_registry_state(registry)

    if not state.reviewed_stale:
        click.secho("✓ No reviewed files have changed.", fg="green")
        sys.exit(0)

    # Collect warnings
    warnings = []
    for info in state.reviewed_stale:
        mod = info.module
        msg = f"{mod.path} changed since review ({mod.last_reviewed_commit})"
        warnings.append(msg)

    click.secho("⚠ Reviewed files have changed:", fg="yellow")
    for msg in warnings:
        click.echo(f"  {msg}")

    click.echo()
    click.echo("Consider re-reviewing these modules after merging.")
    sys.exit(1)


@click.command()
@click.option(
    "--sync", "do_sync", is_flag=True, help="Run sync before generating report"
)
@click.option(
    "--format",
    "fmt",
    type=click.Choice(["markdown", "json", "badge"]),
    default="markdown",
    help="Output format",
)
@click.option("--output", "-o", type=click.Path(), help="Write report to file")
@click.option(
    "--commit", "do_commit", is_flag=True, help="Commit registry changes (for CI)"
)
@click.option(
    "--message", "-m", default="Update review registry", help="Commit message"
)
@click.pass_context
def report(
    ctx: click.Context,
    do_sync: bool,
    fmt: str,
    output: str | None,
    do_commit: bool,
    message: str,
) -> None:
    """Generate review coverage report."""
    config = ctx.obj["config"]

    # Run sync first if requested
    if do_sync:
        ctx.invoke(sync)
        click.echo()

    registry = load_registry(config.registry_path)

    if not registry.modules:
        click.echo("No modules in registry. Run 'glreview init' first.")
        sys.exit(1)

    # Use ReviewService for consistent state calculation
    service = ReviewService(registry, config)
    summary = service.get_summary()
    state = service.get_registry_state()

    reviewed = summary["reviewed"]
    in_progress = summary["in_progress"]
    needs_review = summary["needs_review"]
    total_lines = summary["total_lines"]
    reviewed_lines = summary["reviewed_lines"]
    coverage_pct = summary["coverage_pct"]
    line_coverage_pct = summary["line_coverage_pct"]

    # Generate report in requested format
    if fmt == "json":
        import json

        report_text = json.dumps(summary, indent=2)

    elif fmt == "badge":
        # Generate shields.io badge URL
        color = _get_coverage_color(coverage_pct)
        badge_url = f"https://img.shields.io/badge/review-{coverage_pct:.0f}%25-{color}"
        report_text = badge_url

    else:  # markdown
        report_text = _generate_markdown_report(
            summary,
            state,
            coverage_pct,
            line_coverage_pct,
            reviewed,
            in_progress,
            needs_review,
            total_lines,
            reviewed_lines,
        )

    # Output report
    if output:
        Path(output).write_text(report_text)
        click.echo(f"Report written to {output}")
    else:
        click.echo(report_text)

    # Commit if requested
    if do_commit:
        _commit_registry_changes(config, output, message)


def _get_coverage_color(coverage_pct: float) -> str:
    """Get shields.io color based on coverage percentage."""
    if coverage_pct >= 90:
        return "brightgreen"
    elif coverage_pct >= 70:
        return "green"
    elif coverage_pct >= 50:
        return "yellow"
    else:
        return "red"


def _generate_markdown_report(
    summary: dict,
    state,
    coverage_pct: float,
    line_coverage_pct: float,
    reviewed: int,
    in_progress: int,
    needs_review: int,
    total_lines: int,
    reviewed_lines: int,
) -> str:
    """Generate a markdown report."""
    color = _get_coverage_color(coverage_pct)
    badge_url = f"https://img.shields.io/badge/review-{coverage_pct:.0f}%25-{color}"

    report_text = f"""## Review Coverage

![Coverage]({badge_url})

| Status | Modules | Lines |
|--------|---------|-------|
| ✓ Reviewed | {reviewed} | {reviewed_lines:,} |
| ◐ In Progress | {in_progress} | - |
| ○ Needs Review | {needs_review} | {total_lines - reviewed_lines:,} |

**Coverage:** {coverage_pct:.0f}% of modules, {line_coverage_pct:.0f}% of lines
"""

    # In Progress section
    if state.in_progress:
        report_text += "\n### In Progress\n\n"
        report_text += "| Module | Assignee | Issue |\n"
        report_text += "|--------|----------|-------|\n"
        for info in sorted(state.in_progress, key=lambda i: i.module.path):
            mod = info.module
            assignee = mod.assigned_reviewer or "unassigned"
            issue = ReviewStateChecker.format_issue_link(mod.review_issue)
            report_text += f"| `{mod.path}` | {assignee} | {issue} |\n"

    # Needs Review section
    if state.needs_review:
        report_text += "\n### Needs Review\n\n"
        report_text += "| Module | Lines |\n"
        report_text += "|--------|-------|\n"
        for info in sorted(state.needs_review, key=lambda i: i.module.path):
            mod = info.module
            report_text += f"| `{mod.path}` | {mod.lines} |\n"

    # Reviewed but Changed section
    if state.reviewed_stale:
        report_text += "\n### ⚠️ Reviewed but Changed\n\n"
        report_text += "These modules have changed since their last review "
        report_text += "and may need re-review.\n\n"
        report_text += "| Module | Last Reviewed | Commit | Reviewers |\n"
        report_text += "|--------|---------------|--------|----------|\n"
        for info in sorted(state.reviewed_stale, key=lambda i: i.module.path):
            mod = info.module
            reviewers = ", ".join(mod.reviewers) if mod.reviewers else "-"
            date = mod.last_reviewed_date or "-"
            commit = mod.last_reviewed_commit[:7] if mod.last_reviewed_commit else "-"
            report_text += f"| `{mod.path}` | {date} | {commit} | {reviewers} |\n"

    # Reviewed and current section
    if state.reviewed_current:
        report_text += "\n### ✅ Reviewed\n\n"
        report_text += "| Module | Date | Reviewers |\n"
        report_text += "|--------|------|----------|\n"
        for info in sorted(state.reviewed_current, key=lambda i: i.module.path):
            mod = info.module
            reviewers = ", ".join(mod.reviewers) if mod.reviewers else "-"
            date = mod.last_reviewed_date or "-"
            report_text += f"| `{mod.path}` | {date} | {reviewers} |\n"

    return report_text


def _commit_registry_changes(config, output: str | None, message: str) -> None:
    """Commit registry changes for CI."""
    # Check if there are changes to commit
    result = subprocess.run(  # noqa: S607
        ["git", "status", "--porcelain", config.registry],
        cwd=config.root,
        capture_output=True,
        text=True,
    )

    if not result.stdout.strip():
        click.echo("No changes to commit.")
        return

    # Stage and commit
    subprocess.run(  # noqa: S607
        ["git", "add", config.registry],
        cwd=config.root,
        check=True,
    )

    if output:
        subprocess.run(  # noqa: S607
            ["git", "add", output],
            cwd=config.root,
            check=False,  # May not exist in repo yet
        )

    result = subprocess.run(  # noqa: S607
        ["git", "commit", "-m", message],
        cwd=config.root,
        capture_output=True,
        text=True,
    )

    if result.returncode == 0:
        click.secho(f"✓ Committed: {message}", fg="green")
    else:
        click.secho(f"Warning: Commit failed: {result.stderr}", fg="yellow")


@click.command("ci-config")
def ci_config() -> None:
    """Print recommended GitLab CI configuration."""
    # Build config as list of lines to avoid E501 in the YAML content
    lines = [
        "# Add to .gitlab-ci.yml",
        "#",
        "# Required: Create a Project Access Token with write_repository scope",
        "# and add it as CI variable PUSH_TOKEN (Settings -> CI/CD -> Variables)",
        "",
        "stages:",
        "  - build",
        "  - test",
        "  - review",
        "  - deploy",
        "",
        "# Sync and report on MR pushes",
        "review-mr:",
        "  stage: review",
        "  image: python:3.12",
        "  variables:",
        '    GIT_TERMINAL_PROMPT: "0"',
        "    ORIGIN: https://oauth2:${PUSH_TOKEN}@${CI_SERVER_HOST}/"
        "${CI_PROJECT_PATH}.git",
        "  script:",
        "    - pip install glreview",
        '    - git config user.email "gitlab-ci@$CI_SERVER_HOST"',
        '    - git config user.name "GitLab CI"',
        '    - git remote set-url origin "$ORIGIN"',
        "    - git fetch origin $CI_COMMIT_REF_NAME",
        "    - git checkout -B $CI_COMMIT_REF_NAME origin/$CI_COMMIT_REF_NAME",
        "    - glreview report --sync --format markdown --output REVIEW_COVERAGE.md",
        "    - git add REVIEW_COVERAGE.md .glreview/ || true",
        "    - 'git diff --cached --quiet || "
        'git commit -m "chore: update review registry"\'',
        "    - git pull --rebase origin $CI_COMMIT_REF_NAME || true",
        "    - 'git diff origin/$CI_COMMIT_REF_NAME --quiet && "
        'echo "No changes" || git push origin HEAD:$CI_COMMIT_REF_NAME -o ci.skip\'',
        "  rules:",
        '    - if: $CI_PIPELINE_SOURCE == "merge_request_event"',
        "",
        "# Sync and report on main branch",
        "review-main:",
        "  stage: review",
        "  image: python:3.12",
        "  variables:",
        '    GIT_TERMINAL_PROMPT: "0"',
        "    ORIGIN: https://oauth2:${PUSH_TOKEN}@${CI_SERVER_HOST}/"
        "${CI_PROJECT_PATH}.git",
        "  script:",
        "    - pip install glreview",
        '    - git config user.email "gitlab-ci@$CI_SERVER_HOST"',
        '    - git config user.name "GitLab CI"',
        '    - git remote set-url origin "$ORIGIN"',
        "    - git fetch origin $CI_COMMIT_BRANCH",
        "    - git checkout -B $CI_COMMIT_BRANCH origin/$CI_COMMIT_BRANCH",
        "    - glreview report --sync --format markdown --output REVIEW_COVERAGE.md",
        "    - git add REVIEW_COVERAGE.md .glreview/ || true",
        "    - 'git diff --cached --quiet || "
        'git commit -m "chore: update review registry"\'',
        "    - git pull --rebase origin $CI_COMMIT_BRANCH || true",
        "    - 'git diff origin/$CI_COMMIT_BRANCH --quiet && "
        'echo "No changes" || git push origin HEAD:$CI_COMMIT_BRANCH -o ci.skip\'',
        "  rules:",
        "    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH",
        "      changes:",
        '        - "src/**/*.py"  # Adjust to match your sources pattern',
        '        - ".glreview/**"',
        "",
        "# Block releases without 100% coverage (optional)",
        "release-gate:",
        "  stage: review",
        "  image: python:3.12",
        "  script:",
        "    - pip install glreview",
        "    - |",
        "      COVERAGE=$(glreview report --format json | \\",
        '        python -c "import sys,json; '
        "print(int(json.load(sys.stdin)['coverage_pct']))\")",
        '      if [ "$COVERAGE" -lt 100 ]; then',
        '        echo "Review coverage is ${COVERAGE}%, need 100% for release"',
        "        glreview status",
        "        exit 1",
        "      fi",
        '      echo "Review coverage is 100% ✓"',
        "  rules:",
        "    - if: $CI_COMMIT_TAG",
    ]
    click.echo("\n".join(lines))
