#!/usr/bin/env python3
"""PARCID · identificador v1 (PID): implementación de referencia (generar, validar, normalizar).

Formato canónico:  PID-1-XXXX-XXXX-XXXX-CC
  PID        prefijo fijo (no entra en el control)
  1          versión del esquema (símbolo Crockford, entra en el control)
  12 × X     60 bits aleatorios en Base32 Crockford, en 3 grupos de 4
  CC         2 símbolos de control, sistema puro ISO 7064 "MOD 1021-32"

Control: sea P = Σ v_i·32^(n−1−i) mod 1021 sobre los 13 símbolos (versión + carga).
  C = (1 − 1024·P) mod 1021 = (1 − 3·P) mod 1021, codificado en 2 símbolos (C < 1021 < 1024).
  Verificación: la suma sobre los 15 símbolos (versión + carga + control) debe ser ≡ 1 (mod 1021).
Licencia: CC0 1.0 (dominio público). Sin dependencias fuera de la biblioteca estándar.
"""
import re, secrets, sys

ALPHABET = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"   # Crockford: sin I, L, O, U
_VAL = {c: i for i, c in enumerate(ALPHABET)}
_VAL.update({"I": 1, "L": 1, "O": 0})            # tolerancia de lectura
MOD = 1021
VERSION = 1
PAYLOAD_SYMBOLS = 12                               # 60 bits

def _values(symbols: str):
    return [_VAL[c] for c in symbols]

def _poly(values):
    p = 0
    for v in values:
        p = (p * 32 + v) % MOD
    return p

def check_symbols(version: int, payload: str) -> str:
    p = _poly([version] + _values(payload))
    c = (1 - 1024 * p) % MOD
    return ALPHABET[c // 32] + ALPHABET[c % 32]

def fmt(version: int, payload: str, check: str) -> str:
    return f"PID-{ALPHABET[version]}-{payload[0:4]}-{payload[4:8]}-{payload[8:12]}-{check}"

def generate(version: int = VERSION, rng=secrets.SystemRandom()) -> str:
    bits = rng.getrandbits(5 * PAYLOAD_SYMBOLS)
    payload = "".join(ALPHABET[(bits >> (5 * (PAYLOAD_SYMBOLS - 1 - i))) & 31] for i in range(PAYLOAD_SYMBOLS))
    return fmt(version, payload, check_symbols(version, payload))

_RE = re.compile(r"^PID[-\s]?([0-9A-Z])[-\s]?([0-9A-Z]{4})[-\s]?([0-9A-Z]{4})[-\s]?([0-9A-Z]{4})[-\s]?([0-9A-Z]{2})$")

def normalize(text: str) -> str:
    """Acepta mayúsculas/minúsculas, con o sin guiones, I/L→1 y O→0. Devuelve la forma canónica o lanza ValueError."""
    t = re.sub(r"[\s\-_]", "", text.strip().upper())
    if t.startswith("HTTPS://") or t.startswith("HTTP://"):
        t = t.rsplit("/", 1)[-1]
    if not t.startswith("PID"):
        raise ValueError("prefijo PID ausente")
    body = t[3:]
    if len(body) != 15 or any(c not in _VAL for c in body):
        raise ValueError("longitud o símbolos no válidos")
    body = "".join(ALPHABET[_VAL[c]] for c in body)        # canoniza I/L/O
    version, payload, check = _VAL[body[0]], body[1:13], body[13:15]
    if version != VERSION:
        raise ValueError(f"versión {version} no soportada")
    if _poly([version] + _values(payload) + _values(check)) != 1:
        raise ValueError("control incorrecto")
    return fmt(version, payload, check)

def is_valid(text: str) -> bool:
    try:
        normalize(text); return True
    except ValueError:
        return False

if __name__ == "__main__":
    if len(sys.argv) > 1:
        for a in sys.argv[1:]:
            try: print("OK ", normalize(a))
            except ValueError as e: print("KO ", a, "→", e)
    else:
        for _ in range(3): print(generate())
