import unittest

from subscription_manager.cli_command.list import AVAILABLE_SUBS_MATCH_COLUMNS
from subscription_manager.cli_command import status
from subscription_manager.printing_utils import (
    format_name,
    columnize,
    echo_columnize_callback,
    none_wrap_columnize_callback,
    highlight_by_filter_string_columnize_cb,
    FONT_BOLD,
    FONT_RED,
    FONT_NORMAL,
)

from unittest.mock import patch, Mock


class TestFormatName(unittest.TestCase):
    def setUp(self):
        self.indent = 1
        self.max_length = 40

    def test_format_name_long(self):
        name = (
            "This is a Really Long Name For A Product That We Do Not "
            "Want To See But Should Be Able To Deal With"
        )
        format_name(name, self.indent, self.max_length)

    def test_format_name_short(self):
        name = "a"
        format_name(name, self.indent, self.max_length)

    def test_format_name_empty(self):
        name = ""
        new_name = format_name(name, self.indent, self.max_length)
        self.assertEqual(name, new_name)

    def test_format_name_null_width(self):
        name = (
            "This is a Really Long Name For A Product That We Do Not "
            "Want To See But Should Be Able To Deal With"
        )
        new_name = format_name(name, self.indent, None)
        self.assertEqual(name, new_name)

    def test_format_name_none(self):
        name = None
        new_name = format_name(name, self.indent, self.max_length)
        self.assertTrue(new_name is None)

    def test_leading_spaces(self):
        name = " " * 4 + "I have four leading spaces"
        new_name = format_name(name, 3, 10)
        self.assertEqual("    I have\n   four\n   leading\n   spaces", new_name)

    def test_leading_tabs(self):
        name = "\t" * 4 + "I have four leading tabs"
        new_name = format_name(name, self.indent, self.max_length)
        self.assertEqual("\t" * 4, new_name[0:4])


class TestHighlightByFilter(unittest.TestCase):
    def test_highlight_by_filter_string(self):
        args = ["Super Test Subscription"]
        kwargs = {
            "filter_string": "Super*",
            "match_columns": AVAILABLE_SUBS_MATCH_COLUMNS,
            "caption": "Subscription Name:",
            "is_atty": True,
        }
        result = highlight_by_filter_string_columnize_cb("Subscription Name:    %s", *args, **kwargs)
        self.assertEqual(
            result, "Subscription Name:    " + FONT_BOLD + FONT_RED + "Super Test Subscription" + FONT_NORMAL
        )

    def test_highlight_by_filter_string_single(self):
        args = ["Super Test Subscription"]
        kwargs = {
            "filter_string": "*Subscriptio?",
            "match_columns": AVAILABLE_SUBS_MATCH_COLUMNS,
            "caption": "Subscription Name:",
            "is_atty": True,
        }
        result = highlight_by_filter_string_columnize_cb("Subscription Name:    %s", *args, **kwargs)
        self.assertEqual(
            result, "Subscription Name:    " + FONT_BOLD + FONT_RED + "Super Test Subscription" + FONT_NORMAL
        )

    def test_highlight_by_filter_string_all(self):
        args = ["Super Test Subscription"]
        kwargs = {
            "filter_string": "*",
            "match_columns": AVAILABLE_SUBS_MATCH_COLUMNS,
            "caption": "Subscription Name:",
            "is_atty": True,
        }
        result = highlight_by_filter_string_columnize_cb("Subscription Name:    %s", *args, **kwargs)
        self.assertEqual(result, "Subscription Name:    Super Test Subscription")

    def test_highlight_by_filter_string_exact(self):
        args = ["Premium"]
        kwargs = {
            "filter_string": "Premium",
            "match_columns": AVAILABLE_SUBS_MATCH_COLUMNS,
            "caption": "Service Level:",
            "is_atty": True,
        }
        result = highlight_by_filter_string_columnize_cb("Service Level:    %s", *args, **kwargs)
        self.assertEqual(result, "Service Level:    " + FONT_BOLD + FONT_RED + "Premium" + FONT_NORMAL)

    def test_highlight_by_filter_string_list_row(self):
        args = ["Awesome-os-stacked"]
        kwargs = {
            "filter_string": "Awesome*",
            "match_columns": AVAILABLE_SUBS_MATCH_COLUMNS,
            "caption": "Subscription Name:",
            "is_atty": True,
        }
        result = highlight_by_filter_string_columnize_cb("    %s", *args, **kwargs)
        self.assertEqual(result, "    " + FONT_BOLD + FONT_RED + "Awesome-os-stacked" + FONT_NORMAL)


class TestNoneWrap(unittest.TestCase):
    def test_none_wrap(self):
        result = none_wrap_columnize_callback("foo %s %s", "doberman pinscher", None)
        self.assertEqual(result, "foo doberman pinscher None")


class TestColumnize(unittest.TestCase):
    def setUp(self):
        self.old_method = status.get_terminal_width
        status.get_terminal_width = Mock(return_value=500)

    def tearDown(self):
        status.get_terminal_width = self.old_method

    def test_columnize(self):
        result = columnize(["Hello:", "Foo:"], echo_columnize_callback, "world", "bar")
        self.assertEqual(result, "Hello: world\nFoo:   bar")

    def test_columnize_with_list(self):
        result = columnize(["Hello:", "Foo:"], echo_columnize_callback, ["world", "space"], "bar")
        self.assertEqual(result, "Hello: world\n       space\nFoo:   bar")

    def test_columnize_with_empty_list(self):
        result = columnize(["Hello:", "Foo:"], echo_columnize_callback, [], "bar")
        self.assertEqual(result, "Hello: \nFoo:   bar")

    @patch("subscription_manager.printing_utils.get_terminal_width")
    def test_columnize_with_small_term(self, term_width_mock):
        term_width_mock.return_value = None
        result = columnize(
            ["Hello Hello Hello Hello:", "Foo Foo Foo Foo:"],
            echo_columnize_callback,
            "This is a testing string",
            "This_is_another_testing_string",
        )
        expected = (
            "Hello\nHello\nHello\nHello\n:     This\n      is a\n      "
            "testin\n      g\n      string\nFoo\nFoo\nFoo\nFoo:  "
            "This_i\n      s_anot\n      her_te\n      sting_\n      string"
        )
        self.assertNotEqual(result, expected)
        term_width_mock.return_value = 12
        result = columnize(
            ["Hello Hello Hello Hello:", "Foo Foo Foo Foo:"],
            echo_columnize_callback,
            "This is a testing string",
            "This_is_another_testing_string",
        )
        self.assertEqual(result, expected)

    def test_format_name_no_break_no_indent(self):
        result = format_name("testing string testing st", 0, 10)
        expected = "testing\nstring\ntesting st"
        self.assertEqual(result, expected)

    def test_format_name_no_break(self):
        result = format_name("testing string testing st", 1, 11)
        expected = "testing\n string\n testing st"
        self.assertEqual(result, expected)
        result = format_name("testing string testing st", 2, 12)
        expected = "testing\n  string\n  testing st"
        self.assertEqual(result, expected)

    def test_format_name_break(self):
        result = format_name("a" * 10, 0, 10)
        expected = "a" * 10
        self.assertEqual(result, expected)
        result = format_name("a" * 11, 0, 10)
        expected = "a" * 10 + "\na"
        self.assertEqual(result, expected)
        result = format_name("a" * 11 + " " + "a" * 9, 0, 10)
        expected = "a" * 10 + "\na\n" + "a" * 9
        self.assertEqual(result, expected)

    def test_format_name_break_indent(self):
        result = format_name("a" * 20, 1, 10)
        expected = "a" * 9 + "\n " + "a" * 9 + "\n " + "aa"
        self.assertEqual(result, expected)

    @patch("subscription_manager.printing_utils.get_terminal_width")
    def test_columnize_multibyte(self, term_width_mock):
        multibyte_str = "このシステム用に"
        term_width_mock.return_value = 40
        result = columnize([multibyte_str], echo_columnize_callback, multibyte_str)
        expected = "このシステム用に このシステム用に"
        self.assertEqual(result, expected)
        term_width_mock.return_value = 14
        result = columnize([multibyte_str], echo_columnize_callback, multibyte_str)
        expected = "このシ\nステム\n用に   このシ\n       ステム\n       用に"
        self.assertEqual(result, expected)
