"""Style command registration."""

from __future__ import annotations

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

import click

from g_gremlin.a1 import parse_a1_range
from g_gremlin.safety import SafetyContext
from g_gremlin.cli import (
    _build_grid_range,
    _emit_json_payload,
    _get_google_client,
    _get_sheet_id_and_size,
    _get_snapshot,
    _handle_restore_on_error,
    _hex_to_color,
    cli,
)


@cli.command("style")
@click.option(
    "--spreadsheet-id",
    "-s",
    required=True,
    help="Spreadsheet ID.",
)
@click.option(
    "--range",
    "-r",
    "a1_range",
    required=False,
    help='A1 range with sheet name, e.g. "Tab!A1:Q200". Not needed with --from-spec.',
)
@click.option(
    "--from-spec",
    "spec_path",
    type=click.Path(exists=True, dir_okay=False, path_type=Path),
    help="Load style rules from YAML/JSON spec file.",
)
@click.option(
    "--tab",
    "tab_name",
    help="Tab name to style (required with --from-spec).",
)
@click.option("--header-rows", default=1, show_default=True, help="Number of header rows to style.")
@click.option("--header-bold/--no-header-bold", default=True, show_default=True, help="Bold header text.")
@click.option("--header-fill", default="#1f4e79", show_default=True, help="Header fill color hex.")
@click.option("--header-font-color", default="#ffffff", show_default=True, help="Header font color hex.")
@click.option("--stripe/--no-stripe", default=True, show_default=True, help="Add zebra striping to body rows.")
@click.option("--stripe-color", default="#f5f7fb", show_default=True, help="Stripe fill color hex.")
@click.option("--font-size", default=None, type=int, help="Body font size.")
@click.option("--font-color", default=None, help="Body font color hex.")
@click.option(
    "--apply",
    is_flag=True,
    help="Execute the styling (default is dry-run preview).",
)
@click.option(
    "--json-summary",
    "json_summary",
    is_flag=True,
    help="Emit AgenticResult-style JSON summary.",
)
@click.option(
    "--backup/--no-backup",
    default=True,
    show_default=True,
    help="Create snapshot before applying styles (default on for safety).",
)
@click.option(
    "--auto-rollback",
    is_flag=True,
    help="Automatically restore from backup on error (for CI/agents).",
)
def style_sheet(
    spreadsheet_id: str,
    a1_range: str | None,
    spec_path: Path | None,
    tab_name: str | None,
    header_rows: int,
    header_bold: bool,
    header_fill: str,
    header_font_color: str,
    stripe: bool,
    stripe_color: str,
    font_size: int | None,
    font_color: str | None,
    apply: bool,
    json_summary: bool,
    backup: bool,
    auto_rollback: bool,
) -> None:
    """
    Apply simple styling (headers, stripes, fonts) to a sheet range.

    Can be used in two modes:
    1. Direct: --range "Tab!A1:Q200" with inline options
    2. From spec: --from-spec styles.yaml --tab "TabName"

    By default, runs in dry-run mode. Use --apply to execute.
    """
    safety_ctx = SafetyContext(command="g-gremlin style", remote_mutation=True)
    safety_ctx.enforce_apply(apply)

    # Handle --from-spec mode
    if spec_path:
        if not tab_name:
            click.echo("Error: --tab is required when using --from-spec", err=True)
            sys.exit(1)

        from g_gremlin.styling import load_style_spec, apply_styles

        try:
            styles = load_style_spec(spec_path)
        except Exception as e:
            click.echo(f"Error loading spec: {e}", err=True)
            sys.exit(1)

        if not apply:
            click.echo(
                f"[DRY-RUN] Would apply {len(styles)} style rules from '{spec_path}' to tab '{tab_name}'."
            )
            if json_summary:
                agentic = safety_ctx.build_result(
                    status="success",
                    dry_run=True,
                    result={
                        "mode": "from_spec",
                        "spec_path": str(spec_path),
                        "tab": tab_name,
                        "style_count": len(styles),
                    },
                )
                click.echo(json.dumps(agentic.to_dict(), indent=2))
            return

        # Create snapshot before applying styles
        snapshot_result = None
        if backup:
            snap_mod = _get_snapshot()
            from datetime import datetime
            timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
            snapshot_name = f"pre-style-{timestamp}"
            try:
                cmd_metadata = snap_mod.collect_command_metadata()
                snapshot_result = snap_mod.create_snapshot(
                    spreadsheet_id=spreadsheet_id,
                    tabs=[tab_name],
                    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)

        try:
            result = apply_styles(spreadsheet_id, tab_name, styles)
        except Exception as e:
            _handle_restore_on_error(e, snapshot_result, auto_rollback, json_summary)
            raise

        if result.errors:
            for error in result.errors:
                click.echo(f"Error: {error}", err=True)

        if result.success:
            click.echo(f"Applied {result.styles_applied} styles to '{tab_name}'")
            if json_summary:
                result_data = {
                    "mode": "from_spec",
                    "tab": tab_name,
                    "styles_applied": result.styles_applied,
                }
                if snapshot_result and snapshot_result.success:
                    result_data["snapshot_name"] = snapshot_result.snapshot_path.stem.rsplit("_", 2)[0]
                    result_data["snapshot_path"] = str(snapshot_result.snapshot_path)
                    result_data["restore_command"] = f"g-gremlin snapshot restore --name {result_data['snapshot_name']}"
                agentic = safety_ctx.build_result(
                    status="success",
                    dry_run=False,
                    result=result_data,
                )
                click.echo(json.dumps(agentic.to_dict(), indent=2))
        else:
            sys.exit(1)
        return

    # Original direct mode
    if not a1_range:
        click.echo("Error: --range is required (or use --from-spec)", err=True)
        sys.exit(1)

    parsed = parse_a1_range(a1_range)
    if not parsed.sheet_name:
        click.echo("Range must include a sheet name (e.g., 'Tab!A1:Q200').")
        sys.exit(1)

    # Build style summary for dry-run
    style_operations = []
    if header_rows > 0:
        style_operations.append(f"header styling ({header_rows} rows)")
    if font_size or font_color:
        style_operations.append("body font styling")
    if stripe:
        style_operations.append("zebra striping")

    if not style_operations:
        click.echo("Nothing to do (no styling options selected).")
        return

    if not apply:
        click.echo(
            f"[DRY-RUN] Would apply {', '.join(style_operations)} to {a1_range}."
        )
        if json_summary:
            agentic = safety_ctx.build_result(
                status="success",
                dry_run=True,
                result={
                    "mode": "direct",
                    "range": a1_range,
                    "operations": style_operations,
                },
            )
            click.echo(json.dumps(agentic.to_dict(), indent=2))
        return

    try:
        service = _get_google_client().get_sheets_service()
        sheet_id, row_count, col_count = _get_sheet_id_and_size(service, spreadsheet_id, parsed.sheet_name)
    except Exception as exc:
        click.echo(f"Error fetching sheet metadata: {exc}")
        sys.exit(1)

    grid_range = _build_grid_range(parsed, sheet_id, row_count, col_count)
    requests: list[dict[str, Any]] = []

    # Header styling
    if header_rows > 0:
        header_range = dict(grid_range)
        header_range["endRowIndex"] = min(
            header_range.get("startRowIndex", 0) + header_rows, header_range.get("endRowIndex", row_count)
        )
        header_format: dict[str, Any] = {
            "backgroundColor": _hex_to_color(header_fill),
            "textFormat": {
                "bold": header_bold,
                "foregroundColor": _hex_to_color(header_font_color),
            },
        }
        requests.append(
            {
                "repeatCell": {
                    "range": header_range,
                    "cell": {"userEnteredFormat": header_format},
                    "fields": "userEnteredFormat(backgroundColor,textFormat)",
                }
            }
        )

    # Body font styling
    if font_size or font_color:
        body_range = dict(grid_range)
        if header_rows > 0:
            body_range["startRowIndex"] = min(
                body_range.get("startRowIndex", 0) + header_rows, body_range.get("endRowIndex", row_count)
            )
        text_format: dict[str, Any] = {}
        if font_size:
            text_format["fontSize"] = font_size
        if font_color:
            text_format["foregroundColor"] = _hex_to_color(font_color)
        requests.append(
            {
                "repeatCell": {
                    "range": body_range,
                    "cell": {"userEnteredFormat": {"textFormat": text_format}},
                    "fields": "userEnteredFormat.textFormat",
                }
            }
        )

    # Zebra striping via conditional format on even rows
    if stripe:
        body_range = dict(grid_range)
        if header_rows > 0:
            body_range["startRowIndex"] = min(
                body_range.get("startRowIndex", 0) + header_rows, body_range.get("endRowIndex", row_count)
            )
        stripe_format = {
            "backgroundColor": _hex_to_color(stripe_color),
        }
        requests.append(
            {
                "addConditionalFormatRule": {
                    "rule": {
                        "ranges": [body_range],
                        "booleanRule": {
                            "condition": {"type": "CUSTOM_FORMULA", "values": [{"userEnteredValue": "=ISEVEN(ROW())"}]},
                            "format": stripe_format,
                        },
                    },
                    "index": 0,
                }
            }
        )

    # Create snapshot before applying styles (direct mode)
    snapshot_result = None
    if backup:
        snap_mod = _get_snapshot()
        from datetime import datetime
        timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
        snapshot_name = f"pre-style-{timestamp}"
        try:
            cmd_metadata = snap_mod.collect_command_metadata()
            snapshot_result = snap_mod.create_snapshot(
                spreadsheet_id=spreadsheet_id,
                tabs=[parsed.sheet_name],
                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)

    try:
        service.spreadsheets().batchUpdate(spreadsheetId=spreadsheet_id, body={"requests": requests}).execute()
    except Exception as err:
        _handle_restore_on_error(err, snapshot_result, auto_rollback, json_summary)
        click.echo(f"Error applying styles: {err}")
        sys.exit(1)
    click.echo(f"Applied styling to {a1_range}")

    if json_summary:
        result_data = {
            "mode": "direct",
            "range": a1_range,
            "operations": style_operations,
            "request_count": len(requests),
        }
        if snapshot_result and snapshot_result.success:
            result_data["snapshot_name"] = snapshot_result.snapshot_path.stem.rsplit("_", 2)[0]
            result_data["snapshot_path"] = str(snapshot_result.snapshot_path)
            result_data["restore_command"] = f"g-gremlin snapshot restore --name {result_data['snapshot_name']}"
        agentic = safety_ctx.build_result(
            status="success",
            dry_run=False,
            result=result_data,
        )
        click.echo(json.dumps(agentic.to_dict(), indent=2))
