"""VS Code (settings + extensions), Zed, Ghostty."""

from __future__ import annotations

from pathlib import Path

from osx_backup.context import Context
from osx_backup.modules.base import (
    Inventory,
    Module,
    ModuleResult,
    ProgressReporter,
    register,
    stderr_detail,
)
from osx_backup.modules.pathset import PathItem, backup_paths, inventory_paths, restore_paths

VSCODE_USER = "Library/Application Support/Code/User"
EXTENSIONS_FILE = "vscode-extensions.txt"

NO_CODE_CLI = "CLI `code` introuvable : extensions VS Code non listées"

EDITOR_ITEMS: list[PathItem] = [
    PathItem(f"{VSCODE_USER}/settings.json"),
    PathItem(f"{VSCODE_USER}/keybindings.json"),
    PathItem(f"{VSCODE_USER}/snippets"),
    PathItem(".config/zed"),
    PathItem("Library/Application Support/com.mitchellh.ghostty"),
    PathItem(".config/ghostty"),
]


def _extension_ids(text: str) -> list[str]:
    return [line.strip() for line in text.splitlines() if line.strip()]


@register
class EditorsModule(Module):
    name = "editors"
    description = "VS Code (settings, extensions), Zed, Ghostty"
    order = 70

    def inventory(self, ctx: Context) -> Inventory:
        inv = inventory_paths(ctx, EDITOR_ITEMS)
        if ctx.runner.which("code") is None:
            inv.warnings.append(NO_CODE_CLI)
        return inv

    def backup(self, ctx: Context, progress: ProgressReporter) -> ModuleResult:
        result = backup_paths(ctx, self.name, EDITOR_ITEMS, progress)
        # L'absence du CLI `code` n'est pas un échec : les settings sont sauvegardés,
        # seule la liste des extensions manquera.
        if ctx.runner.which("code") is None:
            result.warnings.append(NO_CODE_CLI)
            return result
        # readonly=True : la liste doit aussi être établie en dry-run.
        res = ctx.runner.run(["code", "--list-extensions"], check=False, readonly=True)
        if not res.ok:
            result.warnings.append("`code --list-extensions` a échoué")
            return result
        ids = _extension_ids(res.stdout)
        ctx.write_text(ctx.module_dir(self.name) / EXTENSIONS_FILE, "\n".join(ids) + "\n")
        result.items += len(ids)
        return result

    def restore(self, ctx: Context, progress: ProgressReporter) -> ModuleResult:
        result = restore_paths(ctx, self.name, progress)
        ext_file: Path = ctx.dest / self.name / EXTENSIONS_FILE
        if not ext_file.exists():
            return result
        if ctx.runner.which("code") is None:
            result.warnings.append("CLI `code` introuvable : extensions non installées")
            return result
        ids = _extension_ids(ext_file.read_text(encoding="utf-8"))
        task = progress.task("extensions VS Code", total=len(ids))
        for ext in ids:
            # Une extension retirée du marketplace ne doit pas arrêter les suivantes.
            res = ctx.runner.run(["code", "--install-extension", ext], check=False)
            if not res.ok:
                result.warnings.append(
                    f"extension {ext} : échec"
                    f"{stderr_detail(ctx, f'code --install-extension {ext}', res)}"
                )
            task.advance()
        task.finish()
        return result
