"""Physical identifier normalization and existing catalog policy interpretation."""
import re
import unicodedata

from django.core.exceptions import ValidationError

from apps.catalog.identification import get_identification_policy


TYPES = ("IMEI1", "IMEI2", "SERIAL")


def identity_key(value):
    """One exact lookup namespace, including numeric serial/IMEI ambiguity."""
    if not isinstance(value, str):
        raise ValidationError("An identifier must be text.")
    value = value.strip().upper()
    if not value or len(value) > 128 or any(unicodedata.category(c).startswith("C") for c in value):
        raise ValidationError("Supply a nonblank identifier of at most 128 printable characters.")
    if re.fullmatch(r"[0-9 -]+", value):
        digits = value.replace(" ", "").replace("-", "")
        if len(digits) == 15:
            return digits
    return value


def normalize_identifier(identifier_type, value):
    if identifier_type not in TYPES:
        raise ValidationError("Unsupported identifier type.")
    key = identity_key(value)
    entered = value.strip()
    if identifier_type == "SERIAL":
        normalized = entered.upper()
    else:
        if not re.fullmatch(r"[0-9 -]+", entered):
            raise ValidationError("IMEI accepts ASCII digits, spaces and hyphens only.")
        normalized = entered.replace(" ", "").replace("-", "")
        if not re.fullmatch(r"[0-9]{15}", normalized):
            raise ValidationError("IMEI requires exactly 15 ASCII digits.")
        digits = [int(c) for c in normalized]
        total = sum((d * 2 // 10 + d * 2 % 10) if i % 2 else d for i, d in enumerate(digits))
        if total % 10:
            raise ValidationError("IMEI check digit is invalid.")
    return entered, normalized, key


def validate_configuration(product_model, values):
    """Caller holds the model lock used by catalog policy writers."""
    policy = get_identification_policy(product_model)
    if policy is None:
        raise ValidationError("Identification policy is UNCONFIGURED; configure the ProductModel first.")
    normalized = {}
    for kind, field in (("IMEI1", "imei1_requirement"), ("IMEI2", "imei2_requirement"), ("SERIAL", "serial_requirement")):
        value = values.get(kind, "")
        # Empty string means omission; whitespace-only supplied input is invalid.
        present = value != ""
        requirement = getattr(policy, field)
        if requirement == "REQUIRED" and not present:
            raise ValidationError(f"{kind} is required by current identification policy.")
        if requirement == "NOT_APPLICABLE" and present:
            raise ValidationError(f"{kind} is not applicable under current identification policy.")
        if present:
            normalized[kind] = normalize_identifier(kind, value)
    keys = [entry[2] for entry in normalized.values()]
    if len(keys) != len(set(keys)):
        raise ValidationError("A physical identifier cannot occupy multiple slots.")
    return normalized
