"""CLI for Plotly visualizations."""

from __future__ import annotations

import importlib
import json
import os
import sys
import tempfile
from pathlib import Path
from typing import Iterable, List, Optional

import click

from g_gremlin import google_client, google_slides
from g_gremlin.visualization.plotly_charts import PlotlyRenderError, render_chart

EXIT_REMOTE_ERROR = 1


@click.group("viz")
def viz() -> None:
    """
    Plotly visualization helpers (render + embed).
    """


@viz.command("render")
@click.option(
    "--csv",
    "csv_path",
    required=True,
    type=click.Path(exists=True, dir_okay=False, path_type=Path),
    help="CSV file to render.",
)
@click.option(
    "--type",
    "chart_type",
    required=True,
    type=click.Choice(["line", "bar", "column", "area", "scatter", "pie"], case_sensitive=False),
    help="Chart type.",
)
@click.option("--x", "x_col", required=True, help="X-axis column name.")
@click.option("--y", "y_col", required=False, help="Y-axis column name.")
@click.option(
    "--series",
    "series_cols",
    multiple=True,
    help="Additional series columns (repeatable or comma-separated).",
)
@click.option("--title", help="Chart title.")
@click.option(
    "--output",
    "output_path",
    type=click.Path(dir_okay=False, path_type=Path),
    default=Path("chart.png"),
    show_default=True,
    help="Output image path.",
)
@click.option("--width", type=int, default=900, show_default=True, help="Image width in pixels.")
@click.option("--height", type=int, default=500, show_default=True, help="Image height in pixels.")
@click.option("--json", "json_output", is_flag=True, help="Emit JSON output.")
def viz_render(
    csv_path: Path,
    chart_type: str,
    x_col: str,
    y_col: Optional[str],
    series_cols: tuple[str, ...],
    title: Optional[str],
    output_path: Path,
    width: int,
    height: int,
    json_output: bool,
) -> None:
    """
    Render a Plotly chart to an image file.
    """
    series = _parse_series_cols(series_cols)
    try:
        output = render_chart(
            csv_path,
            chart_type=chart_type,
            x_col=x_col,
            y_col=y_col,
            series_cols=series,
            title=title,
            output_path=output_path,
            width=width,
            height=height,
        )
    except PlotlyRenderError as exc:
        if json_output:
            click.echo(json.dumps({"status": "error", "error": str(exc)}, indent=2))
        else:
            click.echo(f"Error: {exc}", err=True)
        sys.exit(EXIT_REMOTE_ERROR)

    if json_output:
        click.echo(json.dumps({"status": "success", "output": str(output)}, indent=2))
        return

    click.echo(f"Rendered chart to {output}.")


@viz.command("embed-slide")
@click.argument("presentation_id")
@click.option(
    "--csv",
    "csv_path",
    required=True,
    type=click.Path(exists=True, dir_okay=False, path_type=Path),
    help="CSV file to render.",
)
@click.option(
    "--type",
    "chart_type",
    required=True,
    type=click.Choice(["line", "bar", "column", "area", "scatter", "pie"], case_sensitive=False),
    help="Chart type.",
)
@click.option("--x", "x_col", required=True, help="X-axis column name.")
@click.option("--y", "y_col", required=False, help="Y-axis column name.")
@click.option(
    "--series",
    "series_cols",
    multiple=True,
    help="Additional series columns (repeatable or comma-separated).",
)
@click.option("--title", help="Chart title.")
@click.option(
    "--slide-index",
    type=int,
    default=0,
    show_default=True,
    help="0-based slide index to insert into.",
)
@click.option(
    "--layout",
    type=click.Choice(["full", "left", "right"], case_sensitive=False),
    default="full",
    show_default=True,
    help="Image layout on the slide.",
)
@click.option(
    "--output",
    "output_path",
    type=click.Path(dir_okay=False, path_type=Path),
    default=None,
    help="Optional path to keep the rendered image.",
)
@click.option("--tag", help="Optional tag used for replace matching.")
@click.option("--replace", is_flag=True, help="Replace existing element with matching tag.")
@click.option("--json", "json_output", is_flag=True, help="Emit JSON output.")
def viz_embed_slide(
    presentation_id: str,
    csv_path: Path,
    chart_type: str,
    x_col: str,
    y_col: Optional[str],
    series_cols: tuple[str, ...],
    title: Optional[str],
    slide_index: int,
    layout: str,
    output_path: Optional[Path],
    tag: Optional[str],
    replace: bool,
    json_output: bool,
) -> None:
    """
    Render a Plotly chart and embed it into a Google Slide.
    """
    series = _parse_series_cols(series_cols)
    if replace and not tag:
        tag = _default_tag(chart_type, x_col, series, title)

    temp_path: Optional[Path] = None
    image_path = output_path
    if image_path is None:
        temp_handle, temp_name = tempfile.mkstemp(suffix=".png", prefix="g_gremlin_viz_")
        os.close(temp_handle)
        image_path = Path(temp_name)
        temp_path = image_path

    try:
        render_chart(
            csv_path,
            chart_type=chart_type,
            x_col=x_col,
            y_col=y_col,
            series_cols=series,
            title=title,
            output_path=image_path,
        )
        upload = google_client.upload_file_to_drive(
            image_path,
            mime_type="image/png",
            name=image_path.name,
            scopes=google_client.VIZ_SCOPES,
        )
        file_id = upload.get("id")
        if not file_id:
            raise RuntimeError("Drive upload did not return a file id.")
        image_url = f"https://drive.google.com/uc?id={file_id}"
        result = google_slides.insert_image(
            presentation_id,
            image_url,
            slide_index=slide_index,
            layout=layout,
            replace=replace,
            tag=tag,
        )
    except Exception as exc:
        if json_output:
            click.echo(json.dumps({"status": "error", "error": str(exc)}, indent=2))
        else:
            click.echo(f"Error: {exc}", err=True)
        sys.exit(EXIT_REMOTE_ERROR)
    finally:
        if temp_path and temp_path.exists():
            try:
                temp_path.unlink()
            except Exception:
                pass

    payload = {
        "status": "success",
        "presentation_id": presentation_id,
        "slide_index": slide_index,
        "image_file_id": upload.get("id"),
        "tag": result.get("tag"),
        "result": result,
    }

    if json_output:
        click.echo(json.dumps(payload, indent=2))
    else:
        message = f"Embedded image on slide {slide_index}."
        replaced = result.get("replaced", 0)
        if replaced:
            message = f"Replaced {replaced} element(s). " + message
        click.echo(message)


@viz.command("doctor")
@click.option("--json", "json_output", is_flag=True, help="Emit JSON output.")
def viz_doctor(json_output: bool) -> None:
    """
    Diagnose Plotly rendering and Drive upload readiness.
    """
    checks = []

    for module_name in ("plotly", "pandas", "kaleido"):
        ok, detail = _check_import(module_name)
        checks.append({"name": module_name, "status": "ok" if ok else "error", "detail": detail})

    token_path = getattr(google_client, "TOKEN_PATH", None)
    token_exists = bool(token_path and token_path.exists())
    drive_scope_ok = google_client.check_viz_scope()
    if drive_scope_ok:
        drive_detail = "drive.file scope present"
    elif not token_exists:
        drive_detail = "Google OAuth token missing"
    else:
        drive_detail = "drive.file scope missing"
    checks.append({"name": "drive_scope", "status": "ok" if drive_scope_ok else "error", "detail": drive_detail})

    missing_deps = any(
        c["status"] != "ok" for c in checks if c["name"] in {"plotly", "pandas", "kaleido"}
    )
    if missing_deps:
        status_label = "needs_deps"
    elif not token_exists:
        status_label = "needs_auth"
    elif not drive_scope_ok:
        status_label = "needs_drive_scope"
    else:
        status_label = "ready"

    remediation = []
    if missing_deps:
        remediation.append("Install visualization deps: pip install g-gremlin[visualize]")
    if not token_exists:
        remediation.append(
            "Authenticate Google OAuth: g-gremlin pull SHEET_ID --range A1"
        )
    if token_exists and not drive_scope_ok:
        remediation.append("Re-auth to add Drive scope:")
        remediation.append("  rm ~/.g_gremlin/token.json")
        remediation.append("Re-run:")
        remediation.append(
            "  g-gremlin viz embed-slide PRESENTATION_ID --csv data.csv --x X --y Y --type line"
        )

    payload = {"status": status_label, "checks": checks, "remediation": remediation}

    if json_output:
        click.echo(json.dumps(payload, indent=2))
        return

    click.echo("Viz doctor")
    click.echo("----------")
    click.echo(f"status: {status_label}")
    for check in checks:
        status = "OK" if check["status"] == "ok" else "ERROR"
        click.echo(f"{status}: {check['name']} - {check['detail']}")
    if remediation:
        click.echo("\nRemediation:")
        for item in remediation:
            click.echo(f"  - {item}")
    if status_label != "ready":
        sys.exit(EXIT_REMOTE_ERROR)


def _parse_series_cols(values: Iterable[str]) -> List[str]:
    series: List[str] = []
    for raw in values:
        if not raw:
            continue
        parts = [p.strip() for p in raw.split(",") if p.strip()]
        for part in parts:
            if part not in series:
                series.append(part)
    return series


def _default_tag(chart_type: str, x_col: str, series: List[str], title: Optional[str]) -> str:
    pieces = ["g-gremlin:viz", chart_type, x_col]
    pieces.extend(series)
    if title:
        pieces.append(title)
    return ":".join(pieces)


def _check_import(module_name: str) -> tuple[bool, str]:
    try:
        importlib.import_module(module_name)
        return True, "installed"
    except Exception as exc:
        return False, str(exc)


__all__ = ["viz"]
