"""
Paso 2 de 3: convertir las claves que ya estan en circulacion.

Las claves tienen el formato `ApiKey_<32 hex>_<32 hex>` desde el principio, y
el formato nuevo es exactamente el mismo. Eso permite calcular el prefijo y el
hash a partir de la clave guardada, de modo que **los clientes siguen usando
la misma clave que ya tienen**: no hay que avisar a nadie ni coordinar un
cambio.

Lo que si hay que asumir es que esas claves estuvieron sin cifrar en la base
de datos, en las copias de seguridad y en el panel de administracion. Conviene
rotarlas cuando se pueda; el hasheo protege de aqui en adelante, no de lo que
ya pudo copiarse.

Una clave cuyo formato no encaje se desactiva en lugar de borrarse: dejarla
activa con un hash vacio la haria inservible igualmente, pero en silencio, y
asi queda constancia de cual fue.
"""

import hashlib

from django.db import migrations


def _partir(clave: str):
    if not clave:
        return None
    partes = clave.strip().split("_", 2)
    if len(partes) != 3 or partes[0] != "ApiKey" or not partes[1] or not partes[2]:
        return None
    return partes[1], partes[2]


def hashear(apps, schema_editor):
    ApiKeys = apps.get_model("keys", "ApiKeys")

    convertidas = 0
    desactivadas = 0
    prefijos_vistos = set()

    for registro in ApiKeys.objects.all().iterator():
        partido = _partir(registro.key or "")

        if partido is None or partido[0] in prefijos_vistos:
            # Formato inesperado, o un prefijo repetido que impediria imponer
            # la unicidad en el paso siguiente.
            registro.key_prefix = f"invalida-{registro.pk}"
            registro.key_hash = ""
            registro.active = False
            registro.save(update_fields=["key_prefix", "key_hash", "active"])
            desactivadas += 1
            continue

        prefijo, secreto = partido
        prefijos_vistos.add(prefijo)
        registro.key_prefix = prefijo
        registro.key_hash = hashlib.sha256(secreto.encode("utf-8")).hexdigest()
        registro.save(update_fields=["key_prefix", "key_hash"])
        convertidas += 1

    if desactivadas:
        print(
            f"\n  AVISO: {desactivadas} clave(s) con formato inesperado se han "
            "desactivado. Emitir claves nuevas desde el panel de administración."
        )
    print(f"  {convertidas} clave(s) convertidas a hash.")


def revertir(apps, schema_editor):
    """
    No se puede deshacer.

    El secreto original no se guarda en ninguna parte una vez hasheado, que es
    justo el objetivo. Para volver atras hay que emitir claves nuevas.
    """
    raise migrations.exceptions.IrreversibleError(
        "Las claves hasheadas no se pueden recuperar. Emite claves nuevas."
    )


class Migration(migrations.Migration):
    dependencies = [
        ("keys", "0003_campos_de_hash"),
    ]

    operations = [
        migrations.RunPython(hashear, revertir),
    ]
