# Copyright (C) 2026 NPO NT
# SPDX-License-Identifier: AGPL-3.0-only

from __future__ import annotations

import hashlib
from typing import Any

from PIL import Image

from .models import Box, Finding
from .policy import category_policy

try:
    import zxingcpp  # type: ignore[import-not-found]
except ImportError:  # pragma: no cover - exercised by capability diagnostics
    zxingcpp = None


def barcode_available() -> bool:
    return zxingcpp is not None


def _coordinate(point: Any, name: str) -> float:
    value = getattr(point, name, None)
    if value is None and isinstance(point, (tuple, list)):
        value = point[0 if name == "x" else 1]
    if callable(value):
        value = value()
    return float(value)


def _position_points(position: Any) -> list[Any]:
    names = ("top_left", "top_right", "bottom_right", "bottom_left")
    points = [getattr(position, name, None) for name in names]
    return [point for point in points if point is not None]


def detect_barcodes(image: Image.Image, page_index: int) -> list[Finding]:
    if zxingcpp is None:
        return []
    results = zxingcpp.read_barcodes(image.convert("RGB"), return_errors=True)
    findings: list[Finding] = []
    policy = category_policy("barcode")
    for result in results:
        points = _position_points(result.position)
        if not points:
            continue
        xs = [_coordinate(point, "x") for point in points]
        ys = [_coordinate(point, "y") for point in points]
        box = Box(min(xs), min(ys), max(xs), max(ys)).expand(8, image.width, image.height)
        format_name = str(getattr(result, "format", "barcode"))
        material = f"{page_index}:{box.x0:.1f}:{box.y0:.1f}:{box.x1:.1f}:{box.y1:.1f}:{format_name}"
        findings.append(
            Finding(
                finding_id=hashlib.sha256(material.encode("utf-8")).hexdigest()[:16],
                page_index=page_index,
                category="barcode",
                box=box,
                action=policy.action,
                severity=policy.severity,
                confidence=99.0 if not getattr(result, "error", None) else 80.0,
                source="barcode",
                reason=f"Обнаружен машиночитаемый код ({format_name})",
            )
        )
    return findings
