"""
Module: test_output
Description: Tests for the dual-mode OutputContext class.

Implements:
    - docs/cli/reference.md#global-options
    - docs/cli/agent-integration.md#json-envelope
"""

from __future__ import annotations

import io
import json
import sys
from typing import Any
from unittest.mock import MagicMock

from rich.console import Console

from huitzo_cli.output import OutputContext, get_output


class TestIsJson:
    """Test the is_json property for both modes."""

    def test_json_mode_returns_true(self) -> None:
        out = OutputContext(mode="json", console=Console())
        assert out.is_json is True

    def test_human_mode_returns_false(self) -> None:
        out = OutputContext(mode="human", console=Console())
        assert out.is_json is False


class TestSuccess:
    """Test success() in JSON and human modes."""

    def test_json_mode_prints_envelope_to_stdout(self, capsys: Any) -> None:
        out = OutputContext(mode="json", console=Console())
        out.success({"key": "value"})

        captured = capsys.readouterr()
        parsed = json.loads(captured.out)
        assert parsed == {"ok": True, "data": {"key": "value"}}

    def test_human_mode_is_noop(self, capsys: Any) -> None:
        buf = io.StringIO()
        console = Console(file=buf, force_terminal=False)
        out = OutputContext(mode="human", console=console)
        out.success({"key": "value"})

        assert buf.getvalue() == ""
        captured = capsys.readouterr()
        assert captured.out == ""


class TestError:
    """Test error() in JSON and human modes."""

    def test_json_mode_prints_to_stderr(self) -> None:
        fake_stderr = io.StringIO()
        real_stderr = sys.stderr
        try:
            sys.stderr = fake_stderr
            out = OutputContext(mode="json", console=Console())
            out.error("AuthError", "bad token", 2)
        finally:
            sys.stderr = real_stderr

        parsed = json.loads(fake_stderr.getvalue())
        assert parsed == {
            "ok": False,
            "error": {"type": "AuthError", "message": "bad token", "code": 2},
        }

    def test_human_mode_prints_red_text(self) -> None:
        buf = io.StringIO()
        console = Console(file=buf, force_terminal=True, color_system="truecolor")
        out = OutputContext(mode="human", console=console)
        out.error("AuthError", "bad token", 2)

        rendered = buf.getvalue()
        assert "bad token" in rendered


class TestPrint:
    """Test print() suppression in JSON mode and passthrough in human mode."""

    def test_json_mode_suppresses_output(self) -> None:
        buf = io.StringIO()
        console = Console(file=buf, force_terminal=False)
        out = OutputContext(mode="json", console=console)
        out.print("hello world")

        assert buf.getvalue() == ""

    def test_human_mode_passes_through(self) -> None:
        buf = io.StringIO()
        console = Console(file=buf, force_terminal=False)
        out = OutputContext(mode="human", console=console)
        out.print("hello world")

        assert "hello world" in buf.getvalue()


class TestGetOutput:
    """Test get_output() helper for extracting OutputContext from ctx."""

    def test_returns_output_from_ctx_obj(self) -> None:
        expected = OutputContext(mode="json", console=Console())
        ctx = MagicMock()
        ctx.obj = {"output": expected}

        result = get_output(ctx)
        assert result is expected

    def test_fallback_to_human_when_missing(self) -> None:
        ctx = MagicMock()
        ctx.obj = {}

        result = get_output(ctx)
        assert isinstance(result, OutputContext)
        assert result.is_json is False

    def test_fallback_when_obj_is_none(self) -> None:
        ctx = MagicMock()
        ctx.obj = None

        result = get_output(ctx)
        assert isinstance(result, OutputContext)
        assert result.is_json is False

    def test_works_with_plain_dict(self) -> None:
        expected = OutputContext(mode="json", console=Console())
        result = get_output({"output": expected})
        assert result is expected
