"""
Reescribe la respuesta del coping de cada pedido guardado: de dos claves a una.

El coping de Paver era un interruptor (`coping`) y, prendido, un estilo aparte
(`coping_style`). Ahora es una sola respuesta, `coping`, con un código: `keep`,
`none` o el estilo. `ImageGeneration.areas` es un JSON, así que el cambio de
forma no lo hace el esquema: hay que tocar las filas. Sin esta migración, un
pedido viejo abierto para «ajustar y volver a generar» llegaría con un booleano
donde el catálogo espera un código, y la respuesta se descartaría en silencio.

Las dos direcciones están escritas. La vuelta pierde un dato y no hay forma de
evitarlo: `none` —sin coping, el piso llega hasta el agua— no existía en la
forma vieja, y vuelve como interruptor apagado, que es lo más cercano.

Un pedido sin respuesta de coping se deja como está: no se le inventa una. Y un
interruptor prendido sin estilo —que la forma vieja permitía— pasa a
`match_field_pavers`: de los cinco estilos es el único que no agrega una
decisión de diseño, porque repite el material del piso.
"""

from django.db import migrations

STYLES = ('bullnose', 'pool_coping_cap', 'tumbled', 'straight_cut', 'match_field_pavers')
STYLE_WHEN_UNSTATED = 'match_field_pavers'
PAVER = 'paver'

BATCH_SIZE = 500


def _one_answer(answers: dict) -> dict:
    """De `coping` booleano más `coping_style` a `coping` con un código."""
    switch = answers.get('coping')
    if not isinstance(switch, bool):
        return answers

    style = answers.get('coping_style')
    if not switch:
        code = 'keep'
    elif style in STYLES:
        code = style
    else:
        code = STYLE_WHEN_UNSTATED
    # Se conserva el orden de las claves: es el orden en que se contestó.
    return {
        key: (code if key == 'coping' else value)
        for key, value in answers.items()
        if key != 'coping_style'
    }


def _two_answers(answers: dict) -> dict:
    """De `coping` con un código a `coping` booleano más `coping_style`."""
    code = answers.get('coping')
    if not isinstance(code, str):
        return answers

    rewritten = {}
    for key, value in answers.items():
        if key != 'coping':
            rewritten[key] = value
            continue
        rewritten['coping'] = code in STYLES
        if code in STYLES:
            rewritten['coping_style'] = code
    return rewritten


def _rewrite(apps, convert) -> None:
    ImageGeneration = apps.get_model('ai', 'ImageGeneration')

    pending = []
    for generation in ImageGeneration.objects.exclude(areas=[]).iterator(chunk_size=BATCH_SIZE):
        areas = generation.areas
        if not isinstance(areas, list):
            continue

        rewritten = [
            {**container, 'answers': convert(container['answers'])}
            if isinstance(container, dict)
            and container.get('service') == PAVER
            and isinstance(container.get('answers'), dict)
            else container
            for container in areas
        ]
        if rewritten == areas:
            continue

        generation.areas = rewritten
        pending.append(generation)

        if len(pending) >= BATCH_SIZE:
            ImageGeneration.objects.bulk_update(pending, ['areas'])
            pending.clear()

    if pending:
        ImageGeneration.objects.bulk_update(pending, ['areas'])


def to_one_answer(apps, _schema_editor):
    _rewrite(apps, _one_answer)


def to_two_answers(apps, _schema_editor):
    _rewrite(apps, _two_answers)


class Migration(migrations.Migration):
    dependencies = [('ai', '0012_aisettings_watermark')]

    operations = [migrations.RunPython(to_one_answer, to_two_answers)]
