"""CLI commands for Tableau templates."""

from __future__ import annotations

import json
import sys
from pathlib import Path
from tempfile import TemporaryDirectory
from typing import Dict, List, Optional

import click

from g_gremlin.tableau.binding import bind_template
from g_gremlin.tableau.hyper_helpers import create_canonical_hyper
from g_gremlin.tableau.packaging import package_workbook
from g_gremlin.tableau.template_manager import TemplateManager
from g_gremlin.tableau.xml_utils import update_template_captions
from g_gremlin.tableau.client import TableauClient


def _emit_json(payload: object) -> None:
    click.echo(json.dumps(payload, indent=2))


def _parse_map_args(mapping_args: List[str]) -> Dict[str, str]:
    mappings: Dict[str, str] = {}
    for item in mapping_args:
        if "=" not in item:
            raise click.ClickException(f"Invalid mapping '{item}'. Use Role=Column")
        role, column = item.split("=", 1)
        mappings[role] = column
    return mappings


@click.group("template")
def template() -> None:
    """Template-based Tableau report generation."""


@template.command("list")
@click.option("--json", "json_output", is_flag=True, help="Output as JSON.")
def template_list(json_output: bool) -> None:
    manager = TemplateManager()
    templates = manager.list_templates()
    if json_output:
        _emit_json(templates)
        return
    for tmpl in templates:
        roles = ", ".join(tmpl["roles"])
        click.echo(f"{tmpl['name']}\t{tmpl['viz_type']}\troles: {roles}")


@template.command("inspect")
@click.argument("template_name")
@click.option("--json", "json_output", is_flag=True, help="Output as JSON.")
def template_inspect(template_name: str, json_output: bool) -> None:
    manager = TemplateManager()
    manifest = manager.load_manifest(template_name)
    payload = manifest.model_dump()
    if json_output:
        _emit_json(payload)
    else:
        click.echo(json.dumps(payload, indent=2))


@template.command("validate")
@click.argument("template_name")
@click.argument("csv_path", type=click.Path(exists=True, path_type=Path))
@click.option("--map", "mapping_args", multiple=True, help="Mapping Role=Column (repeatable).")
@click.option("--json", "json_output", is_flag=True, help="Output as JSON.")
def template_validate(template_name: str, csv_path: Path, mapping_args: List[str], json_output: bool) -> None:
    manager = TemplateManager()
    manifest = manager.load_manifest(template_name)
    explicit = _parse_map_args(list(mapping_args)) if mapping_args else {}
    result = bind_template(manifest, csv_path, explicit_mappings=explicit)
    payload = result.to_dict()
    if json_output:
        _emit_json(payload)
    else:
        click.echo(json.dumps(payload, indent=2))
    if not result.success:
        sys.exit(1)


@template.command("bind")
@click.argument("template_name")
@click.argument("csv_path", type=click.Path(exists=True, path_type=Path))
@click.option("--output", "output_path", type=click.Path(path_type=Path), required=True)
@click.option("--map", "mapping_args", multiple=True, help="Mapping Role=Column (repeatable).")
@click.option("--json", "json_output", is_flag=True, help="Output as JSON.")
def template_bind(
    template_name: str,
    csv_path: Path,
    output_path: Path,
    mapping_args: List[str],
    json_output: bool,
) -> None:
    manager = TemplateManager()
    manifest = manager.load_manifest(template_name)
    _, twb_path = manager.get_template_paths(template_name)

    explicit = _parse_map_args(list(mapping_args)) if mapping_args else {}
    result = bind_template(manifest, csv_path, explicit_mappings=explicit)
    if not result.success:
        payload = result.to_dict()
        if json_output:
            _emit_json(payload)
        else:
            click.echo(json.dumps(payload, indent=2))
        sys.exit(1)

    try:
        with TemporaryDirectory() as tmp_dir:
            tmp_dir_path = Path(tmp_dir)
            tmp_twb = tmp_dir_path / twb_path.name
            update_template_captions(twb_path, {m.canonical_role: m.csv_column for m in result.mappings}, tmp_twb)

            hyper_path = tmp_dir_path / "data.hyper"
            create_canonical_hyper(
                csv_path,
                hyper_path,
                role_mapping={m.canonical_role: m.csv_column for m in result.mappings},
                template_manifest=manifest,
            )

            package_workbook(tmp_twb, hyper_path, output_path)
    except Exception as exc:
        raise click.ClickException(str(exc)) from exc

    payload = {
        "status": "ok",
        "output": str(output_path),
        "mappings": [m.to_dict() for m in result.mappings],
        "mapping_explanations": result.mapping_explanations,
    }
    if json_output:
        _emit_json(payload)
    else:
        click.echo(json.dumps(payload, indent=2))


@template.command("publish")
@click.argument("template_name")
@click.argument("csv_path", type=click.Path(exists=True, path_type=Path))
@click.option("--project", "project_id", required=True, help="Target project ID.")
@click.option("--name", "workbook_name", required=True, help="Workbook name.")
@click.option(
    "--publish-mode",
    default="create",
    show_default=True,
    type=click.Choice(["create", "overwrite"], case_sensitive=False),
)
@click.option("--map", "mapping_args", multiple=True, help="Mapping Role=Column (repeatable).")
@click.option("--json", "json_output", is_flag=True, help="Output as JSON.")
def template_publish(
    template_name: str,
    csv_path: Path,
    project_id: str,
    workbook_name: str,
    publish_mode: str,
    mapping_args: List[str],
    json_output: bool,
) -> None:
    manager = TemplateManager()
    manifest = manager.load_manifest(template_name)
    _, twb_path = manager.get_template_paths(template_name)

    explicit = _parse_map_args(list(mapping_args)) if mapping_args else {}
    result = bind_template(manifest, csv_path, explicit_mappings=explicit)
    if not result.success:
        payload = result.to_dict()
        if json_output:
            _emit_json(payload)
        else:
            click.echo(json.dumps(payload, indent=2))
        sys.exit(1)

    try:
        with TemporaryDirectory() as tmp_dir:
            tmp_dir_path = Path(tmp_dir)
            tmp_twb = tmp_dir_path / twb_path.name
            update_template_captions(twb_path, {m.canonical_role: m.csv_column for m in result.mappings}, tmp_twb)

            hyper_path = tmp_dir_path / "data.hyper"
            create_canonical_hyper(
                csv_path,
                hyper_path,
                role_mapping={m.canonical_role: m.csv_column for m in result.mappings},
                template_manifest=manifest,
            )

            tmp_twbx = tmp_dir_path / f"{template_name}.twbx"
            package_workbook(tmp_twb, hyper_path, tmp_twbx)

            client = TableauClient()
            publish_result = client.publish_workbook(
                tmp_twbx,
                project_id=project_id,
                name=workbook_name,
                publish_mode=publish_mode,
            )
    except Exception as exc:
        raise click.ClickException(str(exc)) from exc

    payload = {
        "status": "ok",
        "publish": publish_result,
        "mappings": [m.to_dict() for m in result.mappings],
        "mapping_explanations": result.mapping_explanations,
    }
    if json_output:
        _emit_json(payload)
    else:
        click.echo(json.dumps(payload, indent=2))


__all__ = ["template"]
