"""Graph management commands for Metaxy CLI."""

from typing import Annotated, Literal

import cyclopts

from metaxy.cli.console import console, data_console, error_console
from metaxy.graph import RenderConfig

# Graph subcommand app
app = cyclopts.App(
    name="graph",
    help="Manage feature graphs",
    console=console,
    error_console=error_console,
)


@app.command()
def render(
    render_config: Annotated[RenderConfig | None, cyclopts.Parameter(name="*", help="Render configuration")] = None,
    format: Annotated[
        str,
        cyclopts.Parameter(
            name=["--format", "-f"],
            help="Output format: terminal, mermaid, or graphviz",
        ),
    ] = "terminal",
    type: Annotated[
        Literal["graph", "cards"],
        cyclopts.Parameter(
            name=["--type", "-t"],
            help="Terminal rendering type: graph or cards (only for --format terminal)",
        ),
    ] = "graph",
    output: Annotated[
        str | None,
        cyclopts.Parameter(
            name=["--output", "-o"],
            help="Output file path (default: stdout)",
        ),
    ] = None,
    snapshot: Annotated[
        str | None,
        cyclopts.Parameter(
            name=["--snapshot"],
            help="Project version to render (default: current graph from code)",
        ),
    ] = None,
    store: Annotated[
        str | None,
        cyclopts.Parameter(
            name=["--store"],
            help="Metadata store to use (for loading historical snapshots)",
        ),
    ] = None,
    # Preset modes
    minimal: Annotated[
        bool,
        cyclopts.Parameter(
            name=["--minimal"],
            help="Minimal output: only feature keys and dependencies",
        ),
    ] = False,
    verbose: Annotated[
        bool,
        cyclopts.Parameter(
            name=["--verbose"],
            help="Verbose output: show all available information",
        ),
    ] = False,
):
    """Render feature graph visualization.

    Visualize the feature graph in different formats:
    - terminal: Terminal rendering with two types:
      - graph (default): Hierarchical tree view
      - cards: Panel/card-based view with dependency edges
    - mermaid: Mermaid flowchart markup
    - graphviz: Graphviz DOT format

    Examples:
        Render to terminal (default graph view):
        ```console
        $ metaxy graph render
        ```

        Render as cards with dependency edges:
        ```console
        $ metaxy graph render --type cards
        ```

        Minimal view:
        ```console
        $ metaxy graph render --minimal
        ```

        Everything:
        ```console
        $ metaxy graph render --verbose
        ```

        Save Mermaid diagram to file:
        ```console
        $ metaxy graph render --format mermaid --output graph.mmd
        ```

        Graphviz DOT format (pipe to dot command):
        ```console
        $ metaxy graph render --format graphviz | dot -Tpng -o graph.png
        ```

        Show only structure with short hashes:
        ```console
        $ metaxy graph render --no-show-fields --hash-length 6
        ```

        Focus on a specific feature and its dependencies:
        ```console
        $ metaxy graph render --feature video/processing --up 2
        ```

        Show a feature and its downstream dependents:
        ```console
        $ metaxy graph render --feature video/files --down 1
        ```

        Render historical snapshot:
        ```console
        $ metaxy graph render --snapshot abc123... --store prod
        ```
    """
    from metaxy.graph import (
        CardsRenderer,
        GraphvizRenderer,
        MermaidRenderer,
        TerminalRenderer,
    )
    from metaxy.models.feature import FeatureGraph

    # Validate format
    valid_formats = ["terminal", "mermaid", "graphviz"]
    if format not in valid_formats:
        console.print(f"[red]Error:[/red] Invalid format '{format}'. Must be one of: {', '.join(valid_formats)}")
        raise SystemExit(1)

    # Validate type (only applies to terminal format)
    valid_types = ["graph", "cards"]
    if type not in valid_types:
        console.print(f"[red]Error:[/red] Invalid type '{type}'. Must be one of: {', '.join(valid_types)}")
        raise SystemExit(1)

    # Validate type is only used with terminal format
    if type != "graph" and format != "terminal":
        console.print("[red]Error:[/red] --type can only be used with --format terminal")
        raise SystemExit(1)

    # Resolve configuration from presets
    if minimal and verbose:
        console.print("[red]Error:[/red] Cannot specify both --minimal and --verbose")
        raise SystemExit(1)

    # If config is None, create a default instance
    if render_config is None:
        render_config = RenderConfig()

    # Apply presets if specified (overrides display settings but preserves filtering)
    if minimal:
        preset = RenderConfig.minimal(show_projects=render_config.show_projects)
        # Preserve filtering parameters from original config
        preset.feature = render_config.feature
        preset.up = render_config.up
        preset.down = render_config.down
        render_config = preset
    elif verbose:
        preset = RenderConfig.verbose(show_projects=render_config.show_projects)
        # Preserve filtering parameters from original config
        preset.feature = render_config.feature
        preset.up = render_config.up
        preset.down = render_config.down
        render_config = preset

    # Validate direction
    if render_config.direction not in ["TB", "LR"]:
        console.print(f"[red]Error:[/red] Invalid direction '{render_config.direction}'. Must be TB or LR.")
        raise SystemExit(1)

    # Validate filtering options
    if (render_config.up is not None or render_config.down is not None) and render_config.feature is None:
        console.print("[red]Error:[/red] --up and --down require --feature to be specified")
        raise SystemExit(1)

    # Auto-disable field versions if fields are disabled
    if not render_config.show_fields and render_config.show_field_versions:
        render_config.show_field_versions = False

    from metaxy.cli.context import AppContext

    context = AppContext.get()

    # Apply project filter from context if not specified in config
    if render_config.project is None and context.project is not None:
        render_config.project = context.project

    # Determine which graph to render
    # Initialize to satisfy type checker - will be assigned in all code paths
    graph = FeatureGraph.get_active()  # Default initialization

    if snapshot is None:
        # Use current graph from code
        graph = FeatureGraph.get_active()

        # Validate feature exists if specified
        if render_config.feature is not None:
            focus_key = render_config.get_feature_key()
            if focus_key not in graph.feature_definitions_by_key:
                console.print(f"[red]Error:[/red] Feature '{render_config.feature}' not found in graph")
                console.print("\nAvailable features:")
                for key in sorted(graph.feature_definitions_by_key.keys(), key=lambda k: k.to_string()):
                    console.print(f"  • {key.to_string()}")
                raise SystemExit(1)

        if len(graph.feature_definitions_by_key) == 0:
            console.print("[yellow]Warning:[/yellow] Graph is empty (no features found)")
            if output:
                # Write empty output to file
                with open(output, "w") as f:
                    f.write("")
            return
    else:
        # Load historical snapshot from store
        metadata_store = context.get_store(store)

        from metaxy.metadata_store.system.storage import SystemTableStorage

        with metadata_store:
            storage = SystemTableStorage(metadata_store)
            try:
                graph = storage.load_graph_from_snapshot(project_version=snapshot)
            except ValueError as e:
                from metaxy.cli.utils import print_error

                print_error(console, "Snapshot error", e)
                raise SystemExit(1)
            except ImportError as e:
                from metaxy.cli.utils import print_error

                print_error(console, "Failed to load snapshot", e)
                console.print("[yellow]Hint:[/yellow] Feature classes may have been moved or deleted.")
                raise SystemExit(1) from e
            except Exception as e:
                from metaxy.cli.utils import print_error

                print_error(console, "Failed to load snapshot", e)
                raise SystemExit(1) from e

            console.print(
                f"[green]✓[/green] Loaded {len(graph.feature_definitions_by_key)} features from snapshot {snapshot}"
            )

    # Instantiate renderer based on format and type
    # (graph is guaranteed to be assigned by this point - either from get_active() or from_snapshot())
    assert "graph" in locals(), "graph must be assigned"
    if format == "terminal":
        if type == "graph":
            renderer = TerminalRenderer(graph, render_config)
        elif type == "cards":
            renderer = CardsRenderer(graph, render_config)
        else:
            # Should not reach here due to validation above
            console.print(f"[red]Error:[/red] Unknown type: {type}")
            raise SystemExit(1)
    elif format == "mermaid":
        renderer = MermaidRenderer(graph, render_config)
    elif format == "graphviz":
        try:
            renderer = GraphvizRenderer(graph, render_config)
        except ImportError as e:
            console.print(f"[red]✗[/red] {e}")
            raise SystemExit(1)
    else:
        # Should not reach here due to validation above
        console.print(f"[red]Error:[/red] Unknown format: {format}")
        raise SystemExit(1)

    # Render graph
    try:
        rendered = renderer.render()
    except Exception as e:
        from metaxy.cli.utils import print_error

        print_error(console, "Rendering failed", e)
        import traceback

        traceback.print_exc()
        raise SystemExit(1)

    # Output to stdout or file
    if output:
        try:
            with open(output, "w") as f:
                f.write(rendered)
            console.print(f"[green]✓[/green] Rendered graph saved to: {output}")
        except Exception as e:
            from metaxy.cli.utils import print_error

            print_error(console, "Failed to write to file", e)
            raise SystemExit(1)
    else:
        # Print to stdout using data_console
        # Rendered graph output is data that users might pipe/redirect
        data_console.print(rendered)
