from datetime import datetime, timedelta

def planificar(juegos, asociaciones, estados, ahora):
    acciones = []

    for juego in juegos:
        inicio_texto = juego.get("_hermes_start_at")

        if not inicio_texto:
            continue

        try:
            inicio = datetime.fromisoformat(
                inicio_texto.replace("Z", "+00:00")
            )
        except (ValueError, TypeError):
            continue

        if inicio.tzinfo is None:
            continue  # Sin zona horaria fiable, no programar.

        clave = juego.get("_hermes_key")
        canal = asociaciones.get(clave)

        if not canal:
            continue

        estado = estados.get(clave, {})
        running = estado.get("running")
        ondemand = estado.get("ondemand")

        if ondemand is not False:
            continue

        # Reintentos de arranque: -10, -5, 0, +10 y +20 minutos.
        horarios = [-10, -5, 0, 10, 20]
        intentos = estado.get("start_attempts", [])

        if estado.get("status") in ("scheduled", "live"):
            if running is False and len(intentos) < len(horarios):
                siguiente = inicio + timedelta(
                    minutes=horarios[len(intentos)]
                )

                limite = inicio + timedelta(minutes=30)

                # Si la liga confirma LIVE, recuperar hasta 6 h después del inicio.
                recuperacion_live = (estado.get("status") == "live" and
                                    inicio - timedelta(minutes=10) <= ahora <= inicio + timedelta(hours=6))
                ultimo = intentos[-1] if intentos else None
                if recuperacion_live and ultimo:
                    try:
                        anterior = datetime.fromisoformat(ultimo['time'])
                        recuperacion_live = (anterior.tzinfo is not None and
                                            ahora >= anterior + timedelta(minutes=5))
                    except (KeyError, ValueError, TypeError):
                        recuperacion_live = False
                if (siguiente <= ahora <= limite) or recuperacion_live:
                    acciones.append({
                        "action": "start",
                        "channel": canal,
                        "game": clave,
                        "reason": "REINTENTO_PROGRAMADO",
                        "attempt": len(intentos) + 1
                    })

        if estado.get("status") == "final":
            primera = estado.get("first_final_seen")

            if not primera:
                continue

            try:
                primera = datetime.fromisoformat(primera)
            except (ValueError, TypeError):
                continue

            if primera.tzinfo is None:
                continue

            if ahora >= primera + timedelta(minutes=30):
                if running is True:
                    acciones.append({
                        "action": "stop",
                        "channel": canal,
                        "game": clave,
                        "reason": "FINAL_CONFIRMADO_30"
                    })

    return acciones
