import logging
import socket
import struct

from app.core.config import Settings
from app.core.exceptions import ValidationError

logger = logging.getLogger(__name__)


def scan_file_bytes(data: bytes, settings: Settings) -> None:
    """Scan uploaded bytes with ClamAV (clamd INSTREAM). No-op when disabled."""
    if not settings.virus_scan_enabled:
        return

    try:
        _clamd_instream_scan(data, settings.clamd_host, settings.clamd_port)
    except ValidationError:
        raise
    except Exception as exc:
        if settings.virus_scan_required:
            raise ValidationError("Virus scan unavailable — upload rejected") from exc
        logger.warning("Virus scan skipped (clamd unavailable): %s", exc)


def _clamd_instream_scan(data: bytes, host: str, port: int) -> None:
    with socket.create_connection((host, port), timeout=10) as sock:
        sock.sendall(b"zINSTREAM\0")
        chunk_size = 8192
        for offset in range(0, len(data), chunk_size):
            chunk = data[offset : offset + chunk_size]
            sock.sendall(struct.pack("!I", len(chunk)) + chunk)
        sock.sendall(struct.pack("!I", 0))

        response = b""
        while True:
            part = sock.recv(4096)
            if not part:
                break
            response += part

    text = response.decode("utf-8", errors="ignore").strip()
    if "FOUND" in text:
        threat = text.split("FOUND")[0].split(":")[-1].strip() if ":" in text else "malware"
        raise ValidationError(f"Upload rejected — threat detected: {threat}")
    if "OK" not in text:
        raise ValidationError("Virus scan failed — upload rejected")
    logger.debug("ClamAV scan passed: %s", text)
