"""Document duplicate detection — file hash + optional metadata matching."""

from __future__ import annotations

import hashlib
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from app.store import Invoice

DUPLICATE_NONE = "None"
DUPLICATE_POSSIBLE = "Possible duplicate"
DUPLICATE_CONFIRMED = "Confirmed duplicate"
DUPLICATE_CLEARED = "Not duplicate"

ALL_DUPLICATE_STATUSES = frozenset(
    {DUPLICATE_NONE, DUPLICATE_POSSIBLE, DUPLICATE_CONFIRMED, DUPLICATE_CLEARED}
)


class DuplicateUploadError(Exception):
    """Raised when an exact file duplicate is uploaded without force flag."""

    def __init__(self, existing: Invoice) -> None:
        self.existing = existing
        super().__init__("Duplicate document detected")


def sha256_hex(data: bytes) -> str:
    return hashlib.sha256(data).hexdigest()


def find_exact_file_duplicate(
    invoices: list[Invoice],
    *,
    client_id: str,
    file_hash: str,
    exclude_id: str | None = None,
) -> Invoice | None:
    """Return an existing invoice with the same hash for this client."""
    if not file_hash:
        return None
    for inv in invoices:
        if inv.id == exclude_id:
            continue
        if inv.clientId != client_id:
            continue
        existing_hash = getattr(inv, "fileHash", None) or ""
        if existing_hash and existing_hash == file_hash:
            return inv
    return None


def find_metadata_duplicate(
    invoices: list[Invoice],
    *,
    client_id: str,
    supplier: str,
    number: str,
    invoice_date: str,
    total: float,
    exclude_id: str | None = None,
) -> Invoice | None:
    """Detect duplicate by metadata when all key fields are present."""
    supplier_n = (supplier or "").strip().lower()
    number_n = (number or "").strip().lower()
    date_n = (invoice_date or "").strip()
    if not supplier_n or not number_n or not date_n or total <= 0:
        return None

    for inv in invoices:
        if inv.id == exclude_id:
            continue
        if inv.clientId != client_id:
            continue
        if (inv.supplier or "").strip().lower() != supplier_n:
            continue
        if (inv.number or "").strip().lower() != number_n:
            continue
        if (inv.invoiceDate or "").strip() != date_n:
            continue
        if abs(float(inv.total or 0) - total) > 0.01:
            continue
        return inv
    return None
