"""Rescue command registration."""

from __future__ import annotations

import json
import sys
import time
from pathlib import Path
from typing import Any

import click

from g_gremlin.safety import SafetyContext
from g_gremlin.cli import (
    _apply_auto_fixes,
    _bands_to_expected_map,
    _fetch_sheet_grids,
    _get_all_sheet_names_throttled,
    _get_google_client,
    _get_http_error,
    _get_repair_helpers,
    _get_snapshot,
    _handle_missing_sheet_issue,
    _handle_restore_on_error,
    _print_rescue_banner,
    _print_rescue_complete,
    _print_rescue_damage,
    _write_rescue_receipt,
    cli,
)


@cli.command("rescue")
@click.argument("spreadsheet_id")
@click.option("--dry-run", is_flag=True, help="Show what would be fixed without applying.")
@click.option("--auto", is_flag=True, help="Skip prompts; use best-guess for ambiguous fixes.")
@click.option("--receipt", is_flag=True, help="Write a shareable rescue receipt to .g_gremlin/rescues/.")
@click.option("--output", "-o", "output_path", default=".g_gremlin/rescue_report.json", show_default=True, help="Save detailed JSON report.")
@click.option("--band", "bands", multiple=True, help='Optional band spec like "J:M=FY26_Assumptions" to suppress expected cross-sheet anomalies.')
@click.option("--backup/--no-backup", default=True, show_default=True, help="Create snapshot before applying fixes (default on for safety).")
@click.option("--throttle/--no-throttle", default=True, help="Throttle read requests to avoid quota issues.")
@click.option(
    "--apply",
    is_flag=True,
    help="Execute fixes (default is apply unless --dry-run is provided).",
)
@click.option(
    "--json-summary",
    "json_summary",
    is_flag=True,
    help="Emit AgenticResult-style JSON summary.",
)
@click.option(
    "--auto-rollback",
    is_flag=True,
    help="Automatically restore from backup on error (for CI/agents).",
)
def rescue(
    spreadsheet_id: str,
    dry_run: bool,
    auto: bool,
    receipt: bool,
    output_path: str,
    bands: tuple[str, ...],
    backup: bool,
    throttle: bool,
    apply: bool,
    json_summary: bool,
    auto_rollback: bool,
) -> None:
    """
    One-shot rescue: scan all tabs, fix what can be fixed, and show the transformation.
    """
    apply_effective = apply or not dry_run
    safety_ctx = SafetyContext(command="g-gremlin rescue", remote_mutation=True)
    if apply_effective:
        safety_ctx.enforce_apply(apply_effective)

    start_ts = time.time()
    expected_sheet_map = _bands_to_expected_map(bands)
    try:
        service = _get_google_client().get_sheets_service()
        sheet_names = _get_all_sheet_names_throttled(service, spreadsheet_id, throttle=throttle)
        if not sheet_names:
            click.echo("No sheets found.")
            return
        # Create snapshot before applying fixes
        snapshot_result = None
        if backup and apply_effective:
            snap_mod = _get_snapshot()
            from datetime import datetime
            timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
            snapshot_name = f"pre-rescue-{timestamp}"
            try:
                cmd_metadata = snap_mod.collect_command_metadata()
                snapshot_result = snap_mod.create_snapshot(
                    spreadsheet_id=spreadsheet_id,
                    tabs=sheet_names,
                    name=snapshot_name,
                    metadata=cmd_metadata,
                )
                click.echo(f"Created backup snapshot: {snapshot_name}")
            except Exception as e:
                click.echo(f"Warning: Failed to create backup snapshot: {e}", err=True)

        plans: list[dict[str, Any]] = []
        before_health_scores: list[int] = []
        total_cells = 0
        total_issues = 0
        skipped_sheets: list[str] = []
        for name in sheet_names:
            tab_range = f"'{name}'!A1:ZZ500"
            try:
                values_grid, formulas_grid, _ = _fetch_sheet_grids(
                    service, spreadsheet_id, tab_range, throttle=throttle, include_formatted=False
                )
                cells = _get_repair_helpers().cells_from_grids(tab_range, values_grid, formulas_grid)
                plan = _get_repair_helpers().analyze_sheet(cells, sheet_names, expected_sheet_map=expected_sheet_map or None)
                plans.append({"sheet": name, "plan": plan, "cells": cells})
                before_health_scores.append(plan.health_score)
                total_cells += len(cells)
                total_issues += len(plan.issues)
            except _get_http_error() as sheet_err:
                skipped_sheets.append(name)
                click.echo(f"  [SKIP] {name}: {sheet_err.reason if hasattr(sheet_err, 'reason') else sheet_err}")
            except Exception as sheet_exc:
                skipped_sheets.append(name)
                click.echo(f"  [SKIP] {name}: {sheet_exc}")
    except _get_http_error() as err:
        click.echo(f"Error scanning sheets: {err}")
        sys.exit(1)
    except Exception as exc:
        click.echo(f"Error: {exc}")
        sys.exit(1)

    before_health = int(sum(before_health_scores) / len(before_health_scores)) if before_health_scores else 100
    before_grade = _get_repair_helpers().health_grade(before_health)
    _print_rescue_banner()
    _print_rescue_damage(plan_summaries=plans, total_issues=total_issues, health=before_health, grade=before_grade)

    report = {
        "spreadsheet_id": spreadsheet_id,
        "sheets": [
            {
                "sheet": entry["sheet"],
                "summary": _get_repair_helpers().summarize_issues(entry["plan"].issues),
                "health_score": entry["plan"].health_score,
                "health_grade": _get_repair_helpers().health_grade(entry["plan"].health_score),
                "issues": [iss.to_dict() for iss in entry["plan"].issues],
            }
            for entry in plans
        ],
        "overall": {
            "health_score": before_health,
            "health_grade": before_grade,
            "issues_total": total_issues,
        },
    }

    out_path = Path(output_path)
    out_path.parent.mkdir(parents=True, exist_ok=True)
    out_path.write_text(json.dumps(report, indent=2), encoding="utf-8")

    if dry_run or not apply_effective:
        click.echo(f"Rescue dry-run report written to {out_path}")
        if json_summary:
            agentic = safety_ctx.build_result(
                status="success",
                dry_run=True,
                result={
                    "sheets_scanned": len(sheet_names),
                    "issues_total": total_issues,
                    "health_before": before_health,
                    "health_grade_before": before_grade,
                    "skipped_sheets": skipped_sheets,
                    "report_path": str(out_path),
                },
            )
            click.echo(json.dumps(agentic.to_dict(), indent=2))
        return

    if not auto and not click.confirm("Proceed with rescue?"):
        click.echo("Rescue aborted.")
        return

    try:
        applied = 0
        service = _get_google_client().get_sheets_service()
        for entry in plans:
            applied += _apply_auto_fixes(spreadsheet_id, entry["plan"], dry_run=False)
            missing_issues = [i for i in entry["plan"].issues if i.type == "missing_sheet_ref"]
            for issue in missing_issues:
                applied += _handle_missing_sheet_issue(service, spreadsheet_id, issue, auto)
    except Exception as e:
        _handle_restore_on_error(e, snapshot_result, auto_rollback, json_summary)
        raise

    # Re-scan to compute after health
    after_health_scores: list[int] = []
    remaining_issues = 0
    for name in sheet_names:
        if name in skipped_sheets:
            continue
        tab_range = f"'{name}'!A1:ZZ500"
        try:
            values_grid, formulas_grid, _ = _fetch_sheet_grids(
                service, spreadsheet_id, tab_range, throttle=throttle, include_formatted=False
            )
            cells = _get_repair_helpers().cells_from_grids(tab_range, values_grid, formulas_grid)
            plan = _get_repair_helpers().analyze_sheet(cells, sheet_names, expected_sheet_map=expected_sheet_map or None)
            after_health_scores.append(plan.health_score)
            remaining_issues += len(plan.issues)
        except Exception:
            pass  # Already skipped in initial scan

    after_health = int(sum(after_health_scores) / len(after_health_scores)) if after_health_scores else before_health
    after_grade = _get_repair_helpers().health_grade(after_health)
    duration = int(time.time() - start_ts)
    _print_rescue_complete(before_health, after_health, before_grade, after_grade, applied, duration)

    if receipt or applied > 0:
        _write_rescue_receipt(
            spreadsheet_id=spreadsheet_id,
            before_health=before_health,
            before_grade=before_grade,
            after_health=after_health,
            after_grade=after_grade,
            fixes_applied=applied,
            issues_before=total_issues,
            issues_after=remaining_issues,
            duration_seconds=duration,
        )
        if receipt:
            click.echo("Rescue receipt saved to .g_gremlin/rescues/")

    if json_summary:
        result_data = {
            "sheets_scanned": len(sheet_names),
            "applied_fixes": applied,
            "issues_before": total_issues,
            "issues_after": remaining_issues,
            "health_before": before_health,
            "health_after": after_health,
            "health_grade_before": before_grade,
            "health_grade_after": after_grade,
            "duration_seconds": duration,
        }
        # Add snapshot info if backup was created
        if snapshot_result:
            result_data["snapshot_name"] = snapshot_result.name
            result_data["snapshot_path"] = snapshot_result.file_path
            result_data["restore_command"] = f"g-gremlin snapshot restore --name {snapshot_result.name}"
            if snapshot_result.concurrency_token:
                result_data["etag"] = snapshot_result.concurrency_token
            if snapshot_result.shape:
                result_data["shape"] = snapshot_result.shape
            if snapshot_result.correlation_id:
                result_data["correlation_id"] = snapshot_result.correlation_id
        agentic = safety_ctx.build_result(
            status="success",
            dry_run=False,
            result=result_data,
        )
        click.echo(json.dumps(agentic.to_dict(), indent=2))
