#!/usr/bin/env python3
"""Exercise the real PHP front controller in an isolated temporary deployment."""
from __future__ import annotations

import json
import shutil
import socket
import subprocess
import tempfile
import time
import urllib.error
import urllib.request
from pathlib import Path

ROOT = Path(__file__).resolve().parents[2]
SOURCE_APP = ROOT / "app"
REQUIRED_EXTENSIONS = {"sqlite3", "curl", "json", "openssl", "mbstring", "fileinfo", "zip"}


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


def php_modules() -> set[str]:
    result = subprocess.run(["php", "-m"], check=True, capture_output=True, text=True)
    return {line.strip().lower() for line in result.stdout.splitlines()}


with tempfile.TemporaryDirectory(prefix="glmchat-http-") as temporary:
    app = Path(temporary) / "app"
    shutil.copytree(SOURCE_APP, app, ignore=shutil.ignore_patterns("CHANGES_*.txt"))
    port = free_port()
    process = subprocess.Popen(
        ["php", "-S", f"127.0.0.1:{port}", "-t", str(app), str(app / "index.php")],
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT,
        text=True,
    )
    try:
        url = f"http://127.0.0.1:{port}/api/status"
        response = None
        for _ in range(50):
            try:
                response = urllib.request.urlopen(url, timeout=2)
                break
            except urllib.error.HTTPError as error:
                response = error
                break
            except OSError:
                time.sleep(0.05)
        assert response is not None, "PHP front controller did not start."
        body = response.read().decode("utf-8")
        payload = json.loads(body)
        headers = {key.lower(): value for key, value in response.headers.items()}
        assert "no-store" in headers.get("cache-control", "").lower()
        assert headers.get("x-content-type-options", "").lower() == "nosniff"
        assert headers.get("x-frame-options", "").lower() == "deny"

        missing = sorted(REQUIRED_EXTENSIONS - php_modules())
        if missing:
            assert response.status == 503, (response.status, payload)
            error = payload.get("error", {})
            assert error.get("code") == "ENVIRONMENT_INVALID", payload
            failures = " ".join(error.get("failures", [])).lower()
            for extension in missing:
                assert extension in failures, (extension, failures)
            print(f"HTTP preflight passed: controlled 503 for missing extensions {', '.join(missing)}.")
        else:
            assert response.status == 200, (response.status, payload)
            data = payload.get("data", payload)
            assert data.get("state") in {"setup_required", "installed", "unavailable"}, payload
            assert isinstance(data.get("runtime"), dict), payload
            print(f"HTTP preflight passed: healthy status {data.get('state')}.")
    finally:
        process.terminate()
        try:
            process.wait(timeout=3)
        except subprocess.TimeoutExpired:
            process.kill()
            process.wait(timeout=3)
