"""Step definitions for CLI output format validation (rich / color formats)."""

from __future__ import annotations

import json

import yaml
from behave import then
from behave.runner import Context


@then("the format result should be valid JSON dict")
def step_format_result_json_dict(context: Context) -> None:
    parsed = json.loads(context.format_result)
    assert isinstance(parsed, dict)
    # Verify spec-required envelope fields are present
    for field in ("command", "status", "exit_code", "data", "timing", "messages"):
        assert field in parsed, f"Envelope field '{field}' missing from JSON result"
    # Verify data field contains the original dict
    assert isinstance(parsed["data"], dict)


@then("the format result should be valid YAML dict")
def step_format_result_yaml_dict(context: Context) -> None:
    parsed = yaml.safe_load(context.format_result)
    assert isinstance(parsed, dict)
    # Verify spec-required envelope fields are present
    for field in ("command", "status", "exit_code", "data", "timing", "messages"):
        assert field in parsed, f"Envelope field '{field}' missing from YAML result"
    # Verify data field contains the original dict
    assert isinstance(parsed["data"], dict)


@then("the format result should contain plain key-value pairs")
def step_format_result_plain(context: Context) -> None:
    assert "name: test" in context.format_result
    assert "nested:" in context.format_result
    assert "items:" in context.format_result


@then("the format result should contain table output")
def step_format_result_table(context: Context) -> None:
    assert "name" in context.format_result
    assert "count" in context.format_result


@then("the format result should contain separator lines")
def step_format_result_separator(context: Context) -> None:
    assert "---" in context.format_result


@then("the serialized result should have string enum values")
def step_serialized_enum_values(context: Context) -> None:
    assert context.serialized["state"] == "available"
    assert isinstance(context.serialized["nested"], dict)
    assert context.serialized["items"][0] == "archived"
    assert isinstance(context.serialized["ts"], str)


@then("the format result should contain rich styled output")
def step_format_result_rich_styled(context: Context) -> None:
    """Assert that rich format produces styled output (ANSI codes or key-value lines).

    The rich format must NOT produce raw JSON — it must produce styled terminal
    output via the RichMaterializer (ANSI escape codes or structured key-value
    lines with panel headers).
    """
    result = context.format_result
    # Rich output must contain the data keys
    assert "name" in result, f"Rich output missing 'name': {result!r}"
    assert "count" in result, f"Rich output missing 'count': {result!r}"
    # Rich output must contain styled panel header or ANSI codes
    has_ansi = "\033[" in result or "\x1b[" in result
    has_panel_header = "Output" in result
    assert has_ansi or has_panel_header, (
        f"Rich output has no ANSI codes or panel header — got: {result!r}"
    )


@then("the format result should not be valid JSON")
def step_format_result_not_json(context: Context) -> None:
    """Assert that the format result is NOT valid JSON (i.e. not the JSON fallback)."""
    try:
        json.loads(context.format_result)
        raise AssertionError(
            f"format_output() with 'rich' returned valid JSON — "
            f"expected styled terminal output, got: {context.format_result!r}"
        )
    except (json.JSONDecodeError, ValueError):
        pass  # Expected: rich output is not JSON


@then("the format result should contain color styled output")
def step_format_result_color_styled(context: Context) -> None:
    """Assert that color format produces ANSI-colored output (not plain text).

    The color format must NOT produce plain text — it must produce ANSI-colored
    terminal output via the ColorMaterializer.
    """
    result = context.format_result
    # Color output must contain the data keys
    assert "name" in result, f"Color output missing 'name': {result!r}"
    assert "count" in result, f"Color output missing 'count': {result!r}"
    # Color output must contain ANSI escape codes or styled panel header
    has_ansi = "\033[" in result or "\x1b[" in result
    has_panel_header = "Output" in result
    assert has_ansi or has_panel_header, (
        f"Color output has no ANSI codes or panel header — got: {result!r}"
    )


@then("the format result should not be plain text only")
def step_format_result_not_plain_text(context: Context) -> None:
    """Assert that the color format result is NOT plain text (i.e. not the plain fallback).

    Plain text would look like 'name: test\\ncount: 42' with no ANSI codes.
    Color output must have ANSI escape codes or styled panel headers.
    """
    result = context.format_result
    has_ansi = "\033[" in result or "\x1b[" in result
    has_panel_header = "Output" in result
    # If it's purely plain text (no ANSI, no panel header), that's the bug
    is_plain_only = not has_ansi and not has_panel_header
    assert not is_plain_only, (
        f"format_output() with 'color' returned plain text — "
        f"expected ANSI-colored output, got: {result!r}"
    )


@then("the format result should contain rich styled list output")
def step_format_result_rich_list(context: Context) -> None:
    """Assert rich format produces styled output for list data (panel per item)."""
    result = context.format_result
    assert "name" in result, f"Rich list output missing 'name': {result!r}"
    has_ansi = "\033[" in result or "\x1b[" in result
    has_item_panel = "Item 1" in result or "Item 2" in result
    assert has_ansi or has_item_panel, (
        f"Rich list output has no ANSI codes or item panels — got: {result!r}"
    )


@then("the format result should contain color styled list output")
def step_format_result_color_list(context: Context) -> None:
    """Assert color format produces styled output for list data (panel per item)."""
    result = context.format_result
    assert "name" in result, f"Color list output missing 'name': {result!r}"
    has_ansi = "\033[" in result or "\x1b[" in result
    has_item_panel = "Item 1" in result or "Item 2" in result
    assert has_ansi or has_item_panel, (
        f"Color list output has no ANSI codes or item panels — got: {result!r}"
    )
