from __future__ import annotations

from pathlib import Path

import typer

from rimpack.core.modpack import (
    AliasLine,
    BlankLine,
    CommentLine,
    EntryLine,
    Modpack,
    ModpackFile,
    ModpackListReference,
    PackInfo,
    PackLayout,
    PackPaths,
    new_modpack_list_document,
    new_pack_toml_manifest,
    write_modpack,
)
from rimpack.core.mods.mod_folder import ModFolderError, load_mod_folder

DEFAULT_PACKAGE_ID_REFERENCES: tuple[str, ...] = (
    "ludeon.rimworld",
    "ludeon.rimworld.royalty",
    "ludeon.rimworld.ideology",
    "ludeon.rimworld.biotech",
    "ludeon.rimworld.anomaly",
)

DEFAULT_LIST_TEMPLATES: tuple[tuple[str, str], ...] = (
    ("mods/00_core.list", "Core"),
    ("mods/10_libraries.list", "Libraries"),
    ("mods/20_ui.list", "UI"),
    ("mods/30_gameplay.list", "Gameplay"),
    ("mods/40_qol.list", "QoL"),
    ("mods/50_overhaul.list", "Overhaul"),
    ("mods/60_factions.list", "Factions"),
    ("mods/70_combat.list", "Combat"),
    ("mods/80_graphics.list", "Graphics"),
    ("mods/90_textures.list", "Textures"),
    ("mods/99_patch.list", "Patch"),
)


def initialize_modpack_directory(
    target_directory: Path,
    *,
    rimworld_folder: Path | None,
) -> None:
    pack_toml_path = target_directory / "pack.toml"
    if pack_toml_path.exists():
        raise typer.Exit(code=1)

    _create_directory(target_directory)
    _create_directory(target_directory / "mods")
    _create_directory(target_directory / "config" / "Mod_Configs")
    _create_directory(target_directory / "generated")
    _create_directory(target_directory / "local" / "mods")

    list_files = tuple(path for path, _ in DEFAULT_LIST_TEMPLATES)
    _write_file(
        target_directory / "README.md",
        _default_readme_text(target_directory.name),
    )
    write_modpack(
        target_directory,
        _default_modpack(
            pack_name=target_directory.name,
            mod_files=list_files,
            rimworld_folder=rimworld_folder,
        ),
    )


def default_dlc_references(rimworld_folder: Path | None) -> tuple[str, ...]:
    return _default_dlc_references(rimworld_folder)


def _create_directory(path: Path) -> None:
    _ = path.mkdir(parents=True, exist_ok=True)


def _write_file(path: Path, content: str) -> None:
    _ = path.write_text(content, encoding="utf-8", newline="\n")


def _default_readme_text(pack_name: str) -> str:
    return (
        f"# {pack_name}\n\n"
        "RimWorld modpack project initialized by `rimpack init`.\n"
    )


def _default_pack_manifest(pack_name: str, mod_files: tuple[str, ...]):
    return new_pack_toml_manifest(
        pack=PackInfo(
            name=pack_name,
            rimworld_version="1.6",
            pack_format_version=1,
        ),
        layout=PackLayout(mod_files=mod_files),
        paths=PackPaths(
            config_dir="config/Mod_Configs",
            diagnostics_file="generated/diagnostics.json",
            resolved_load_order_file="generated/resolved_load_order.txt",
            local_mods_dir="local/mods",
        ),
    )


def _default_modpack(
    *,
    pack_name: str,
    mod_files: tuple[str, ...],
    rimworld_folder: Path | None,
) -> Modpack:
    return Modpack(
        manifest=_default_pack_manifest(pack_name, mod_files),
        mod_files=tuple(
            ModpackFile(
                path=path,
                document=(
                    _default_core_list_document(_default_dlc_references(rimworld_folder))
                    if path == "mods/00_core.list"
                    else _default_comment_only_list_document(title)
                ),
            )
            for path, title in DEFAULT_LIST_TEMPLATES
        ),
    )


def _default_core_list_document(dlc_references: tuple[str, ...]):
    lines: list[AliasLine | BlankLine | CommentLine | EntryLine] = [
        AliasLine(
            raw_text="@alias core",
            leading_whitespace="",
            whitespace_after_directive=" ",
            alias="core",
            trailing_whitespace="",
            line_ending="\n",
        ),
        BlankLine(raw_text="", 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",
        ),
    ]

    for dlc_reference in dlc_references:
        lines.append(
            EntryLine(
                raw_text=dlc_reference,
                leading_whitespace="",
                disabled=False,
                whitespace_after_disabled="",
                raw_reference_text=dlc_reference,
                reference=ModpackListReference(
                    kind="packageid",
                    value=dlc_reference.removeprefix("packageid:"),
                ),
                spacing_before_comment="",
                trailing_comment_text=None,
                line_ending="\n",
            ),
        )

    return new_modpack_list_document(tuple(lines))


def _default_comment_only_list_document(title: str):
    alias = title.casefold().replace(" ", "_")
    return new_modpack_list_document(
        (
            AliasLine(
                raw_text=f"@alias {alias}",
                leading_whitespace="",
                whitespace_after_directive=" ",
                alias=alias,
                trailing_whitespace="",
                line_ending="\n",
            ),
            BlankLine(raw_text="", line_ending="\n"),
            CommentLine(
                raw_text=f"# {title}",
                leading_whitespace="",
                comment_text=f" {title}",
                line_ending="\n",
            ),
        ),
    )


def _default_dlc_references(rimworld_folder: Path | None) -> tuple[str, ...]:
    if rimworld_folder is None:
        return ()

    data_directory = rimworld_folder / "Data"
    if not data_directory.is_dir():
        return ()

    detected_package_ids: set[str] = set()
    for child in sorted(data_directory.iterdir(), key=lambda path: path.name.casefold()):
        try:
            mod_folder = load_mod_folder(child)
        except ModFolderError:
            continue
        detected_package_ids.add(mod_folder.about.package_id.casefold())

    return tuple(
        f"packageid:{package_id}"
        for package_id in DEFAULT_PACKAGE_ID_REFERENCES[1:]
        if package_id in detected_package_ids
    )
