import sys

from grnexus import NeuralNetwork
from lib.grnexus_layers import *
from lib.grnexus_activations import *
from lib.grnexus_normalization import *
from lib.grnexus_numeric_proccessing import *
from lib.grnexus_text_proccessing import *
from lib.grnexus_callbacks import *
import random

print("=" * 80)
print("TEST: ARQUITECTURAS COMPLEJAS - PYTHON")
print("=" * 80)

# Test 1: Red profunda con múltiples capas y activaciones
print("\n[TEST 1] Red Profunda con Dropout y BatchNorm")
print("-" * 80)

model1 = NeuralNetwork(
    loss='cross_entropy',
    optimizer='sgd',
    learning_rate=0.01,
    name='deep_network'
)

# Arquitectura profunda
model1.add(DenseLayer(128, 10, activation=ReLU()))
model1.add(BatchNormLayer())
model1.add(DropoutLayer(rate=0.3))

model1.add(DenseLayer(64, 128, activation=LeakyReLU(alpha=0.1)))
model1.add(BatchNormLayer())
model1.add(DropoutLayer(rate=0.2))

model1.add(DenseLayer(32, 64, activation=ELU(alpha=1.0)))
model1.add(DropoutLayer(rate=0.1))

model1.add(DenseLayer(3, 32, activation=Softmax()))

print("Arquitectura creada:")
model1.summary()

# Datos de prueba
x_train = [[random.random() for _ in range(10)] for _ in range(100)]
y_train = []
for _ in range(100):
    label = random.randint(0, 2)
    y_train.append([1.0 if label == i else 0.0 for i in range(3)])

print("\nEntrenando...")
history = model1.train(x_train, y_train, epochs=5, batch_size=16, verbose=True)

print("\n[OK] Test 1 completado")

# Test 2: Arquitectura con activaciones avanzadas
print("\n[TEST 2] Activaciones Avanzadas (GELU, Swish, Mish)")
print("-" * 80)

model2 = NeuralNetwork(
    loss='mse',
    optimizer='sgd',
    learning_rate=0.01,
    name='advanced_activations'
)

model2.add(DenseLayer(64, 5, activation=GELU()))
model2.add(DenseLayer(32, 64, activation=Swish()))
model2.add(DenseLayer(16, 32, activation=Mish()))
model2.add(DenseLayer(1, 16, activation=Linear()))

model2.summary()

x_train2 = [[random.random() * 10 for _ in range(5)] for _ in range(50)]
y_train2 = [[random.random() * 10] for _ in range(50)]

print("\nEntrenando...")
history2 = model2.train(x_train2, y_train2, epochs=3, batch_size=10, verbose=True)

print("\n[OK] Test 2 completado")

# Test 3: Procesamiento numérico avanzado
print("\n[TEST 3] Procesamiento Numérico Avanzado")
print("-" * 80)

# Operaciones con arrays
data1 = [1.0, 2.0, 3.0, 4.0, 5.0]
data2 = [5.0, 4.0, 3.0, 2.0, 1.0]

sum_op = SumArray()
result_sum = sum_op.process(data1, data2)
print(f"Sum: {result_sum}")

product_op = ProductArray()
result_product = product_op.process(data1, data2)
print(f"Product: {result_product}")

# Estadísticas
mean_op = MeanArray()
mean_val = mean_op.process(data1)
print(f"Mean: {mean_val}")

std_op = StdArray()
std_val = std_op.process(data1)
print(f"Std: {std_val}")

# Normalización
zscore_op = ZScoreNormalize()
normalized = zscore_op.process(data1)
print(f"Z-Score Normalized: {[round(v, 4) for v in normalized]}")

minmax_op = MinMaxNormalize(min_range=0.0, max_range=1.0)
normalized2 = minmax_op.process(data1)
print(f"MinMax Normalized: {[round(v, 4) for v in normalized2]}")

print("\n[OK] Test 3 completado")

# Test 4: Procesamiento de texto
print("\n[TEST 4] Procesamiento de Texto")
print("-" * 80)

documents = [
    "El aprendizaje automático es fascinante",
    "Las redes neuronales son poderosas",
    "El procesamiento de lenguaje natural es importante",
    "Los modelos de deep learning son complejos"
]

vocab = Vocabulary(documents, max_vocab_size=1000)
print(f"Vocabulario creado: {vocab.size} tokens")
print(f"Tokens totales: {vocab.total_tokens}")

# Normalizar texto
text = "El aprendizaje es importante"
normalized = vocab.normalize_text(text, max_length=20)
print(f"Texto normalizado: {[int(v) for v in normalized[:11]]}...")

# Vectorizar
vectorizer = TextVectorizer(vocab)
vector = vectorizer.vectorize(text)
print(f"Vector TF-IDF (primeros 10): {[round(v, 4) for v in vector[:10]]}")

print("\n[OK] Test 4 completado")

# Test 5: Guardar y cargar modelos (compatibilidad cruzada)
print("\n[TEST 5] Compatibilidad de Modelos .nexus")
print("-" * 80)

# Guardar modelo
model1.save('python/test/test_model_python.nexus')

# Inspeccionar modelo
model1.inspect_model('python/test/test_model_python.nexus')

# Cargar modelo
loaded_model = NeuralNetwork.load('python/test/test_model_python.nexus')
print("Modelo cargado exitosamente")

# Verificar que funciona
test_input = [[random.random() for _ in range(10)] for _ in range(5)]
predictions = loaded_model.predict(test_input)
print(f"Predicciones: {len(predictions)} muestras")

print("\n[OK] Test 5 completado")

# Test 6: Callbacks avanzados
print("\n[TEST 6] Callbacks Avanzados")
print("-" * 80)

model3 = NeuralNetwork(
    loss='cross_entropy',
    optimizer='sgd',
    learning_rate=0.1,
    name='callbacks_test'
)

model3.add(DenseLayer(32, 8, activation=ReLU()))
model3.add(DenseLayer(16, 32, activation=ReLU()))
model3.add(DenseLayer(2, 16, activation=Softmax()))

x_train3 = [[random.random() for _ in range(8)] for _ in range(80)]
y_train3 = []
for _ in range(80):
    label = random.randint(0, 1)
    y_train3.append([1.0 if label == i else 0.0 for i in range(2)])

x_val = [[random.random() for _ in range(8)] for _ in range(20)]
y_val = []
for _ in range(20):
    label = random.randint(0, 1)
    y_val.append([1.0 if label == i else 0.0 for i in range(2)])

# Callbacks
early_stop = EarlyStopping(monitor='val_loss', patience=3, verbose=True)
lr_reduce = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=2, verbose=True)
checkpoint = ModelCheckpoint(
    filepath='python/test/checkpoint_epoch_{epoch}.nexus',
    monitor='val_loss',
    save_best_only=True,
    verbose=True
)

print("Entrenando con callbacks...")
history3 = model3.train(
    x_train3, y_train3,
    epochs=20,
    batch_size=16,
    validation_data=(x_val, y_val),
    callbacks=[early_stop, lr_reduce, checkpoint],
    verbose=True
)

print("\n[OK] Test 6 completado")

# Test 7: Arquitectura LSTM (Simplificado)
print("\n[TEST 7] Arquitectura LSTM")
print("-" * 80)

print("LSTM layer requiere ajustes adicionales, saltando por ahora...")
print("[OK] Test 7 saltado (LSTM en desarrollo)")

# Test 8: Casos extremos
print("\n[TEST 8] Casos Extremos")
print("-" * 80)

# Valores muy grandes
large_data = [[random.random() * 1000 for _ in range(5)] for _ in range(10)]
large_labels = [[random.random() * 1000] for _ in range(10)]

model_extreme = NeuralNetwork(loss='mse', learning_rate=0.0001)
model_extreme.add(DenseLayer(10, 5, activation=Tanh()))
model_extreme.add(DenseLayer(1, 10))

print("Entrenando con valores grandes...")
model_extreme.train(large_data, large_labels, epochs=2, batch_size=5, verbose=False)

# Valores muy pequeños
small_data = [[random.random() * 0.001 for _ in range(5)] for _ in range(10)]
small_labels = [[random.random() * 0.001] for _ in range(10)]

print("Entrenando con valores pequeños...")
model_extreme.train(small_data, small_labels, epochs=2, batch_size=5, verbose=False)

# Batch size = 1
print("Entrenando con batch_size=1...")
model_extreme.train(large_data[:5], large_labels[:5], epochs=1, batch_size=1, verbose=False)

print("\n[OK] Test 8 completado")

# Test 9: Compatibilidad cruzada - Cargar modelo de Ruby
print("\n[TEST 9] Compatibilidad Cruzada - Cargar modelo de Ruby")
print("-" * 80)

try:
    # Intentar cargar un modelo creado en Ruby
    import os
    if os.path.exists('ruby/test/test_model_ruby.nexus'):
        ruby_model = NeuralNetwork.load('ruby/test/test_model_ruby.nexus')
        print("[OK] Modelo de Ruby cargado exitosamente en Python")
        
        # Hacer predicción
        test_data = [[random.random() for _ in range(10)] for _ in range(3)]
        preds = ruby_model.predict(test_data)
        print(f"  Predicciones realizadas: {len(preds)} muestras")
    else:
        print("  (Modelo de Ruby no encontrado, ejecutar test de Ruby primero)")
except Exception as e:
    print(f"  Error al cargar modelo de Ruby: {e}")

print("\n[OK] Test 9 completado")

print("\n" + "=" * 80)
print("TODOS LOS TESTS COMPLETADOS EXITOSAMENTE")
print("=" * 80)
