import os
import tempfile
from pathlib import Path

import pytest
from scripts.redeploy_pages_cloud_runtime import (
    activate_modern_node,
    checkout_skip_reason,
    clean_js_build_tree,
    command_failure_detail,
    list_nvm_node_bins,
    merge_pages_functions,
    node_major,
    npm_install_command,
    pages_deploy_command,
    assemble_pages_output,
    pages_output_ready,
    require_node,
    _install_runtime,
)
from src.integrations.cursor_cli import _AGENT_PROMPT_POINTER, write_agent_prompt_file


def test_node_major_and_require_node(monkeypatch):
    assert node_major("v16.20.2") == 16
    assert node_major("v22.14.0") == 22
    monkeypatch.setattr("scripts.redeploy_pages_cloud_runtime.shutil.which", lambda _: "/usr/bin/node")

    def fake_run(*_args, **_kwargs):
        return type("R", (), {"stdout": "v16.20.2\n", "stderr": ""})()

    monkeypatch.setattr("scripts.redeploy_pages_cloud_runtime.subprocess.run", fake_run)
    with pytest.raises(RuntimeError, match="Node 20"):
        require_node(20)


def test_npm_install_ignores_lockfile():
    assert npm_install_command(Path("/tmp/app")) == ["npm", "install"]


def test_activate_modern_node_unsets_prefix_and_uses_nvm(monkeypatch):
    with tempfile.TemporaryDirectory() as raw:
        nvm_root = Path(raw)
        nvm_bin = nvm_root / "versions" / "node" / "v22.14.0" / "bin"
        nvm_bin.mkdir(parents=True)
        (nvm_bin / "node").write_text("", encoding="utf-8")
        monkeypatch.setenv("NVM_DIR", str(nvm_root))
        monkeypatch.setenv("npm_config_prefix", "/home2/p2hs/.npm-global")
        monkeypatch.setenv("PATH", "/usr/bin")

        def fake_which(name):
            if name == "node" and str(nvm_bin) in os.environ.get("PATH", ""):
                return str(nvm_bin / "node")
            return None

        def fake_run(cmd, **_kwargs):
            if cmd and str(cmd[0]).endswith("node") and str(nvm_bin) in str(cmd[0]):
                return type("R", (), {"stdout": "v22.14.0\n", "stderr": ""})()
            return type("R", (), {"stdout": "v16.20.2\n", "stderr": ""})()

        monkeypatch.setattr("scripts.redeploy_pages_cloud_runtime.shutil.which", fake_which)
        monkeypatch.setattr("scripts.redeploy_pages_cloud_runtime.subprocess.run", fake_run)

        assert list_nvm_node_bins(20) == [nvm_bin]
        version = activate_modern_node(20)
        assert version == "v22.14.0"
        assert "npm_config_prefix" not in os.environ
        assert str(nvm_bin) in os.environ["PATH"]


def test_pages_deploy_command_targets_direct_upload_branch(monkeypatch):
    monkeypatch.setattr(
        "scripts.redeploy_pages_cloud_runtime.shutil.which",
        lambda name: "/opt/npx" if name == "npx" else None,
    )
    cmd = pages_deploy_command(Path("/tmp/out"), "p2hs-p1q9l8dk", "preview")
    assert cmd[:3] == ["/opt/npx", "--yes", "wrangler@4"]
    assert "pages" in cmd
    assert "deploy" in cmd
    assert "--project-name=p2hs-p1q9l8dk" in cmd
    assert "--branch=preview" in cmd
    assert "--commit-dirty=true" in cmd


def test_checkout_skip_reason():
    with tempfile.TemporaryDirectory() as raw:
        root = Path(raw)
        missing = root / "gone"
        assert checkout_skip_reason(missing, "html") == "checkout missing"

        html = root / "html"
        html.mkdir()
        assert checkout_skip_reason(html, "html") == "no index.html or functions/"
        (html / "index.html").write_text("<h1>ok</h1>", encoding="utf-8")
        assert checkout_skip_reason(html, "html") is None

        nuxt = root / "nuxt"
        nuxt.mkdir()
        assert checkout_skip_reason(nuxt, "nuxt_app") == "no package.json"
        (nuxt / "package.json").write_text("{}", encoding="utf-8")
        assert checkout_skip_reason(nuxt, "nuxt_app") is None


def test_merge_pages_functions_skips_nitro_worker():
    with tempfile.TemporaryDirectory() as raw:
        root = Path(raw)
        repo = root / "repo"
        (repo / "functions").mkdir(parents=True)
        (repo / "functions" / "hello.js").write_text("export default {}", encoding="utf-8")
        nitro = root / "out"
        nitro.mkdir()
        (nitro / "_worker.js").write_text("export default {}", encoding="utf-8")
        merge_pages_functions(repo, nitro)
        assert not (nitro / "functions").exists()

        static = root / "static"
        static.mkdir()
        merge_pages_functions(repo, static)
        assert (static / "functions" / "hello.js").is_file()


def test_command_failure_detail_keeps_stderr_errors():
    stderr = "ERROR  [nitro] Failed to prerender /admin\n" + ("w" * 80)
    stdout = ("ℹ .nuxt/dist/client/_nuxt/x.js  1 kB\n" * 80) + "[nitro] Building Nuxt Nitro server\n"
    detail = command_failure_detail(stderr, stdout, limit=80)
    assert "Failed to prerender /admin" in detail
    assert "---- errors ----" in detail
    assert detail.count("_nuxt") < 5


def test_activate_prefers_nvm_22_over_path_node20(monkeypatch):
    with tempfile.TemporaryDirectory() as raw:
        nvm_root = Path(raw)
        nvm22 = nvm_root / "versions" / "node" / "v22.23.2" / "bin"
        nvm22.mkdir(parents=True)
        (nvm22 / "node").write_text("", encoding="utf-8")
        monkeypatch.setenv("NVM_DIR", str(nvm_root))
        monkeypatch.setenv("PATH", "/usr/bin")

        def fake_which(name):
            path = os.environ.get("PATH", "")
            if name == "node" and str(nvm22) in path:
                return str(nvm22 / "node")
            if name == "node":
                return "/usr/bin/node"
            return None

        def fake_run(cmd, **_kwargs):
            exe = str(cmd[0]) if cmd else ""
            if str(nvm22) in exe:
                return type("R", (), {"stdout": "v22.23.2\n", "stderr": ""})()
            return type("R", (), {"stdout": "v20.20.2\n", "stderr": ""})()

        monkeypatch.setattr("scripts.redeploy_pages_cloud_runtime.shutil.which", fake_which)
        monkeypatch.setattr("scripts.redeploy_pages_cloud_runtime.subprocess.run", fake_run)
        assert activate_modern_node(20) == "v22.23.2"
        assert str(nvm22) in os.environ["PATH"]


def test_pages_output_ready_accepts_public_index():
    with tempfile.TemporaryDirectory() as raw:
        repo = Path(raw)
        public = repo / ".output" / "public"
        public.mkdir(parents=True)
        assert pages_output_ready(repo, "nuxt_app") is False
        (public / "index.html").write_text("<html></html>", encoding="utf-8")
        assert pages_output_ready(repo, "nuxt_app") is True


def test_assemble_pages_output_promotes_spa_fallback():
    with tempfile.TemporaryDirectory() as raw:
        repo = Path(raw)
        client = repo / ".nuxt" / "dist" / "client"
        client.mkdir(parents=True)
        (client / "200.html").write_text("<html>spa</html>", encoding="utf-8")
        (client / "_nuxt").mkdir()
        (client / "_nuxt" / "entry.js").write_text("export {}", encoding="utf-8")
        public = assemble_pages_output(repo)
        assert public is not None
        assert (public / "index.html").read_text(encoding="utf-8") == "<html>spa</html>"


def test_install_runtime_writes_catalog_jobs_and_backs_up_homemade(monkeypatch):
    with tempfile.TemporaryDirectory() as raw:
        root = Path(raw)
        monkeypatch.setattr(
            "scripts.redeploy_pages_cloud_runtime.write_managed_files",
            lambda *_a, **_k: [],
        )
        retired: list = []
        monkeypatch.setattr(
            "scripts.redeploy_pages_cloud_runtime.retire_shadow_store_files",
            lambda *args, **kwargs: retired.append((args, kwargs)) or [],
        )
        homemade = root / "server/api/auth/forgot-password.ts"
        homemade.parent.mkdir(parents=True)
        homemade.write_text(
            "export default defineEventHandler(async () => ({ success: true }));\n",
            encoding="utf-8",
        )
        result = _install_runtime(root, "nuxt_app", retire=False)
        assert retired == []
        lock = (root / "p2hs/platform-ports.lock.json").read_text(encoding="utf-8")
        assert "jobs.email_send" in lock
        jobs = (root / "server/api/jobs/email.send.post.ts").read_text(encoding="utf-8")
        assert "platformRequest" in jobs
        written = (root / "server/api/auth/forgot-password.ts").read_text(encoding="utf-8")
        assert "p2hs-auth-password" in written
        backup = root / ".p2hs/local-bak/server/api/auth/forgot-password.ts"
        assert backup.is_file()
        assert "success: true" in backup.read_text(encoding="utf-8")
        assert "server/api/jobs/email.send.post.ts" in result["written"]


def test_clean_js_build_tree_removes_stale_install():
    with tempfile.TemporaryDirectory() as raw:
        repo = Path(raw)
        (repo / "node_modules").mkdir()
        (repo / ".nuxt").mkdir()
        (repo / "src").mkdir()
        assert clean_js_build_tree(repo) == ["node_modules", ".nuxt"]
        assert not (repo / "node_modules").exists()
        assert (repo / "src").is_dir()


def test_write_agent_prompt_file_returns_short_pointer():
    with tempfile.TemporaryDirectory() as raw:
        huge = "x" * 200_000
        pointer = write_agent_prompt_file(raw, huge)
        assert pointer == _AGENT_PROMPT_POINTER
        assert len(pointer) < 200
        written = Path(raw, ".p2hs/ai-context/agent-prompt.md").read_text(encoding="utf-8")
        assert written == huge
