"""Tests for CLI formatters."""

import unittest
from io import StringIO
from unittest.mock import patch

from pygeai_orchestration.cli.formatters import (
    Color,
    OutputFormatter,
    ProgressBar,
    Spinner,
    Symbol,
    format_error_details,
)


class TestColor(unittest.TestCase):
    def test_color_values(self):
        self.assertIsInstance(Color.RED, str)
        self.assertIsInstance(Color.GREEN, str)
        self.assertIsInstance(Color.RESET, str)


class TestSymbol(unittest.TestCase):
    def test_symbol_values(self):
        self.assertIsInstance(Symbol.SUCCESS, str)
        self.assertIsInstance(Symbol.ERROR, str)
        self.assertIsInstance(Symbol.WARNING, str)


class TestOutputFormatter(unittest.TestCase):
    def setUp(self):
        self.formatter_color = OutputFormatter(use_color=True)
        self.formatter_no_color = OutputFormatter(use_color=False)

    def test_colorize_with_color(self):
        result = self.formatter_color.colorize("test", Color.RED)
        self.assertIn("test", result)

    def test_colorize_no_color(self):
        result = self.formatter_no_color.colorize("test", Color.RED)
        self.assertEqual(result, "test")

    def test_colorize_bold(self):
        result = self.formatter_color.colorize("test", Color.RED, bold=True)
        self.assertIn("test", result)

    def test_success(self):
        result = self.formatter_no_color.success("Great!")
        self.assertIn(Symbol.SUCCESS, result)
        self.assertIn("Great!", result)

    def test_error(self):
        result = self.formatter_no_color.error("Oops!")
        self.assertIn(Symbol.ERROR, result)
        self.assertIn("Oops!", result)

    def test_warning(self):
        result = self.formatter_no_color.warning("Careful!")
        self.assertIn(Symbol.WARNING, result)
        self.assertIn("Careful!", result)

    def test_info(self):
        result = self.formatter_no_color.info("FYI")
        self.assertIn(Symbol.INFO, result)
        self.assertIn("FYI", result)

    def test_heading_level_1(self):
        result = self.formatter_no_color.heading("Title", level=1)
        self.assertEqual(result, "Title")

    def test_heading_level_2(self):
        result = self.formatter_no_color.heading("Subtitle", level=2)
        self.assertEqual(result, "Subtitle")

    def test_heading_level_3(self):
        result = self.formatter_no_color.heading("Minor", level=3)
        self.assertEqual(result, "Minor")

    def test_dim(self):
        result = self.formatter_no_color.dim("faded")
        self.assertEqual(result, "faded")

    def test_bold(self):
        result = self.formatter_no_color.bold("strong")
        self.assertEqual(result, "strong")

    def test_key_value(self):
        result = self.formatter_no_color.key_value("name", "value")
        self.assertIn("name", result)
        self.assertIn("value", result)

    def test_key_value_indent(self):
        result = self.formatter_no_color.key_value("name", "value", indent=2)
        self.assertIn("    ", result)
        self.assertIn("name", result)

    def test_bullet_list(self):
        items = ["item1", "item2", "item3"]
        result = self.formatter_no_color.bullet_list(items)
        self.assertIn("item1", result)
        self.assertIn("item2", result)
        self.assertIn("item3", result)
        self.assertIn(Symbol.BULLET, result)

    def test_bullet_list_indent(self):
        items = ["item1"]
        result = self.formatter_no_color.bullet_list(items, indent=1)
        self.assertIn("  ", result)

    def test_section(self):
        result = self.formatter_no_color.section("Section", "content")
        self.assertIn("Section", result)
        self.assertIn("content", result)

    def test_table_empty(self):
        result = self.formatter_no_color.table(["H1", "H2"], [])
        self.assertEqual(result, "")

    def test_table_with_rows(self):
        headers = ["Name", "Age"]
        rows = [["Alice", "30"], ["Bob", "25"]]
        result = self.formatter_no_color.table(headers, rows)
        self.assertIn("Name", result)
        self.assertIn("Age", result)
        self.assertIn("Alice", result)
        self.assertIn("Bob", result)

    def test_json_tree_simple(self):
        data = {"key": "value"}
        result = self.formatter_no_color.json_tree(data)
        self.assertIn("key", result)
        self.assertIn("value", result)

    def test_json_tree_nested(self):
        data = {"outer": {"inner": "value"}}
        result = self.formatter_no_color.json_tree(data)
        self.assertIn("outer", result)
        self.assertIn("inner", result)
        self.assertIn("value", result)

    def test_json_tree_list(self):
        data = {"items": ["a", "b"]}
        result = self.formatter_no_color.json_tree(data)
        self.assertIn("items", result)
        self.assertIn("a", result)
        self.assertIn("b", result)


class TestProgressBar(unittest.TestCase):
    @patch("sys.stdout", new_callable=StringIO)
    def test_progress_bar_creation(self, mock_stdout):
        bar = ProgressBar(total=10, width=20)
        self.assertEqual(bar.total, 10)
        self.assertEqual(bar.width, 20)
        self.assertEqual(bar.current, 0)

    @patch("sys.stdout.isatty", return_value=False)
    def test_progress_bar_update_no_tty(self, mock_isatty):
        bar = ProgressBar(total=10)
        bar.update(5)
        self.assertEqual(bar.current, 5)

    @patch("sys.stdout.isatty", return_value=False)
    def test_progress_bar_finish(self, mock_isatty):
        bar = ProgressBar(total=10)
        bar.finish()
        self.assertEqual(bar.current, 10)


class TestSpinner(unittest.TestCase):
    @patch("sys.stdout.isatty", return_value=False)
    def test_spinner_creation(self, mock_isatty):
        spinner = Spinner("Loading...")
        self.assertEqual(spinner.message, "Loading...")
        self.assertFalse(spinner.running)

    @patch("sys.stdout.isatty", return_value=False)
    def test_spinner_start(self, mock_isatty):
        spinner = Spinner("Loading...")
        spinner.start()
        self.assertTrue(spinner.running)

    @patch("sys.stdout.isatty", return_value=False)
    def test_spinner_update(self, mock_isatty):
        spinner = Spinner("Loading...")
        spinner.update("New message")
        self.assertEqual(spinner.message, "New message")

    @patch("sys.stdout.isatty", return_value=False)
    def test_spinner_stop(self, mock_isatty):
        spinner = Spinner("Loading...")
        spinner.start()
        spinner.stop()
        self.assertFalse(spinner.running)

    @patch("sys.stdout.isatty", return_value=False)
    def test_spinner_stop_with_message(self, mock_isatty):
        spinner = Spinner("Loading...")
        spinner.start()
        spinner.stop("Done!")
        self.assertFalse(spinner.running)


class TestFormatErrorDetails(unittest.TestCase):
    def test_format_simple_error(self):
        error = ValueError("Something went wrong")
        result = format_error_details(error)
        self.assertIn("ValueError", result)
        self.assertIn("Something went wrong", result)

    def test_format_error_with_attributes(self):
        class CustomError(Exception):
            def __init__(self, message: str, code: int):
                super().__init__(message)
                self.code = code

        error = CustomError("Error message", 42)
        result = format_error_details(error)
        self.assertIn("CustomError", result)
        self.assertIn("Error message", result)

    def test_format_error_no_color(self):
        formatter = OutputFormatter(use_color=False)
        error = RuntimeError("Test error")
        result = format_error_details(error, formatter=formatter)
        self.assertIn("RuntimeError", result)
        self.assertIn("Test error", result)


if __name__ == "__main__":
    unittest.main()
