"""
A simple tool that checks whether a local port is running an HTTP service.

This module combines two smaller probes: a TCP probe (to see if the port is open)
and a banner probe (to read whatever text the service sends back). If the banner
looks like an HTTP response, we parse out the HTTP version, status code, headers,
and body. If not, we explain why—maybe the port wasn’t open, the banner timed out,
or the text didn’t match HTTP at all.

Everything is restricted to local addresses for safety, so the agent never scans
remote machines. The final result is returned as an HttpProbeResult dataclass,
which includes both a simple summary (“SUCCESS” or “FAILURE”) and the raw probe
details. This makes it easy for the rest of the agent to understand what’s
running on a port without needing to know the low‑level networking steps.
"""


from dataclasses import dataclass
from typing import Optional, Dict, Any

from .utility import resolve_local_address
from .banner_probe import banner_probe
from .tcp_probe import tcp_probe


# -----------------------------
# Summary-oriented return type
# -----------------------------
@dataclass
class HttpProbeResult:
    # Pure summary fields
    status: str            # SUCCESS | FAILURE
    reason: str            # NON_LOCAL | TCP_FAILED | READ_TIMEOUT | BANNER_FAILED | NOT_HTTP | OK
    success: bool          # True if HTTP detected and parsed
    is_http: bool          # True if banner looks like HTTP
    http_version: Optional[str]
    status_code: Optional[int]
    headers: Dict[str, str]
    body_text: Optional[str]
    confidence: float
    signature: Optional[str]

    # Embedded raw probe results
    tcp: Any
    banner: Any


# -----------------------------
# Pure HTTP parser (no network)
# -----------------------------
def _parse_http(raw_bytes: bytes, decoded_text: Optional[str]):
    """
    Returns a tuple:
        (is_http, version, status_code, reason, headers, body_text, signature)
    """

    if not raw_bytes and not decoded_text:
        return (False, None, None, None, {}, None, None)

    text = decoded_text or raw_bytes.decode("utf-8", errors="replace")

    if not (text.startswith("HTTP/1.1") or text.startswith("HTTP/1.0")):
        return (False, None, None, None, {}, text, None)

    parts = text.split("\r\n\r\n", 1)
    header_block = parts[0]
    body_text = parts[1] if len(parts) > 1 else ""

    lines = header_block.split("\r\n")
    status_line = lines[0]

    try:
        version, status_code_str, reason = status_line.split(" ", 2)
        status_code = int(status_code_str)
    except Exception:
        version = None
        status_code = None
        reason = None

    headers = {}
    for line in lines[1:]:
        if ":" in line:
            k, v = line.split(":", 1)
            headers[k.strip()] = v.strip()

    return (True, version, status_code, reason, headers, body_text, status_line)


# -----------------------------
# Meta-probe implementation
# -----------------------------
def http_probe(local_address_mode: str, port: int) -> HttpProbeResult:
    """
    HTTP meta-probe.
    Top-level fields are pure summary.
    Full tcp_probe() and banner_probe() results are embedded.
    """

    # --- Local-only guardrail ---
    local_address = resolve_local_address(local_address_mode)
    if not local_address:
        return HttpProbeResult(
            status="FAILURE",
            reason="NON_LOCAL",
            success=False,
            is_http=False,
            http_version=None,
            status_code=None,
            headers={},
            body_text=None,
            confidence=0.0,
            signature=None,
            tcp=None,
            banner=None,
        )

    # --- TCP probe ---
    tcp = tcp_probe(local_address_mode, port)

    if tcp.classification != "OPEN_TCP":
        return HttpProbeResult(
            status="FAILURE",
            reason="TCP_FAILED",
            success=False,
            is_http=False,
            http_version=None,
            status_code=None,
            headers={},
            body_text=None,
            confidence=0.0,
            signature=None,
            tcp=tcp,
            banner=None,
        )

    # --- Banner probe ---
    banner = banner_probe(local_address_mode, port)

    # Distinguish read timeout from other banner failures
    if banner.error:
        if "timed out" in (banner.error or "").lower():
            reason = "READ_TIMEOUT"
        else:
            reason = "BANNER_FAILED"

        return HttpProbeResult(
            status="FAILURE",
            reason=reason,
            success=False,
            is_http=False,
            http_version=None,
            status_code=None,
            headers={},
            body_text=None,
            confidence=0.0,
            signature=None,
            tcp=tcp,
            banner=banner,
        )

    # --- Parse HTTP ---
    (
        is_http,
        version,
        status_code,
        reason_phrase,
        headers,
        body_text,
        signature,
    ) = _parse_http(banner.raw_bytes, banner.decoded_text)

    if not is_http:
        return HttpProbeResult(
            status="FAILURE",
            reason="NOT_HTTP",
            success=False,
            is_http=False,
            http_version=None,
            status_code=None,
            headers={},
            body_text=body_text,
            confidence=0.0,
            signature=None,
            tcp=tcp,
            banner=banner,
        )

    # --- Success ---
    return HttpProbeResult(
        status="SUCCESS",
        reason="OK",
        success=True,
        is_http=True,
        http_version=version,
        status_code=status_code,
        headers=headers,
        body_text=body_text,
        confidence=0.9,
        signature=signature,
        tcp=tcp,
        banner=banner,
    )


# -----------------------------
# Manual test block
# -----------------------------
if __name__ == "__main__":
    print("Running http_probe(ipv4_loopback, 9999)...")

    result = http_probe("ipv4_loopback", 9999)

    print("\n=== HTTP Probe Summary ===")
    print(f"Status        : {result.status}")
    print(f"Reason        : {result.reason}")
    print(f"Success       : {result.success}")
    print(f"Is HTTP       : {result.is_http}")
    print(f"Version       : {result.http_version}")
    print(f"Status Code   : {result.status_code}")
    print(f"Headers       : {result.headers}")
    print(f"Body Text     : {result.body_text}")
    print(f"Confidence    : {result.confidence}")
    print(f"Signature     : {result.signature}")

    print("\n--- TCP Probe ---")
    print(result.tcp)

    print("\n--- Banner Probe ---")
    print(result.banner)
