"""
High-level detect API for clawID.

Detection order (algorithm='auto'):
  1. QR-Mark  — fastest, direct JSON decode, AI-model readable
  2. DWT-QIM  — delta=32, fast, PNG/JPEG-robust
  3. Resize-robust DWT-QIM — delta=192, handles ±35% resize + JPEG
"""

import struct
from PIL import Image

from .payload import MAGIC, VERSION, HEADER_BYTES, from_bits, decode
from ..algorithms.dwt_qim import extract_bits as _qim_bits, DELTA as QIM_DELTA, REPEAT as QIM_REPEAT
from ..algorithms.fourier_mellin import extract_bits as _fm_bits, DELTA as FM_DELTA, REPEAT as FM_REPEAT
from ..algorithms.qr_mark import detect_qr as _qr_detect

_MAX_COMPRESSED_LEN = 4096


def detect(source, algorithm: str = 'auto') -> dict | None:
    """
    Detect and extract a clawID watermark from an image.

    Args:
        source:    file path (str) or PIL Image
        algorithm: 'auto'  — try QR first, then DWT-QIM, then resize-robust
                   'qr'    — QR-Mark only (AI-readable, scanner-readable)
                   'qim'   — DWT-QIM only (fast, PNG/JPEG)
                   'fm'    — Resize-robust DWT-QIM (handles ±35% resize)

    Returns:
        Metadata dict if a valid clawID watermark is found, None otherwise.
    """
    image = (
        Image.open(source).convert('RGB')
        if isinstance(source, str)
        else source.convert('RGB')
    )

    if algorithm in ('auto', 'qr'):
        result = _try_detect_qr(image)
        if result is not None:
            return result
        if algorithm == 'qr':
            return None

    if algorithm in ('auto', 'qim'):
        result = _try_detect_qim(image)
        if result is not None:
            return result

    if algorithm in ('auto', 'fm'):
        result = _try_detect_fm(image)
        if result is not None:
            return result

    return None


# ── QR detector ───────────────────────────────────────────────────────────────

def _try_detect_qr(image: Image.Image) -> dict | None:
    try:
        return _qr_detect(image)
    except Exception:
        return None


# ── QIM detector (delta=32) ───────────────────────────────────────────────────

def _try_detect_qim(image: Image.Image) -> dict | None:
    try:
        header_bits = _qim_bits(image, HEADER_BYTES * 8, QIM_DELTA, QIM_REPEAT)
    except ValueError:
        return None

    header_bytes = from_bits(header_bits)
    if header_bytes[:4] != MAGIC or header_bytes[4] != VERSION:
        return None

    compressed_len = struct.unpack('>H', header_bytes[5:7])[0]
    if compressed_len == 0 or compressed_len > _MAX_COMPRESSED_LEN:
        return None

    total_bytes = HEADER_BYTES + compressed_len + 4
    try:
        all_bits = _qim_bits(image, total_bytes * 8, QIM_DELTA, QIM_REPEAT)
    except ValueError:
        return None

    try:
        return decode(from_bits(all_bits))
    except Exception:
        return None


# ── Resize-robust detector (delta=192) ────────────────────────────────────────

def _try_detect_fm(image: Image.Image) -> dict | None:
    try:
        header_bits = _fm_bits(image, HEADER_BYTES * 8, FM_DELTA, FM_REPEAT)
    except (ValueError, Exception):
        return None

    header_bytes = from_bits(header_bits)
    if header_bytes[:4] != MAGIC or header_bytes[4] != VERSION:
        return None

    compressed_len = struct.unpack('>H', header_bytes[5:7])[0]
    if compressed_len == 0 or compressed_len > _MAX_COMPRESSED_LEN:
        return None

    total_bytes = HEADER_BYTES + compressed_len + 4
    try:
        all_bits = _fm_bits(image, total_bytes * 8, FM_DELTA, FM_REPEAT)
    except (ValueError, Exception):
        return None

    try:
        return decode(from_bits(all_bits))
    except Exception:
        return None
