"""Tests para comfyui_ensure_server.

Los tests no tocan systemd ni la red reales: inyectan un runner falso que
registra los comandos systemctl y se mockea el health check.
"""

import os
import subprocess

from . import comfyui_ensure_server as mod
from .comfyui_ensure_server import (
    _detect_lowvram,
    _render_unit,
    comfyui_ensure_server,
)


class FakeRunner:
    """Runner inyectable: respuestas programables por prefijo de comando."""

    def __init__(self, active_state="inactive"):
        self.calls = []
        self.active_state = active_state

    def __call__(self, cmd):
        self.calls.append(list(cmd))
        # nvidia-smi VRAM
        if cmd[:1] == ["nvidia-smi"]:
            return subprocess.CompletedProcess(cmd, 0, stdout="8192\n", stderr="")
        if cmd[:2] == ["systemctl", "--user"]:
            sub = cmd[2] if len(cmd) > 2 else ""
            if sub == "is-active":
                return subprocess.CompletedProcess(
                    cmd, 0, stdout=self.active_state + "\n", stderr=""
                )
            # daemon-reload, enable, start -> exito
            return subprocess.CompletedProcess(cmd, 0, stdout="", stderr="")
        if cmd[:1] == ["ss"]:
            return subprocess.CompletedProcess(cmd, 0, stdout="", stderr="")
        return subprocess.CompletedProcess(cmd, 0, stdout="", stderr="")

    def ran(self, *needle):
        return any(call[: len(needle)] == list(needle) for call in self.calls)


def _fake_comfy_dir(tmp_path):
    """Crea un comfyui_dir falso con .venv/bin/python y main.py."""
    d = tmp_path / "ComfyUI"
    (d / ".venv" / "bin").mkdir(parents=True)
    (d / ".venv" / "bin" / "python").write_text("#!/bin/sh\n")
    (d / "main.py").write_text("# fake\n")
    return d


# --- helpers puros ---

def test_detect_lowvram_umbral_8gb():
    assert _detect_lowvram(8192) is True
    assert _detect_lowvram(8200) is True
    assert _detect_lowvram(8201) is False
    assert _detect_lowvram(24564) is False
    assert _detect_lowvram(None) is False


def test_render_unit_restart_always_y_wantedby():
    unit = _render_unit(
        "/x/.venv/bin/python", "/x/main.py", "/x", 8188, True, "ComfyUI test"
    )
    assert "Restart=always" in unit
    assert "on-failure" not in unit  # regla function_tags
    assert "WantedBy=default.target" in unit
    assert "ExecStart=/x/.venv/bin/python /x/main.py --port 8188 --lowvram" in unit
    assert "WorkingDirectory=/x" in unit


def test_render_unit_sin_lowvram():
    unit = _render_unit(
        "/x/.venv/bin/python", "/x/main.py", "/x", 9000, False, "ComfyUI test"
    )
    assert "--lowvram" not in unit
    assert "--port 9000" in unit


# --- orquestacion (runner falso + health mockeado) ---

def test_error_si_falta_venv(tmp_path, monkeypatch):
    monkeypatch.setenv("HOME", str(tmp_path))
    res = comfyui_ensure_server(
        comfyui_dir=str(tmp_path / "no_existe"), runner=FakeRunner()
    )
    assert res["ok"] is False
    assert "venv python no encontrado" in res["error"]


def test_idempotente_si_ya_activo_y_sano(tmp_path, monkeypatch):
    monkeypatch.setenv("HOME", str(tmp_path))
    monkeypatch.setattr(mod, "_health", lambda *a, **k: True)
    d = _fake_comfy_dir(tmp_path)
    runner = FakeRunner(active_state="active")

    # 1a llamada: instala el unit por primera vez (changed -> idempotent False),
    # pero como ya esta active+sano NO debe arrancar nada.
    res1 = comfyui_ensure_server(comfyui_dir=str(d), runner=runner)
    assert res1["ok"] is True
    assert res1["health"] is True
    assert res1["active"] == "active"
    assert res1["idempotent"] is False  # escribio el unit por primera vez
    assert not runner.ran("systemctl", "--user", "start", "comfyui")

    # 2a llamada: el unit ya existe identico -> no toca nada -> idempotent True.
    runner2 = FakeRunner(active_state="active")
    res2 = comfyui_ensure_server(comfyui_dir=str(d), runner=runner2)
    assert res2["ok"] is True
    assert res2["idempotent"] is True
    assert res2["reloaded"] is False  # no reescribio el unit
    assert not runner2.ran("systemctl", "--user", "start", "comfyui")


def test_arranque_fresco_escribe_unit_y_arranca(tmp_path, monkeypatch):
    monkeypatch.setenv("HOME", str(tmp_path))
    # health: False antes de arrancar, True despues
    estados = iter([False, True, True, True])
    monkeypatch.setattr(mod, "_health", lambda *a, **k: next(estados, True))
    d = _fake_comfy_dir(tmp_path)
    runner = FakeRunner(active_state="inactive")
    res = comfyui_ensure_server(comfyui_dir=str(d), runner=runner, health_timeout=5)
    assert res["ok"] is True
    assert res["health"] is True
    assert res["lowvram"] is True  # nvidia-smi falso devuelve 8192
    assert runner.ran("systemctl", "--user", "daemon-reload")
    assert runner.ran("systemctl", "--user", "enable", "comfyui")
    assert runner.ran("systemctl", "--user", "start", "comfyui")
    # el unit quedo escrito
    unit_path = os.path.join(
        str(tmp_path), ".config", "systemd", "user", "comfyui.service"
    )
    assert os.path.exists(unit_path)
    with open(unit_path) as f:
        assert "Restart=always" in f.read()


def test_lowvram_forzado_false_omite_flag(tmp_path, monkeypatch):
    monkeypatch.setenv("HOME", str(tmp_path))
    estados = iter([False, True])
    monkeypatch.setattr(mod, "_health", lambda *a, **k: next(estados, True))
    d = _fake_comfy_dir(tmp_path)
    runner = FakeRunner(active_state="inactive")
    res = comfyui_ensure_server(
        comfyui_dir=str(d), runner=runner, lowvram=False, health_timeout=5
    )
    assert res["lowvram"] is False
    unit_path = os.path.join(
        str(tmp_path), ".config", "systemd", "user", "comfyui.service"
    )
    with open(unit_path) as f:
        assert "--lowvram" not in f.read()
