from __future__ import annotations

import threading
from http.server import BaseHTTPRequestHandler, HTTPServer
from types import SimpleNamespace

from acl_engine.actions import file as file_actions
from acl_engine.actions import memory as memory_actions
from acl_engine.actions import net as net_actions
from acl_engine.actions import system as system_actions
from acl_engine.actions import util as util_actions


def build_runtime(tmp_path):
    return SimpleNamespace(workspace_root=tmp_path, timeout=5, memory={})


def test_file_memory_and_util_actions(tmp_path) -> None:
    runtime = build_runtime(tmp_path)

    assert file_actions.write("sample.txt", "hello", runtime=runtime)["written"] is True
    assert file_actions.read("sample.txt", runtime=runtime) == "hello"
    assert "sample.txt" in file_actions.list_dir(".", runtime=runtime)
    assert file_actions.exists("sample.txt", runtime=runtime) is True
    stat_result = file_actions.stat("sample.txt", runtime=runtime)
    assert stat_result["exists"] is True
    assert stat_result["is_file"] is True
    assert file_actions.mkdir("nested", runtime=runtime)["created"] is True

    memory_actions.store("message", "hello", runtime=runtime)
    assert memory_actions.get_value("message", runtime=runtime) == "hello"
    assert memory_actions.list_keys(runtime=runtime) == ["message"]
    assert memory_actions.delete("message", runtime=runtime)["deleted"] is True
    memory_actions.store("message", "hello", runtime=runtime)
    assert memory_actions.clear(runtime=runtime)["cleared"] == 1
    assert util_actions.echo("value", runtime=runtime) == "value"
    assert util_actions.sleep(0, runtime=runtime)["slept"] is True
    assert isinstance(util_actions.now(runtime=runtime), str)
    assert isinstance(util_actions.uuid(runtime=runtime), str)
    digest = util_actions.hash_value("value", runtime=runtime)
    assert isinstance(digest, str)
    encoded = util_actions.base64_encode("hello", runtime=runtime)
    assert util_actions.base64_decode(encoded, runtime=runtime) == "hello"


def test_system_action_returns_process_output(tmp_path) -> None:
    runtime = build_runtime(tmp_path)
    result = system_actions.run("ls", ["."], runtime=runtime)

    assert result["code"] == 0
    assert isinstance(result["stdout"], str)
    assert isinstance(result["stderr"], str)


def test_net_request_reads_from_local_http_server(tmp_path) -> None:
    runtime = build_runtime(tmp_path)

    class Handler(BaseHTTPRequestHandler):
        def do_GET(self) -> None:  # noqa: N802
            self.send_response(200)
            self.send_header("Content-Type", "text/plain")
            self.end_headers()
            self.wfile.write(b"pong")

        def log_message(self, format, *args):  # type: ignore[override]
            return

    server = HTTPServer(("127.0.0.1", 0), Handler)
    thread = threading.Thread(target=server.serve_forever)
    thread.start()

    try:
        url = f"http://127.0.0.1:{server.server_port}/ping"
        result = net_actions.request("GET", url, runtime=runtime)
    finally:
        server.shutdown()
        thread.join()
        server.server_close()

    assert result["status"] == 200
    assert result["body"] == "pong"


def test_net_request_accepts_headers_and_body(tmp_path) -> None:
    runtime = build_runtime(tmp_path)

    class Handler(BaseHTTPRequestHandler):
        def do_POST(self) -> None:  # noqa: N802
            content_length = int(self.headers.get("Content-Length", "0"))
            body = self.rfile.read(content_length)
            self.send_response(200)
            self.send_header("Content-Type", "text/plain")
            self.end_headers()
            self.wfile.write(body)

        def log_message(self, format, *args):  # type: ignore[override]
            return

    server = HTTPServer(("127.0.0.1", 0), Handler)
    thread = threading.Thread(target=server.serve_forever)
    thread.start()

    try:
        url = f"http://127.0.0.1:{server.server_port}/echo"
        result = net_actions.request(
            "POST",
            url,
            headers={"Content-Type": "application/json"},
            body={"hello": "world"},
            runtime=runtime,
        )
    finally:
        server.shutdown()
        thread.join()
        server.server_close()

    assert result["status"] == 200
    assert "hello" in result["body"]
