"""
code_updater — helper do zarządzania własnym kodem serwera MCP.

Użycie:
    from mcp_helpers import add_code_updater
    from fastmcp import FastMCP

    mcp = FastMCP("my_server")
    add_code_updater(mcp, __file__)

Rejestruje dwa narzędzia:
    readCode()               — zwraca aktualny kod serwera
    updateCode(new_code: str) — pokazuje diff, pyta o potwierdzenie w konsoli serwera,
                                po zatwierdzeniu nadpisuje plik i restartuje serwer
"""
from __future__ import annotations

import asyncio
import os
import subprocess
import sys
import tempfile


def _show_diff_and_confirm(abs_path: str, new_code: str) -> bool:
    """Wyświetla diff stary→nowy w konsoli serwera i pyta o potwierdzenie.

    Zwraca True jeśli operator zatwierdził, False jeśli odrzucił.
    Gdy stdin nie jest TTY (serwer headless), auto-potwierdza.
    Działa synchronicznie — wywoływać przez run_in_executor.
    """
    if not sys.stdin.isatty():
        return True  # brak terminalu — auto-potwierdź

    with open(abs_path, encoding="utf-8") as f:
        old_code = f.read()

    with tempfile.NamedTemporaryFile("w", suffix=".py", delete=False, encoding="utf-8") as tmp:
        tmp.write(new_code)
        tmp_path = tmp.name

    try:
        result = subprocess.run(
            ["diff", "--color=always", "-u", abs_path, tmp_path],
            capture_output=False,
            text=True,
        )
        # diff zwraca 0 = brak zmian, 1 = są zmiany, 2 = błąd
        if result.returncode == 0:
            print("\n[updateCode] Brak zmian w kodzie.", flush=True)
            return False
    finally:
        os.unlink(tmp_path)

    print(flush=True)
    try:
        answer = input("[updateCode] Zatwierdź zmiany? [Enter = tak, n = odrzuć] ")
    except (EOFError, KeyboardInterrupt):
        print("\n[updateCode] Odrzucono (EOF/przerwanie).", flush=True)
        return False

    return answer.strip().lower() != "n"


def add_code_updater(mcp, file_path: str) -> None:
    """Rejestruje readCode i updateCode na instancji FastMCP.

    Args:
        mcp:       instancja FastMCP
        file_path: ścieżka do pliku serwera — przekaż __file__
    """
    abs_path = os.path.abspath(file_path)

    @mcp.tool()
    def readCode() -> str:
        """Zwraca aktualny kod źródłowy serwera."""
        with open(abs_path, encoding="utf-8") as f:
            return f.read()

    @mcp.tool()
    async def updateCode(new_code: str) -> str:
        """Aktualizuje kod źródłowy serwera i restartuje go.

        Przed zapisem wyświetla diff w konsoli serwera i czeka na potwierdzenie
        operatora. Jeśli operator odrzuci, zwraca informację bez zmian w pliku.

        Args:
            new_code: nowa treść pliku serwera
        """
        try:
            loop = asyncio.get_running_loop()
            confirmed = await loop.run_in_executor(
                None, lambda: _show_diff_and_confirm(abs_path, new_code)
            )

            if not confirmed:
                return "Aktualizacja odrzucona przez operatora."

            with open(abs_path, "w", encoding="utf-8") as f:
                f.write(new_code)

            async def _restart():
                await asyncio.sleep(1.0)
                os.execv(sys.executable, [sys.executable] + sys.argv)

            asyncio.get_running_loop().create_task(_restart())

            return f"Kod zaktualizowany ({abs_path}), restartuję..."
        except Exception as e:
            return f"Błąd podczas aktualizacji: {e}"
