"""Source contradictions for requirement alternatives (audit R14-F01).

A posting can list "Current RN or LPN license" and, elsewhere, say "LPN applicants are not eligible for this dialysis
unit". A fit that reads only the requirement list then calls an LPN holder supported. `excluded(text, labels)` finds,
for each alternative label, a sentence of the posting that rules it out:

- an explicit denial naming it: "X applicants are not eligible", "X not accepted", "X will not be considered",
  "excluding X", "X does not qualify", ...
- or an exclusive statement naming a DIFFERENT alternative: "RN only", "only RNs will be considered" rules out LPN
  when both are alternatives of the same requirement.

The alternative must be named as a whole word by its core term (its abbreviation in parentheses, or its label without
generic words like "license", "certification"), so "PN" never matches "LPN". Deterministic; no model. Shared by demo
apply fit (the retired apply fit) and, if wanted, the agent pack's fit report, so the two cannot disagree.
"""

import re

GENERIC = {"license", "licence", "licensure", "certification", "certificate", "certified", "cert", "card", "current", "valid", "active",
           "state", "credential", "registration", "degree", "required", "preferred"}
_DENY = re.compile(r"\b(?:not eligible|ineligible|not (?:be )?accepted|not acceptable|will not be considered|(?:are|is) not considered|"
                   r"cannot be considered|not be considered|do(?:es)? not qualify|not qualified to apply|(?:are|is) excluded|excluding|"
                   r"not sufficient|does not meet|do not meet|need not apply|not be hired)\b", re.I)
_ONLY = re.compile(r"\b(?:only|exclusively)\b", re.I)
_SENT = re.compile(r"[^.!?\n]+[.!?]?")


def core_terms(label) -> list:
    """The words that name an alternative: an abbreviation in parentheses first, then the label minus generic words."""
    label = str(label or "")
    out = [a.strip() for a in re.findall(r"\(([^)]*)\)", label) if a.strip()]
    rest = re.sub(r"\([^)]*\)", " ", label)
    words = [w for w in re.findall(r"[A-Za-z0-9+]+", rest) if w.lower() not in GENERIC]
    if words:
        out.append(" ".join(words))
    return [t for t in out if len(t) >= 2]


def _names(sentence, label) -> bool:
    for term in core_terms(label):
        if re.search(r"(?<![A-Za-z0-9])" + re.escape(term) + r"s?(?![A-Za-z0-9])", sentence, re.I):
            return True
    return False


def excluded(text, labels) -> dict:
    """{label: {"quote", "span", "why": "denied"|"only"}} for alternatives the posting itself rules out."""
    text = text or ""
    labels = [l for l in labels if l]
    out = {}
    for m in _SENT.finditer(text):
        s = m.group(0)
        if not s.strip():
            continue
        named = [l for l in labels if _names(s, l)]
        if not named:
            continue
        span = [m.start() + (len(s) - len(s.lstrip())), m.end()]
        quote = s.strip()
        if _DENY.search(s):
            for l in named:
                out.setdefault(l, {"quote": quote, "span": span, "why": "denied"})
        elif _ONLY.search(s) and len(labels) > 1 and len(named) < len(labels) and not re.search(r"\bor\b", s, re.I):
            for l in labels:                                # "RN only": every alternative not named is ruled out
                if l not in named:
                    out.setdefault(l, {"quote": quote, "span": span, "why": "only"})
    return out
