"""Format API responses into AI-friendly text.

Output is plain text optimized for LLM consumption — structured,
concise, with clear section headers. No Rich markup, no ANSI codes.
"""

from __future__ import annotations

from typing import Any


def _truncate(text: str, limit: int) -> str:
    """Truncate text at a sentence or word boundary."""
    if len(text) <= limit:
        return text
    for end in (". ", ".\n", "! ", "? "):
        idx = text.rfind(end, 0, limit)
        if idx > limit * 0.5:
            return text[: idx + 1] + " ..."
    idx = text.rfind(" ", 0, limit)
    if idx > limit * 0.5:
        return text[:idx] + " ..."
    return text[:limit] + "..."


def _iso_short(ts: str | None) -> str:
    """Shorten an ISO timestamp to YYYY-MM-DD HH:MM."""
    if not ts:
        return "?"
    return ts[:16].replace("T", " ")


# ---------------------------------------------------------------------------
# Service search results
# ---------------------------------------------------------------------------

def format_services(data: dict) -> str:
    total = data.get("total", 0)
    is_exact = data.get("total_is_exact", True)
    results = data.get("results", [])
    query = data.get("query", "")

    if total == 0:
        return "No services found."

    approx = "" if is_exact else "~"
    lines = [f"Found {approx}{total:,} services for query: {query or '(all)'}\n"]

    for s in results:
        ip = s.get("ip", "?")
        port = s.get("port", "?")
        svc = s.get("service_name") or ""
        product = s.get("product") or ""
        version = s.get("version") or ""
        banner = s.get("banner_preview") or ""
        title = s.get("http_title") or ""
        cc = s.get("country_code") or ""
        asn_name = s.get("as_name") or ""
        rdns = s.get("reverse_dns") or ""
        techs = s.get("technologies") or []
        sid = s.get("service_id", "?")
        seen = _iso_short(s.get("last_seen"))
        screenshot = " [screenshot]" if s.get("has_screenshot") else ""

        header = f"  {ip}:{port}/{s.get('transport', 'tcp')}"
        if svc:
            header += f"  {svc}"
        if product:
            header += f"  ({product}"
            if version:
                header += f" {version}"
            header += ")"
        header += screenshot
        lines.append(header)

        details = []
        if title:
            details.append(f"Title: {title}")
        if banner and not title:
            details.append(f"Banner: {_truncate(banner, 120)}")
        if cc or asn_name:
            geo = " ".join(filter(None, [cc, asn_name]))
            details.append(f"Geo: {geo}")
        if rdns:
            details.append(f"rDNS: {rdns}")
        if techs:
            details.append(f"Tech: {', '.join(techs[:8])}")
        details.append(f"Seen: {seen}  [service_id={sid}]")

        for d in details:
            lines.append(f"    {d}")
        lines.append("")

    return "\n".join(lines)


# ---------------------------------------------------------------------------
# Host search results
# ---------------------------------------------------------------------------

def format_hosts(data: dict) -> str:
    total = data.get("total", 0)
    is_exact = data.get("total_is_exact", True)
    results = data.get("results", [])

    if total == 0:
        return "No hosts found."

    approx = "" if is_exact else "~"
    lines = [f"Found {approx}{total:,} hosts\n"]

    for h in results:
        ip = h.get("ip", "?")
        ports = h.get("ports") or []
        services = h.get("services") or []
        cc = h.get("country_code") or ""
        asn_name = h.get("as_name") or ""
        rdns = h.get("reverse_dns") or ""
        seen = _iso_short(h.get("last_seen"))
        port_str = ", ".join(str(p) for p in ports[:15])
        if len(ports) > 15:
            port_str += f" (+{len(ports) - 15} more)"

        lines.append(f"  {ip}  [{h.get('service_count', 0)} services]")
        lines.append(f"    Ports: {port_str}")
        if services:
            lines.append(f"    Services: {', '.join(str(s) for s in services[:10])}")
        if cc or asn_name:
            lines.append(f"    Geo: {' '.join(filter(None, [cc, asn_name]))}")
        if rdns:
            lines.append(f"    rDNS: {rdns}")
        lines.append(f"    Last seen: {seen}")
        lines.append("")

    return "\n".join(lines)


# ---------------------------------------------------------------------------
# Service detail
# ---------------------------------------------------------------------------

def format_service_detail(data: dict) -> str:
    ip = data.get("ip", "?")
    port = data.get("port", "?")
    transport = data.get("transport", "tcp")
    svc = data.get("service_name") or "unknown"
    product = data.get("product") or ""
    version = data.get("version") or ""
    sid = data.get("service_id", "?")

    lines = [
        f"{'=' * 60}",
        f"{ip}:{port}/{transport}  {svc}  [service_id={sid}]",
        f"{'=' * 60}",
    ]

    if product:
        prod_line = f"Product: {product}"
        if version:
            prod_line += f" {version}"
        lines.append(prod_line)

    # Timestamps
    lines.append(f"First seen: {_iso_short(data.get('first_seen'))}  |  "
                 f"Last seen: {_iso_short(data.get('last_seen'))}")
    lines.append(f"Scan: #{data.get('scan_id', '?')}  |  "
                 f"Current: {'yes' if data.get('is_current') else 'no'}")
    lines.append("")

    # GeoIP
    cc = data.get("country_code") or ""
    asn = data.get("asn")
    as_name = data.get("as_name") or ""
    rdns = data.get("reverse_dns") or ""
    if cc or asn or rdns:
        lines.append("GEO/NETWORK:")
        if cc:
            lines.append(f"  Country: {cc}")
        if asn:
            lines.append(f"  ASN: AS{asn} ({as_name})")
        if rdns:
            lines.append(f"  rDNS: {rdns}")
        lines.append("")

    # Banner
    banner = data.get("banner") or ""
    if banner:
        lines.append("BANNER:")
        lines.append(f"  {_truncate(banner, 500)}")
        lines.append("")

    # HTTP
    http_status = data.get("http_status_code")
    if http_status:
        lines.append("HTTP:")
        lines.append(f"  Status: {http_status}")
        if data.get("http_title"):
            lines.append(f"  Title: {data['http_title']}")
        if data.get("http_server"):
            lines.append(f"  Server: {data['http_server']}")
        http_json = data.get("http_json")
        if isinstance(http_json, dict):
            if http_json.get("redirect_url"):
                lines.append(f"  Redirect: {http_json['redirect_url']}")
            if http_json.get("body_snippet"):
                lines.append(f"  Body: {_truncate(http_json['body_snippet'], 200)}")
        lines.append("")

    # TLS
    tls_cn = data.get("tls_subject_cn") or ""
    tls_json = data.get("tls_json")
    if tls_cn or tls_json:
        lines.append("TLS:")
        if tls_cn:
            lines.append(f"  Subject CN: {tls_cn}")
        if data.get("tls_issuer_org"):
            lines.append(f"  Issuer: {data['tls_issuer_org']}")
        if data.get("tls_not_after"):
            lines.append(f"  Expires: {_iso_short(data['tls_not_after'])}")
        if data.get("tls_jarm"):
            lines.append(f"  JARM: {data['tls_jarm']}")
        if isinstance(tls_json, dict):
            san = tls_json.get("san_dns")
            if san:
                san_list = san if isinstance(san, list) else [san]
                lines.append(f"  SANs: {', '.join(san_list[:10])}")
        lines.append("")

    # Technologies
    techs = data.get("technologies") or []
    if techs:
        lines.append(f"TECHNOLOGIES: {', '.join(techs)}")
        lines.append("")

    # Enumeration
    enum = data.get("enum_json")
    if isinstance(enum, dict) and enum:
        lines.append("ENUMERATION:")
        for k, v in enum.items():
            if k.startswith("_"):
                continue
            lines.append(f"  {k}: {v}")
        lines.append("")

    # SNMP
    snmp = data.get("snmp_json")
    if isinstance(snmp, dict) and snmp:
        lines.append("SNMP:")
        for k, v in snmp.items():
            lines.append(f"  {k}: {v}")
        lines.append("")

    # Screenshots
    screenshots = data.get("screenshots") or []
    if screenshots:
        lines.append(f"SCREENSHOTS ({len(screenshots)}):")
        for sc in screenshots:
            sc_type = sc.get("screenshot_type") or "unknown"
            sc_title = sc.get("final_title") or ""
            sc_url = sc.get("final_url") or ""
            lines.append(f"  [{sc_type}] {sc_title}")
            if sc_url:
                lines.append(f"    URL: {sc_url}")
        lines.append("")

    # Nuclei findings
    findings = data.get("findings") or []
    if findings:
        lines.append(f"SECURITY FINDINGS ({len(findings)}):")
        for f in findings:
            sev = (f.get("severity") or "?").upper()
            tid = f.get("template_id") or "?"
            name = f.get("template_name") or ""
            desc = f.get("description") or ""
            lines.append(f"  [{sev}] {tid}")
            if name:
                lines.append(f"    {name}")
            if desc:
                lines.append(f"    {_truncate(desc, 150)}")
            if f.get("matched_url"):
                lines.append(f"    Matched: {f['matched_url']}")
            if f.get("tags"):
                lines.append(f"    Tags: {f['tags']}")
        lines.append("")

    return "\n".join(lines)


# ---------------------------------------------------------------------------
# Host detail
# ---------------------------------------------------------------------------

def format_host_detail(data: dict) -> str:
    ip = data.get("ip", "?")
    services = data.get("services", [])
    host = data.get("host") or {}
    count = data.get("service_count", len(services))

    lines = [
        f"{'=' * 60}",
        f"Host: {ip}  [{count} services]",
        f"{'=' * 60}",
    ]

    if host:
        if host.get("country_code"):
            lines.append(f"Country: {host['country_code']}")
        if host.get("asn"):
            lines.append(f"ASN: AS{host['asn']} ({host.get('as_name', '')})")
        if host.get("reverse_dns"):
            lines.append(f"rDNS: {host['reverse_dns']}")
        if host.get("open_ports"):
            lines.append(f"Open ports: {host['open_ports']}")
        lines.append(f"First seen: {_iso_short(host.get('first_seen'))}  |  "
                     f"Last seen: {_iso_short(host.get('last_seen'))}")

        snmp = host.get("snmp_json")
        if isinstance(snmp, dict) and snmp:
            lines.append("")
            lines.append("SNMP:")
            for k, v in snmp.items():
                lines.append(f"  {k}: {v}")
    lines.append("")

    lines.append("SERVICES:")
    for s in services:
        port = s.get("port", "?")
        svc = s.get("service_name") or ""
        product = s.get("product") or ""
        title = s.get("http_title") or ""
        banner = s.get("banner_preview") or ""
        sid = s.get("service_id", "?")
        screenshot = " [screenshot]" if s.get("has_screenshot") else ""

        header = f"  :{port}/{s.get('transport', 'tcp')}"
        if svc:
            header += f"  {svc}"
        if product:
            header += f"  ({product})"
        header += f"  [service_id={sid}]{screenshot}"
        lines.append(header)

        if title:
            lines.append(f"    Title: {title}")
        elif banner:
            lines.append(f"    Banner: {_truncate(banner, 120)}")

        enum = s.get("enum_json")
        if isinstance(enum, dict) and enum:
            enum_parts = []
            for k, v in enum.items():
                if k.startswith("_"):
                    continue
                enum_parts.append(f"{k}={v}")
            if enum_parts:
                lines.append(f"    Enum: {', '.join(enum_parts[:5])}")

    return "\n".join(lines)


# ---------------------------------------------------------------------------
# Facets
# ---------------------------------------------------------------------------

def format_facets(data: dict) -> str:
    query = data.get("query", "")
    facets = data.get("facets", {})

    if not facets:
        return "No facet data available."

    lines = [f"Facets for query: {query or '(all)'}\n"]

    for facet_name, values in facets.items():
        lines.append(f"  {facet_name.upper()}:")
        if isinstance(values, list):
            for item in values[:20]:
                if isinstance(item, (list, tuple)) and len(item) == 2:
                    lines.append(f"    {item[0]:30}  {item[1]:>6}")
                elif isinstance(item, dict):
                    lines.append(f"    {item}")
        lines.append("")

    return "\n".join(lines)


# ---------------------------------------------------------------------------
# Dashboard
# ---------------------------------------------------------------------------

def format_dashboard(data: dict) -> str:
    stats = data.get("stats", {})
    facets = data.get("facets", {})

    lines = ["NetScope Dashboard\n"]

    if stats:
        lines.append("STATISTICS:")
        for k, v in stats.items():
            if isinstance(v, (int, float)):
                lines.append(f"  {k}: {v:,}")
            else:
                lines.append(f"  {k}: {v}")
        lines.append("")

    if facets:
        lines.append("TOP FACETS:")
        for facet_name, values in facets.items():
            lines.append(f"  {facet_name.upper()}:")
            if isinstance(values, list):
                for item in values[:10]:
                    if isinstance(item, (list, tuple)) and len(item) == 2:
                        lines.append(f"    {item[0]:30}  {item[1]:>6}")
            lines.append("")

    return "\n".join(lines)


# ---------------------------------------------------------------------------
# Findings
# ---------------------------------------------------------------------------

def format_findings(data: dict) -> str:
    query = data.get("query", "")
    findings = data.get("findings", [])

    if not findings:
        return "No security findings found."

    lines = [f"Security Findings{' for: ' + query if query else ''}\n"]

    for f in findings:
        sev = (f.get("severity") or "?").upper()
        tid = f.get("template_id") or "?"
        name = f.get("template_name") or ""
        hosts = f.get("affected_hosts", 0)
        sample_ip = f.get("sample_ip") or ""
        sample_port = f.get("sample_port") or ""

        lines.append(f"  [{sev:8}] {tid}")
        if name:
            lines.append(f"             {name}")
        lines.append(f"             Affected hosts: {hosts}")
        if sample_ip:
            lines.append(f"             Example: {sample_ip}:{sample_port}")
        lines.append("")

    return "\n".join(lines)


# ---------------------------------------------------------------------------
# Web apps
# ---------------------------------------------------------------------------

def format_webapps(data: dict) -> str:
    query = data.get("query", "")
    webapps = data.get("webapps", [])

    if not webapps:
        return "No web applications found."

    lines = [f"Web Applications{' matching: ' + query if query else ''}\n"]

    for w in webapps:
        title = w.get("title") or "?"
        hosts = w.get("hosts", 0)
        lines.append(f"  {hosts:>5} hosts  {title}")

    return "\n".join(lines)


# ---------------------------------------------------------------------------
# Ports
# ---------------------------------------------------------------------------

def format_ports(data: dict) -> str:
    ports = data.get("ports", [])

    if not ports:
        return "No port data available."

    lines = ["Top Open Ports\n"]

    for p in ports:
        port = p.get("port", "?")
        service = p.get("service") or ""
        hosts = p.get("hosts", 0)
        lines.append(f"  {port:>5}  {service:15}  {hosts:>6} hosts")

    return "\n".join(lines)


# ---------------------------------------------------------------------------
# Explore
# ---------------------------------------------------------------------------

def format_explore(data: dict) -> str:
    title = data.get("title", "Explore")
    items = data.get("items", [])
    total = data.get("total", len(items))
    search_key = data.get("search_key", "")

    if not items:
        return f"No data for {title}."

    lines = [f"{title} ({total:,} entries)\n"]

    for item in items[:100]:
        name = item.get("name") or item.get("template_id") or "?"
        hosts = item.get("hosts", "")
        sev = item.get("severity") or ""

        parts = []
        if hosts:
            parts.append(f"{hosts:>6} hosts")
        if sev:
            parts.append(f"[{sev}]")
        parts.append(str(name))

        lines.append(f"  {'  '.join(parts)}")

    if total > 100:
        lines.append(f"\n  ... and {total - 100} more")

    if search_key:
        lines.append(f"\nUse search_services(query='{search_key}:<value>') to drill into any item.")

    return "\n".join(lines)


# ---------------------------------------------------------------------------
# Drones
# ---------------------------------------------------------------------------

def format_drones(data: dict) -> str:
    drones = data.get("drones", [])

    if not drones:
        return "No scanning drones registered."

    lines = [f"Scanning Fleet ({len(drones)} drones)\n"]

    for d in drones:
        did = d.get("id") or d.get("name") or "?"
        status = d.get("status") or "?"
        ip = d.get("ip_address") or "?"
        completed = d.get("scans_completed", 0)
        seen = _iso_short(d.get("last_seen"))
        target = d.get("current_target") or ""
        scan_status = d.get("scan_status") or ""

        status_icon = {"idle": "IDLE", "scanning": "SCANNING", "offline": "OFFLINE"}.get(
            status, status.upper()
        )

        lines.append(f"  {did}  [{status_icon}]  {ip}")
        lines.append(f"    Scans completed: {completed}  |  Last seen: {seen}")

        caps = d.get("capabilities") or {}
        if caps:
            active = [k for k, v in caps.items() if v]
            if active:
                lines.append(f"    Capabilities: {', '.join(active)}")

        if target:
            lines.append(f"    Current: {target} ({scan_status})")
        lines.append("")

    return "\n".join(lines)


# ---------------------------------------------------------------------------
# Templates
# ---------------------------------------------------------------------------

def format_templates(data: dict) -> str:
    total = data.get("total", 0)
    templates = data.get("templates", [])
    query = data.get("query", "")
    severity = data.get("severity", "")

    if not templates:
        return "No templates found."

    filters = []
    if query:
        filters.append(f"query='{query}'")
    if severity:
        filters.append(f"severity={severity}")
    filter_str = f" ({', '.join(filters)})" if filters else ""

    lines = [f"Nuclei Templates: {total:,} total{filter_str}\n"]

    for t in templates:
        tid = t.get("template_id") or "?"
        name = t.get("name") or ""
        sev = (t.get("severity") or "?").upper()
        author = t.get("author") or ""
        cve = t.get("cve_id") or ""
        verified = " [verified]" if t.get("verified") else ""
        category = t.get("category") or ""
        desc = t.get("description") or ""

        lines.append(f"  [{sev:8}] {tid}{verified}")
        if name:
            lines.append(f"             {name}")
        meta = []
        if author:
            meta.append(f"by {author}")
        if cve:
            meta.append(cve)
        if category:
            meta.append(category)
        if t.get("cvss_score"):
            meta.append(f"CVSS:{t['cvss_score']}")
        if meta:
            lines.append(f"             {' | '.join(meta)}")
        if desc:
            lines.append(f"             {_truncate(desc, 150)}")
        tags = t.get("tags") or []
        if tags:
            lines.append(f"             Tags: {', '.join(tags[:10])}")
        lines.append("")

    return "\n".join(lines)


# ---------------------------------------------------------------------------
# Gallery
# ---------------------------------------------------------------------------

def format_gallery(data: dict) -> str:
    total = data.get("total", 0)
    results = data.get("results", [])
    query = data.get("query", "")

    if not results:
        return "No screenshots found."

    lines = [f"Screenshot Gallery: {total:,} services with screenshots"
             f"{' matching: ' + query if query else ''}\n"]

    for s in results:
        ip = s.get("ip", "?")
        port = s.get("port", "?")
        title = s.get("http_title") or ""
        sid = s.get("service_id", "?")
        screenshots = s.get("screenshots") or []

        lines.append(f"  {ip}:{port}  [service_id={sid}]")
        if title:
            lines.append(f"    Title: {title}")
        for sc in screenshots:
            sc_type = sc.get("screenshot_type") or "unknown"
            sc_title = sc.get("final_title") or ""
            lines.append(f"    [{sc_type}] {sc_title}")
        lines.append("")

    return "\n".join(lines)
