"""Install the workspace into a clean venv and open the projects page in a browser."""

from __future__ import annotations

import os
import socket
import subprocess
import sys
import time
import urllib.error
import urllib.request
from collections.abc import Mapping
from pathlib import Path

import pytest

sync_api = pytest.importorskip("playwright.sync_api")

_REPO_ROOT = Path(__file__).resolve().parents[1]
_KEPT_ENV = (
	"PATH",
	"HOME",
	"USER",
	"LOGNAME",
	"LANG",
	"LC_ALL",
	"LC_CTYPE",
	"TMPDIR",
	"TMP",
	"TEMP",
	"SYSTEMROOT",
)


def _fresh_env(overrides: Mapping[str, str] | None = None) -> dict[str, str]:
	env = {key: os.environ[key] for key in _KEPT_ENV if key in os.environ}
	if overrides:
		env.update(overrides)
	return env


def _venv_python(venv: Path) -> Path:
	if sys.platform == "win32":
		return venv / "Scripts" / "python.exe"
	return venv / "bin" / "python"


def _free_port() -> int:
	with socket.socket() as sock:
		sock.bind(("127.0.0.1", 0))
		return int(sock.getsockname()[1])


def _wait_until_ready(url: str, proc: subprocess.Popen[str], log_path: Path, timeout: float) -> None:
	deadline = time.monotonic() + timeout
	while time.monotonic() < deadline:
		if proc.poll() is not None:
			log = log_path.read_text(encoding="utf-8") if log_path.is_file() else ""
			raise RuntimeError(f"server exited with {proc.returncode}\n{log}")
		try:
			with urllib.request.urlopen(url, timeout=1) as response:
				if response.status == 200:
					return
		except (urllib.error.URLError, TimeoutError, OSError):
			time.sleep(0.1)
	log = log_path.read_text(encoding="utf-8") if log_path.is_file() else ""
	raise TimeoutError(f"server did not become ready: {url}\n{log}")


@pytest.mark.playwright
def test_projects_page_loads_without_auth(tmp_path: Path) -> None:
	projects = tmp_path / "projects"
	repo = projects / "demo"
	repo.mkdir(parents=True)
	subprocess.run(["git", "init", "-b", "main", str(repo)], check=True, capture_output=True)

	venv = tmp_path / "venv"
	subprocess.run(
		["uv", "sync", "--all-packages", "--frozen", "--no-dev"],
		cwd=_REPO_ROOT,
		env=_fresh_env({"UV_PROJECT_ENVIRONMENT": str(venv)}),
		check=True,
	)

	port = _free_port()
	base = f"http://127.0.0.1:{port}/"
	home = tmp_path / "home"
	home.mkdir()
	# Windows Path.home() reads USERPROFILE.
	server_env = _fresh_env({
		"HOME": str(home),
		"USERPROFILE": str(home),
		"PYGITWEB_AUTH": "0",
		"PYGITWEB_PROJECTROOT": str(projects),
		"PYGITWEB_PROJECTS_LIST": str(projects),
		"PYGITWEB_SETTINGS_CONFIG": str(tmp_path / "settings.json"),
		"PYGITWEB_AUTH_CONFIG": str(tmp_path / "auth.json"),
		"PYTHONUNBUFFERED": "1",
	})
	log_out_path = tmp_path / "server.out"
	log_err_path = tmp_path / "server.err"
	log_out = log_out_path.open("w", encoding="utf-8")
	log_err = log_err_path.open("w", encoding="utf-8")
	proc = subprocess.Popen(
		[
			str(_venv_python(venv)),
			"-m",
			"uvicorn",
			"pygitweb.main:app",
			"--host",
			"127.0.0.1",
			"--port",
			str(port),
		],
		cwd=tmp_path,
		env=server_env,
		stdout=log_out,
		stderr=log_err,
	)
	try:
		_wait_until_ready(base, proc, log_err_path, timeout=30)
		with sync_api.sync_playwright() as playwright:
			browser = playwright.chromium.launch(headless=True)
			try:
				page = browser.new_page()
				page.goto(base, wait_until="domcontentloaded")
				sync_api.expect(page).to_have_title("PyGitWeb - Projects")
				sync_api.expect(page.get_by_role("heading", name="Projects")).to_be_visible()
				sync_api.expect(page.get_by_role("link", name="demo", exact=True)).to_be_visible()
			finally:
				browser.close()
	finally:
		if proc.poll() is None:
			proc.terminate()
			try:
				proc.wait(timeout=10)
			except subprocess.TimeoutExpired:
				proc.kill()
				proc.wait(timeout=10)
		log_out.close()
		log_err.close()