# retoor <retoor@molodetz.nl>

from datetime import timedelta
import pytest
import requests
from tests.conftest import BASE_URL, run_async
from devplacepy.database import db, get_table, init_db, refresh_snapshot
from devplacepy import config, project_files
from devplacepy.services.containers import api, store, runtime
from devplacepy.services.containers.backend.base import Mount, PortMapping, RunSpec
from devplacepy.services.containers.backend.docker_cli import build_run_argv, parse_size
from devplacepy.services.containers.backend.fake import FakeBackend
from devplacepy.services.containers.service import ContainerService
from devplacepy.services.containers.api import INSTANCE_LABEL
from devplacepy.services.devii.tasks.schedule import Schedule, now_utc
_CONTAINER_TABLES = (
    "instances",
    "instance_events",
    "instance_metrics",
    "instance_schedules",
)
@pytest.fixture(autouse=True)
def _init_db_containers():
    init_db()
    yield
@pytest.fixture
def env(tmp_path, monkeypatch):
    fake = FakeBackend()
    runtime.set_backend(fake)
    monkeypatch.setattr("devplacepy.config.CONTAINER_WORKSPACES_DIR", tmp_path / "ws")
    pid = "ctest-p1"
    project = {"uid": pid, "slug": "ctest", "title": "C", "user_uid": "ctest-u1"}
    user = {"uid": "ctest-u1", "username": "ctestadmin"}
    project_files.write_text_file(pid, user, "app.py", "print(1)\n")
    yield {"fake": fake, "project": project, "user": user}
    runtime.set_backend(None)
    for table in _CONTAINER_TABLES:
        if table in db.tables:
            for row in [r for r in get_table(table).find()]:
                if str(row.get("project_uid", "")).startswith("ctest"):
                    get_table(table).delete(uid=row["uid"])
    for row in list(get_table("project_files").find()):
        if str(row.get("project_uid", "")).startswith("ctest"):
            get_table("project_files").delete(uid=row["uid"])
def _ready_instance(env, **kwargs):
    return run_async(
        api.create_instance(env["project"], name=kwargs.pop("name", "inst"), **kwargs)
    )
def _promote_admin(username: str) -> None:
    users = get_table("users")
    user = users.find_one(username=username)
    if user:
        users.update({"uid": user["uid"], "role": "Admin"}, ["uid"])
def _api_key(username: str) -> str:
    refresh_snapshot()
    return get_table("users").find_one(username=username)["api_key"]


def test_http_ingress_proxy(app_server):
    import http.server
    import socket
    import socketserver
    import threading

    sock = socket.socket()
    sock.bind(("127.0.0.1", 0))
    port = sock.getsockname()[1]
    sock.close()

    class Handler(http.server.BaseHTTPRequestHandler):
        def do_GET(self):
            self.send_response(200)
            self.send_header("Content-Type", "text/plain")
            self.end_headers()
            self.wfile.write(b"hello from upstream " + self.path.encode())

        def log_message(self, *args):
            pass

    httpd = socketserver.TCPServer(("127.0.0.1", port), Handler)
    thread = threading.Thread(target=httpd.serve_forever, daemon=True)
    thread.start()
    slug = f"ing{port}"
    uid = f"ingtest-{port}"
    get_table("instances").insert(
        {
            "deleted_at": None,
            "deleted_by": None,
            "uid": uid,
            "name": "ingress",
            "project_uid": "ingtest",
            "status": "running",
            "ingress_slug": slug,
            "ingress_port": 8000,
            "ports_json": f'[{{"host": {port}, "container": 8000, "proto": "tcp"}}]',
            "deleted_at": None,
            "deleted_by": None,
        }
    )
    refresh_snapshot()
    try:
        r = requests.get(f"{BASE_URL}/p/{slug}/foo")
        assert r.status_code == 200, r.text
        assert "hello from upstream" in r.text and "/foo" in r.text
        assert requests.get(f"{BASE_URL}/p/does-not-exist-slug").status_code == 404
    finally:
        httpd.shutdown()
        get_table("instances").delete(uid=uid)
        refresh_snapshot()


def test_http_ingress_proxy_injects_a_base_into_the_root_document_only(app_server):
    import http.server
    import socket
    import socketserver
    import threading

    sock = socket.socket()
    sock.bind(("127.0.0.1", 0))
    port = sock.getsockname()[1]
    sock.close()

    class Handler(http.server.BaseHTTPRequestHandler):
        def do_GET(self):
            self.send_response(200)
            self.send_header("Content-Type", "text/html; charset=utf-8")
            self.end_headers()
            self.wfile.write(
                b"<!doctype html><html><head><title>t</title></head>"
                b"<body><script src='app.js'></script></body></html>"
            )

        def log_message(self, *args):
            pass

    httpd = socketserver.TCPServer(("127.0.0.1", port), Handler)
    thread = threading.Thread(target=httpd.serve_forever, daemon=True)
    thread.start()
    slug = f"base{port}"
    uid = f"basetest-{port}"
    get_table("instances").insert(
        {
            "uid": uid,
            "name": "ingress-base",
            "project_uid": "ingtest",
            "status": "running",
            "ingress_slug": slug,
            "ingress_port": 8000,
            "ports_json": f'[{{"host": {port}, "container": 8000, "proto": "tcp"}}]',
            "deleted_at": None,
            "deleted_by": None,
        }
    )
    refresh_snapshot()
    try:
        root = requests.get(f"{BASE_URL}/p/{slug}")
        assert root.status_code == 200, root.text
        assert f'<base href="/p/{slug}/">' in root.text
        nested = requests.get(f"{BASE_URL}/p/{slug}/static/webview/pre/index.html")
        assert nested.status_code == 200, nested.text
        assert "<base" not in nested.text
    finally:
        httpd.shutdown()
        get_table("instances").delete(uid=uid)
        refresh_snapshot()
