"""Tests for colorize."""

from __future__ import annotations

from typing import TYPE_CHECKING
from typing import NamedTuple
from unittest.mock import MagicMock  # pylint: disable=W0407
from unittest.mock import patch  # pylint: disable=W0407


if TYPE_CHECKING:
    import pytest

from ansible_navigator.constants import GRAMMAR_DIR
from ansible_navigator.constants import THEME_PATH
from ansible_navigator.content_defs import ContentFormat
from ansible_navigator.content_defs import ContentView
from ansible_navigator.content_defs import SerializationFormat
from ansible_navigator.ui_framework.colorize import Colorize
from ansible_navigator.ui_framework.curses_defs import SimpleLinePart
from ansible_navigator.utils.serialize import serialize


class Sample(NamedTuple):
    """Sample data for colorize tests."""

    serialization_format: SerializationFormat
    content: dict[str, str] = {"test": "data"}
    content_view: ContentView = ContentView.NORMAL


SAMPLE_JSON = Sample(serialization_format=SerializationFormat.JSON)._asdict()
SAMPLE_YAML = Sample(serialization_format=SerializationFormat.YAML)._asdict()


def test_basic_success_json() -> None:
    """Ensure the json string is as expected.

    Test that the json string is returned as 1 line, 5 parts and can be
    reassembled to the json string.
    """
    sample = str(serialize(**SAMPLE_JSON)) + "\n"
    colorized = Colorize(grammar_dir=GRAMMAR_DIR, theme_path=THEME_PATH).render(
        doc=sample,
        scope="source.json",
    )
    assert len(colorized) == 3
    serialized_lines = sample.splitlines(keepends=True)
    colorized_lines = ["".join(part.chars for part in line) for line in colorized]
    assert serialized_lines == colorized_lines
    assert "".join(serialized_lines) == sample


def test_basic_success_yaml() -> None:
    """Ensure the yaml string is returned as expected.

    Check that the yaml string is returned as 2 lines, with 1 and 3 parts
    respectively, ensure the parts of the second line can be reassembled to
    the second line of the yaml string
    """
    sample = serialize(**SAMPLE_YAML)
    result = Colorize(grammar_dir=GRAMMAR_DIR, theme_path=THEME_PATH).render(
        doc=sample,
        scope="source.yaml",
    )
    assert len(result) == 2
    assert len(result[0]) == 1
    assert sample
    assert result[0][0].chars == sample.splitlines(keepends=True)[0]
    assert len(result[1]) == 4
    assert (
        "".join(line_part.chars for line_part in result[1]) == sample.splitlines(keepends=True)[1]
    )


def test_basic_success_log() -> None:
    """Ensure the log string is returned as 1 line, with 5 parts.

    Also ensure the parts can be reassembled to match the string.
    """
    sample = "1 ERROR text 42\n"

    result = Colorize(grammar_dir=GRAMMAR_DIR, theme_path=THEME_PATH).render(
        doc=sample,
        scope="text.log",
    )
    assert len(result) == 1
    first_line = result[0]
    line_parts = tuple(p.chars for p in first_line)
    assert line_parts == ("1", " ", "ERROR", " text ", "42", "\n")
    assert "".join(line_parts) == sample


def test_basic_success_no_color() -> None:
    """Ensure scope ``no_color`` return just lines."""
    sample = serialize(**SAMPLE_JSON)
    colorized = Colorize(grammar_dir=GRAMMAR_DIR, theme_path=THEME_PATH).render(
        doc=sample,
        scope="no_color",
    )
    assert not any(part.color for line in colorized for part in line)


@patch("ansible_navigator.ui_framework.colorize.tokenize")
def test_graceful_failure(mocked_func: MagicMock, caplog: pytest.LogCaptureFixture) -> None:
    """Test for correct error format.

    Ensure a tokenization error returns the original one line json string
    w/o color and the log reflects the critical error.

    Args:
        mocked_func: Mocked fixture
        caplog: Capture log
    """
    mocked_func.side_effect = ValueError()
    sample = serialize(**SAMPLE_JSON)

    _result = Colorize(grammar_dir=GRAMMAR_DIR, theme_path=THEME_PATH).render(
        doc=sample,
        scope="source.json",
    )
    assert "rendered without color" in caplog.text


YAML_TXT = """
- ansible.builtin.debug:
    var: before

# this is a comment

- ansible.builtin.debug:
    var: after
"""

YAML_TXT_EXPECTED = [
    [SimpleLinePart(chars="\n", column=0, color=None, style=None)],
    [
        SimpleLinePart(chars="- ", column=0, color=None, style=None),
        SimpleLinePart(chars="ansible.builtin.debug", column=2, color=(86, 156, 214), style=None),
        SimpleLinePart(chars=":\n", column=23, color=None, style=None),
    ],
    [
        SimpleLinePart(chars="    ", column=0, color=None, style=None),
        SimpleLinePart(chars="var", column=4, color=(86, 156, 214), style=None),
        SimpleLinePart(chars=": ", column=7, color=None, style=None),
        SimpleLinePart(chars="before", column=9, color=(206, 145, 120), style=None),
        SimpleLinePart(chars="\n", column=15, color=None, style=None),
    ],
    [SimpleLinePart(chars="\n", column=0, color=None, style=None)],
    [SimpleLinePart(chars="# this is a comment\n", column=0, color=(106, 153, 85), style=None)],
    [SimpleLinePart(chars="\n", column=0, color=None, style=None)],
    [
        SimpleLinePart(chars="- ", column=0, color=None, style=None),
        SimpleLinePart(chars="ansible.builtin.debug", column=2, color=(86, 156, 214), style=None),
        SimpleLinePart(chars=":\n", column=23, color=None, style=None),
    ],
    [
        SimpleLinePart(chars="    ", column=0, color=None, style=None),
        SimpleLinePart(chars="var", column=4, color=(86, 156, 214), style=None),
        SimpleLinePart(chars=": ", column=7, color=None, style=None),
        SimpleLinePart(chars="after", column=9, color=(206, 145, 120), style=None),
        SimpleLinePart(chars="\n", column=14, color=None, style=None),
    ],
]


def test_basic_success_yaml_text() -> None:
    """Ensure the yaml string is returned match the expected tokens."""
    content_format = ContentFormat.YAML_TXT

    result = Colorize(grammar_dir=GRAMMAR_DIR, theme_path=THEME_PATH).render(
        doc=YAML_TXT,
        scope=content_format.value.scope,
    )
    assert result == YAML_TXT_EXPECTED


STYLED_MARKDOWN = """\
## This is a header
"""


def test_styled_markdown() -> None:
    """Ensure style is captured from markdown correctly."""
    content_format = ContentFormat.MARKDOWN

    result = Colorize(grammar_dir=GRAMMAR_DIR, theme_path=THEME_PATH).render(
        doc=STYLED_MARKDOWN,
        scope=content_format.value.scope,
    )
    assert result == [
        [SimpleLinePart(chars="This is a header\n", column=0, color=(86, 156, 214), style="bold")],
    ]
