import socket
import struct
import logging
from .base import Finding

logger = logging.getLogger(__name__)

CHECK_NAME = 'dns_resolver'
TIMEOUT = 5


def _build_dns_query(domain: str) -> bytes:
    """Build a minimal DNS A query packet."""
    header = struct.pack('>HHHHHH', 0xAAAA, 0x0100, 1, 0, 0, 0)
    parts = domain.encode().split(b'.')
    question = b''.join(bytes([len(p)]) + p for p in parts) + b'\x00'
    question += struct.pack('>HH', 1, 1)  # type A, class IN
    return header + question


def _parse_dns_response(data: bytes) -> dict:
    """Return basic info from a DNS response header."""
    if len(data) < 12:
        return {'error': 'response too short'}
    txid, flags, qdcount, ancount, nscount, arcount = struct.unpack('>HHHHHH', data[:12])
    rcode = flags & 0x000F
    return {
        'txid': txid,
        'flags': flags,
        'rcode': rcode,
        'ancount': ancount,
    }


def run(profile) -> list:
    host = profile.public_ip
    raw = {'public_ip': host}

    query = _build_dns_query('google.com')
    try:
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.settimeout(TIMEOUT)
        sock.sendto(query, (host, 53))
        data, _ = sock.recvfrom(512)
        sock.close()

        parsed = _parse_dns_response(data)
        raw['response'] = parsed

        if parsed.get('rcode') == 0 and parsed.get('ancount', 0) > 0:
            return [Finding(
                check_name=CHECK_NAME,
                severity='critical',
                title=f'Open DNS resolver detected on {host}:53',
                detail=(
                    'Your public IP responds to recursive DNS queries from external hosts. '
                    'This can be abused for DNS amplification attacks. '
                    'Note: NAT hairpin may cause false positive — verify from off-LAN.'
                ),
                raw=raw,
            )]
        else:
            return [Finding(
                check_name=CHECK_NAME,
                severity='ok',
                title='DNS port 53 does not appear to be an open resolver',
                detail=f'DNS query to {host}:53 returned rcode={parsed.get("rcode")} with {parsed.get("ancount", 0)} answers.',
                raw=raw,
            )]
    except socket.timeout:
        return [Finding(
            check_name=CHECK_NAME,
            severity='ok',
            title='DNS port 53 timed out (not an open resolver)',
            detail=f'No response from {host}:53 within {TIMEOUT}s. Port is likely closed or filtered.',
            raw={**raw, 'error': 'timeout'},
        )]
    except Exception as e:
        logger.warning(f'DNS check error: {e}')
        return [Finding(
            check_name=CHECK_NAME,
            severity='info',
            title='DNS resolver check failed',
            detail=f'Could not complete DNS probe to {host}:53 — {e}',
            raw={**raw, 'error': str(e)},
        )]
