"""Portuguese NISS (Número de Identificação de Segurança Social) detector.

NISS format: 11 digits, first digit encodes the category:
  1/2 = employed worker
  3   = independent worker
  4   = voluntary contributor
  5   = household worker
  6   = welfare beneficiary
  7/8 = foreign resident (special regimes)
  9   = not commonly used

Checksum algorithm (modulo 10):
  Multiply each of the first 10 digits by the weight [29,23,19,17,13,11,7,5,3,2],
  sum the products, compute sum mod 10.  If the remainder is 0, check digit is 0;
  otherwise check digit = 10 - remainder.

We also detect the shorter context-pattern "NISS: XXXXXXXXXXX" to reduce false
positives for unlabelled 11-digit sequences.
"""
import re
from typing import Dict, List, Union

from upf.config import UPFConfig

# Raw 11-digit pattern — digits may be separated by spaces or hyphens
# (e.g. 123 456 789 01 or 12345678901)
_NISS_RAW_RE = re.compile(
    r"(?<!\d)(\d{11}|\d{3}[- ]\d{3}[- ]\d{3}[- ]\d{2})(?!\d)"
)

# Context keyword triggers for NISS
_NISS_CONTEXT_RE = re.compile(
    r"(?i)\b(?:niss|segurança\s+social|segu?ran[cç]a\s+social|"
    r"n[uú]mero\s+de\s+(?:seguran[cç]a|identificação\s+de\s+seguran[cç]a)|"
    r"social\s+security\s+(?:number|no\.?))\s*[=:\-]?\s*$"
)

_WEIGHTS = [29, 23, 19, 17, 13, 11, 7, 5, 3, 2]
_VALID_FIRST_DIGITS = set("12345678")


def _digits_only(s: str) -> str:
    return s.replace(" ", "").replace("-", "")


def _validate_niss(raw: str) -> bool:
    """Return True if *raw* (11 digits, separators stripped) passes NISS checksum."""
    digits = _digits_only(raw)
    if len(digits) != 11:
        return False
    if digits[0] not in _VALID_FIRST_DIGITS:
        return False
    try:
        nums = [int(c) for c in digits]
    except ValueError:
        return False
    total = sum(w * d for w, d in zip(_WEIGHTS, nums[:10]))
    remainder = total % 10
    expected_check = 0 if remainder == 0 else (10 - remainder)
    return nums[10] == expected_check


def detect_niss_entities(text: str, config: UPFConfig) -> List[Dict[str, Union[int, str]]]:
    """Detect Portuguese NISS numbers with deterministic checksum validation."""
    if not getattr(config, "redact_national_ids", True):
        return []

    findings: List[Dict[str, Union[int, str]]] = []
    seen: set = set()

    for m in _NISS_RAW_RE.finditer(text):
        raw = m.group(1)
        start, end = m.start(1), m.end(1)

        if (start, end) in seen:
            continue

        # Determine whether to require checksum validation or just context
        preceding = text[max(0, start - 80): start]
        has_context = bool(_NISS_CONTEXT_RE.search(preceding))

        if has_context:
            # Context-triggered: accept even if checksum doesn't pass
            # (some test data / partial numbers appear in labelled fields)
            seen.add((start, end))
            findings.append({"type": "PT_NISS", "start": start, "end": end, "text": raw})
        elif _validate_niss(raw):
            # Unlabelled: only accept when checksum passes
            seen.add((start, end))
            findings.append({"type": "PT_NISS", "start": start, "end": end, "text": raw})

    findings.sort(key=lambda f: (int(f["start"]), int(f["end"])))
    return findings
