"""Tests para fetch_http_fingerprint.

Levanta un http.server.HTTPServer local en 127.0.0.1 en un puerto efimero,
servido por un thread, con headers fake (Server, X-Powered-By, Set-Cookie) y
un HTML con <title>Hola</title>. NO toca red externa.
"""

import threading
from http.server import BaseHTTPRequestHandler, HTTPServer

from fetch_http_fingerprint import fetch_http_fingerprint

_HTML = b"<!doctype html><html><head><title>Hola</title></head><body>ok</body></html>"


class _FakeHandler(BaseHTTPRequestHandler):
    def do_GET(self):  # noqa: N802 (firma de BaseHTTPRequestHandler)
        self.send_response(200)
        self.send_header("Server", "TestServer/1.0")
        self.send_header("X-Powered-By", "PHP/8.1")
        self.send_header("Content-Type", "text/html; charset=utf-8")
        self.send_header("Set-Cookie", "PHPSESSID=secret_value_no_capturar; Path=/")
        self.end_headers()
        self.wfile.write(_HTML)

    def log_message(self, *args):  # silencia el logging del server en los tests
        pass


def _start_server():
    server = HTTPServer(("127.0.0.1", 0), _FakeHandler)
    thread = threading.Thread(target=server.serve_forever, daemon=True)
    thread.start()
    return server, thread


def test_status_ok_y_status_code_200():
    server, thread = _start_server()
    try:
        port = server.server_address[1]
        res = fetch_http_fingerprint(f"http://127.0.0.1:{port}/")
        assert res["status"] == "ok", res
        assert res["status_code"] == 200, res["status_code"]
    finally:
        server.shutdown()
        thread.join(timeout=2)


def test_headers_normalizados_lowercase():
    server, thread = _start_server()
    try:
        port = server.server_address[1]
        res = fetch_http_fingerprint(f"http://127.0.0.1:{port}/")
        assert res["headers"]["server"] == "TestServer/1.0", res["headers"]
        assert res["server"] == "TestServer/1.0", res["server"]
        assert res["headers"]["x-powered-by"] == "PHP/8.1", res["headers"]
    finally:
        server.shutdown()
        thread.join(timeout=2)


def test_cookies_solo_nombres_no_valores():
    server, thread = _start_server()
    try:
        port = server.server_address[1]
        res = fetch_http_fingerprint(f"http://127.0.0.1:{port}/")
        assert "PHPSESSID" in res["cookies"], res["cookies"]
        # El valor sensible NUNCA debe aparecer en la salida.
        assert "secret_value_no_capturar" not in res["raw"], "valor de cookie filtrado en raw"
        assert all("=" not in c for c in res["cookies"]), res["cookies"]
    finally:
        server.shutdown()
        thread.join(timeout=2)


def test_title_extraido():
    server, thread = _start_server()
    try:
        port = server.server_address[1]
        res = fetch_http_fingerprint(f"http://127.0.0.1:{port}/")
        assert res["title"] == "Hola", res["title"]
        assert res["html_len"] == len(_HTML), res["html_len"]
    finally:
        server.shutdown()
        thread.join(timeout=2)


def test_url_vacia_devuelve_error():
    res = fetch_http_fingerprint("")
    assert res["status"] == "error", res
    assert "url vacia" in res["error"], res["error"]


def test_host_inexistente_devuelve_error_sin_lanzar():
    # Puerto cerrado en loopback: conexion rechazada, debe devolver error, no lanzar.
    res = fetch_http_fingerprint("http://127.0.0.1:1/")
    assert res["status"] == "error", res
    assert res["url"] == "http://127.0.0.1:1/", res


if __name__ == "__main__":
    test_status_ok_y_status_code_200()
    test_headers_normalizados_lowercase()
    test_cookies_solo_nombres_no_valores()
    test_title_extraido()
    test_url_vacia_devuelve_error()
    test_host_inexistente_devuelve_error_sin_lanzar()
    print("all tests passed")
