from __future__ import annotations

from pathlib import Path

from rimpack.core.modpack import (
    AliasLine,
    BlankLine,
    CommentLine,
    EntryLine,
    ModpackListDocument,
    ModpackListReference,
    UnknownLine,
    load_modpack_list,
    new_modpack_list_document,
    parse_modpack_list,
    parse_modpack_list_line,
    render_modpack_list,
)


def test_parse_modpack_list_preserves_line_types_and_raw_text() -> None:
    document = parse_modpack_list(
        """\
@alias core
# Core game
packageid:Ludeon.RimWorld

    # Libraries
!   steam:2009463077  # Harmony
banana
""",
    )

    assert document == ModpackListDocument(
        lines=(
            AliasLine(
                raw_text="@alias core",
                leading_whitespace="",
                whitespace_after_directive=" ",
                alias="core",
                trailing_whitespace="",
                line_ending="\n",
            ),
            CommentLine(
                raw_text="# Core game",
                leading_whitespace="",
                comment_text=" Core game",
                line_ending="\n",
            ),
            EntryLine(
                raw_text="packageid:Ludeon.RimWorld",
                leading_whitespace="",
                disabled=False,
                whitespace_after_disabled="",
                raw_reference_text="packageid:Ludeon.RimWorld",
                reference=ModpackListReference(
                    kind="packageid",
                    value="Ludeon.RimWorld",
                ),
                spacing_before_comment="",
                trailing_comment_text=None,
                line_ending="\n",
            ),
            BlankLine(raw_text="", line_ending="\n"),
            CommentLine(
                raw_text="    # Libraries",
                leading_whitespace="    ",
                comment_text=" Libraries",
                line_ending="\n",
            ),
            EntryLine(
                raw_text="!   steam:2009463077  # Harmony",
                leading_whitespace="",
                disabled=True,
                whitespace_after_disabled="   ",
                raw_reference_text="steam:2009463077",
                reference=ModpackListReference(
                    kind="steam",
                    value="2009463077",
                ),
                spacing_before_comment="  ",
                trailing_comment_text=" Harmony",
                line_ending="\n",
            ),
            UnknownLine(raw_text="banana", line_ending="\n"),
        ),
        source_text=(
            "@alias core\n"
            "# Core game\n"
            "packageid:Ludeon.RimWorld\n"
            "\n"
            "    # Libraries\n"
            "!   steam:2009463077  # Harmony\n"
            "banana\n"
        ),
    )


def test_parse_each_supported_reference_kind() -> None:
    assert parse_modpack_list_line("@alias core") == AliasLine(
        raw_text="@alias core",
        leading_whitespace="",
        whitespace_after_directive=" ",
        alias="core",
        trailing_whitespace="",
    )
    assert parse_modpack_list_line("packageid:Ludeon.RimWorld") == EntryLine(
        raw_text="packageid:Ludeon.RimWorld",
        leading_whitespace="",
        disabled=False,
        whitespace_after_disabled="",
        raw_reference_text="packageid:Ludeon.RimWorld",
        reference=ModpackListReference(
            kind="packageid",
            value="Ludeon.RimWorld",
        ),
        spacing_before_comment="",
        trailing_comment_text=None,
    )
    assert parse_modpack_list_line("packageid:Ludeon.RimWorld.Royalty") == EntryLine(
        raw_text="packageid:Ludeon.RimWorld.Royalty",
        leading_whitespace="",
        disabled=False,
        whitespace_after_disabled="",
        raw_reference_text="packageid:Ludeon.RimWorld.Royalty",
        reference=ModpackListReference(
            kind="packageid",
            value="Ludeon.RimWorld.Royalty",
        ),
        spacing_before_comment="",
        trailing_comment_text=None,
    )
    assert parse_modpack_list_line("packageid:brrainz.harmony") == EntryLine(
        raw_text="packageid:brrainz.harmony",
        leading_whitespace="",
        disabled=False,
        whitespace_after_disabled="",
        raw_reference_text="packageid:brrainz.harmony",
        reference=ModpackListReference(
            kind="packageid",
            value="brrainz.harmony",
        ),
        spacing_before_comment="",
        trailing_comment_text=None,
    )
    assert parse_modpack_list_line("local:MyFork") == EntryLine(
        raw_text="local:MyFork",
        leading_whitespace="",
        disabled=False,
        whitespace_after_disabled="",
        raw_reference_text="local:MyFork",
        reference=ModpackListReference(kind="local", value="MyFork"),
        spacing_before_comment="",
        trailing_comment_text=None,
    )


def test_treat_malformed_entries_as_unknown() -> None:
    assert parse_modpack_list_line("@alias") == UnknownLine(raw_text="@alias")
    assert parse_modpack_list_line("@alias core extra") == UnknownLine(
        raw_text="@alias core extra",
    )
    assert parse_modpack_list_line("steam::") == UnknownLine(raw_text="steam::")
    assert parse_modpack_list_line("dlc:biotech") == UnknownLine(raw_text="dlc:biotech")
    assert parse_modpack_list_line("??? mystery entry") == UnknownLine(
        raw_text="??? mystery entry",
    )


def test_load_modpack_list_from_path() -> None:
    path = Path("tests") / "__tmp_modpack.list"
    try:
        _ = path.write_text(
            """\
core
!local:MyFork  # disabled for testing
""",
            encoding="utf-8",
        )

        document = load_modpack_list(path)

        assert len(document.lines) == 2
        assert document.lines[1] == EntryLine(
            raw_text="!local:MyFork  # disabled for testing",
            leading_whitespace="",
            disabled=True,
            whitespace_after_disabled="",
            raw_reference_text="local:MyFork",
            reference=ModpackListReference(kind="local", value="MyFork"),
            spacing_before_comment="  ",
            trailing_comment_text=" disabled for testing",
            line_ending="\n",
        )
    finally:
        path.unlink(missing_ok=True)


def test_preserve_list_line_endings() -> None:
    document = parse_modpack_list("packageid:Ludeon.RimWorld\r\n# comment\r\n")

    assert document.lines == (
        EntryLine(
            raw_text="packageid:Ludeon.RimWorld",
            leading_whitespace="",
            disabled=False,
            whitespace_after_disabled="",
            raw_reference_text="packageid:Ludeon.RimWorld",
            reference=ModpackListReference(
                kind="packageid",
                value="Ludeon.RimWorld",
            ),
            spacing_before_comment="",
            trailing_comment_text=None,
            line_ending="\r\n",
        ),
        CommentLine(
            raw_text="# comment",
            leading_whitespace="",
            comment_text=" comment",
            line_ending="\r\n",
        ),
    )


def test_render_modpack_list_round_trips_parsed_document() -> None:
    source = "@alias header\r\n# Header\r\n!steam:2009463077  # Harmony\r\nbanana\r\n"

    document = parse_modpack_list(source)

    assert render_modpack_list(document) == source


def test_render_new_modpack_list_document_uses_node_structure() -> None:
    document = new_modpack_list_document(
        (
            AliasLine(
                raw_text="@alias libraries",
                leading_whitespace="",
                whitespace_after_directive=" ",
                alias="libraries",
                trailing_whitespace="",
                line_ending="\n",
            ),
            CommentLine(
                raw_text="# Libraries",
                leading_whitespace="",
                comment_text=" Libraries",
                line_ending="\n",
            ),
            EntryLine(
                raw_text="steam:818773962  # HugsLib",
                leading_whitespace="",
                disabled=False,
                whitespace_after_disabled="",
                raw_reference_text="steam:818773962",
                reference=ModpackListReference(kind="steam", value="818773962"),
                spacing_before_comment="  ",
                trailing_comment_text=" HugsLib",
                line_ending="\n",
            ),
            BlankLine(raw_text="", line_ending="\n"),
        ),
    )

    assert render_modpack_list(document) == (
        "@alias libraries\n# Libraries\nsteam:818773962  # HugsLib\n\n"
    )
