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
import math

print("=" * 100)
print("TEST AVANZADO COMPLETO - PYTHON - GRNexus Framework")
print("=" * 100)

# ============================================================================
# TEST 1: GENERACIÓN DE TEXTO - Predicción de siguiente palabra
# ============================================================================
print("\n[TEST 1] GENERACIÓN DE TEXTO - Predicción de siguiente palabra")
print("-" * 100)

# Corpus de entrenamiento
corpus = [
    "el gato come pescado",
    "el perro come carne",
    "el gato duerme mucho",
    "el perro ladra fuerte",
    "el gato maulla suave",
    "el perro corre rápido",
    "el gato salta alto",
    "el perro juega pelota"
]

# Crear vocabulario
vocab = Vocabulary(corpus, max_vocab_size=100)
print(f"Vocabulario creado: {vocab.size} palabras únicas")

# Preparar datos de entrenamiento (predecir siguiente palabra)
x_train = []
y_train = []

for sentence in corpus:
    words = sentence.split()
    for i in range(len(words) - 1):
        # Contexto: palabra actual
        context = vocab.normalize_text(words[i], max_length=1, strategy='truncate')
        # Target: siguiente palabra
        target_word = words[i + 1]
        target_idx = int(vocab.normalize_text(target_word, max_length=1, strategy='truncate')[0])
        
        # One-hot encoding del target
        target_onehot = [0.0] * vocab.size
        if target_idx < vocab.size:
            target_onehot[target_idx] = 1.0
        
        x_train.append(context)
        y_train.append(target_onehot)

print(f"Datos de entrenamiento: {len(x_train)} pares (contexto -> siguiente palabra)")

# Crear modelo para generación de texto
text_model = NeuralNetwork(
    loss='cross_entropy',
    optimizer='sgd',
    learning_rate=0.1,
    name='text_generator'
)

# Arquitectura: Embedding -> Dense -> Softmax
text_model.add(DenseLayer(64, 1, activation=ReLU()))
text_model.add(DenseLayer(32, 64, activation=Tanh()))
text_model.add(DenseLayer(vocab.size, 32, activation=Softmax()))

print("\nArquitectura del modelo:")
text_model.summary()

print("\nEntrenando modelo de generación de texto...")
history = text_model.train(x_train, y_train, epochs=50, batch_size=4, verbose=False)

print("Entrenamiento completado!")
print(f"  Loss inicial: {history['loss'][0]:.4f}")
print(f"  Loss final: {history['loss'][-1]:.4f}")
print(f"  Accuracy final: {history['accuracy'][-1]:.2f}%")

# Generar texto
print("\nGenerando predicciones:")
test_words = ["el", "gato", "perro"]
for word in test_words:
    context = vocab.normalize_text(word, max_length=1, strategy='truncate')
    prediction = text_model.predict([context])[0]
    predicted_idx = prediction.index(max(prediction))
    
    print(f"  '{word}' -> predicción índice: {predicted_idx} (probabilidad: {max(prediction) * 100:.2f}%)")

# Guardar modelo
text_model.save('python/test/text_generator_python.nexus')
print("\n[OK] Modelo de texto guardado: python/test/text_generator_python.nexus")

# ============================================================================
# TEST 2: ANÁLISIS DE SENTIMIENTOS
# ============================================================================
print("\n[TEST 2] ANÁLISIS DE SENTIMIENTOS")
print("-" * 100)

# Dataset de sentimientos
sentiments_data = [
    ["me encanta este producto es excelente", 1],
    ["muy bueno recomendado totalmente", 1],
    ["increíble calidad superó expectativas", 1],
    ["perfecto justo lo que necesitaba", 1],
    ["terrible producto muy malo", 0],
    ["pésima calidad no funciona", 0],
    ["decepcionante no lo recomiendo", 0],
    ["horrible experiencia muy mal", 0]
]

texts = [d[0] for d in sentiments_data]
labels = [d[1] for d in sentiments_data]

# Crear vocabulario para sentimientos
sentiment_vocab = Vocabulary(texts, max_vocab_size=100)
print(f"Vocabulario de sentimientos: {sentiment_vocab.size} palabras")

# Vectorizar textos
vectorizer = TextVectorizer(sentiment_vocab)
x_sentiment = [vectorizer.vectorize(text) for text in texts]
y_sentiment = [[1.0, 0.0] if label == 1 else [0.0, 1.0] for label in labels]

print(f"Datos vectorizados: {len(x_sentiment)} muestras")

# Modelo de sentimientos
sentiment_model = NeuralNetwork(
    loss='cross_entropy',
    optimizer='sgd',
    learning_rate=0.05,
    name='sentiment_analyzer'
)

sentiment_model.add(DenseLayer(32, sentiment_vocab.size, activation=ReLU()))
sentiment_model.add(DropoutLayer(rate=0.3))
sentiment_model.add(DenseLayer(16, 32, activation=Tanh()))
sentiment_model.add(DenseLayer(2, 16, activation=Softmax()))

print("\nArquitectura del analizador de sentimientos:")
sentiment_model.summary()

print("\nEntrenando...")
history_sent = sentiment_model.train(x_sentiment, y_sentiment, epochs=100, batch_size=2, verbose=False)

print("Entrenamiento completado!")
print(f"  Accuracy final: {history_sent['accuracy'][-1]:.2f}%")

# Probar predicciones
print("\nProbando predicciones:")
test_texts = [
    "excelente producto muy bueno",
    "terrible muy malo",
    "producto normal"
]

for text in test_texts:
    vec = vectorizer.vectorize(text)
    pred = sentiment_model.predict([vec])[0]
    sentiment = "POSITIVO" if pred[0] > pred[1] else "NEGATIVO"
    confidence = max(pred) * 100
    print(f"  '{text}' -> {sentiment} ({confidence:.2f}%)")

sentiment_model.save('python/test/sentiment_analyzer_python.nexus')
print("\n[OK] Modelo de sentimientos guardado: python/test/sentiment_analyzer_python.nexus")

# ============================================================================
# TEST 3: RED PROFUNDA CON TODAS LAS ACTIVACIONES
# ============================================================================
print("\n[TEST 3] RED PROFUNDA - Todas las activaciones avanzadas")
print("-" * 100)

deep_model = NeuralNetwork(
    loss='mse',
    optimizer='sgd',
    learning_rate=0.01,
    name='deep_activations'
)

# Probar TODAS las activaciones
deep_model.add(DenseLayer(128, 20, activation=GELU()))
deep_model.add(BatchNormLayer())
deep_model.add(DenseLayer(96, 128, activation=Swish()))
deep_model.add(DropoutLayer(rate=0.2))
deep_model.add(DenseLayer(64, 96, activation=Mish()))
deep_model.add(BatchNormLayer())
deep_model.add(DenseLayer(48, 64, activation=SELU()))
deep_model.add(DropoutLayer(rate=0.15))
deep_model.add(DenseLayer(32, 48, activation=ELU(alpha=1.0)))
deep_model.add(DenseLayer(16, 32, activation=LeakyReLU(alpha=0.1)))
deep_model.add(DenseLayer(5, 16, activation=Linear()))

print("Arquitectura profunda:")
deep_model.summary()

# Datos sintéticos complejos
x_deep = [[random.random() * 10 - 5 for _ in range(20)] for _ in range(200)]
y_deep = [[random.random() * 10 for _ in range(5)] for _ in range(200)]

print("\nEntrenando red profunda...")
history_deep = deep_model.train(x_deep, y_deep, epochs=20, batch_size=16, verbose=False)

print("Entrenamiento completado!")
print(f"  Loss inicial: {history_deep['loss'][0]:.4f}")
print(f"  Loss final: {history_deep['loss'][-1]:.4f}")
print(f"  Mejora: {((1 - history_deep['loss'][-1] / history_deep['loss'][0]) * 100):.2f}%")

deep_model.save('python/test/deep_model_python.nexus')
print("\n[OK] Modelo profundo guardado: python/test/deep_model_python.nexus")

# ============================================================================
# TEST 4: PROCESAMIENTO NUMÉRICO COMPLEJO
# ============================================================================
print("\n[TEST 4] PROCESAMIENTO NUMÉRICO AVANZADO")
print("-" * 100)

# Serie temporal sintética
time_series = [math.sin(i * 0.1) + random.random() * 0.2 for i in range(100)]
print(f"Serie temporal generada: {len(time_series)} puntos")

# Aplicar múltiples operaciones
ma = MovingAverage(window_size=5)
smoothed = ma.process(time_series)
print(f"  Media móvil (ventana=5): {len(smoothed)} puntos")

diff = FiniteDifference()
differences = diff.process(smoothed)
print(f"  Diferencias finitas: {len(differences)} puntos")

zscore = ZScoreNormalize()
normalized = zscore.process(time_series)
mean_normalized = MeanArray().process(normalized)
std_normalized = StdArray().process(normalized)
print(f"  Normalización Z-Score: media={mean_normalized:.4f}, std={std_normalized:.4f}")

# Operaciones estadísticas
mean_val = MeanArray().process(time_series)
std_val = StdArray().process(time_series)
max_val = MaxValueArray().process(time_series)
min_val = MinValueArray().process(time_series)

print("\nEstadísticas de la serie:")
print(f"  Media: {mean_val:.4f}")
print(f"  Desviación estándar: {std_val:.4f}")
print(f"  Máximo: {max_val:.4f}")
print(f"  Mínimo: {min_val:.4f}")

print("\n[OK] Procesamiento numérico completado")

# ============================================================================
# TEST 5: INSPECCIÓN DE MODELOS
# ============================================================================
print("\n[TEST 5] INSPECCIÓN DE ARQUITECTURAS")
print("-" * 100)

print("\n--- Inspeccionando modelo de texto ---")
NeuralNetwork.inspect_model('python/test/text_generator_python.nexus')

print("\n--- Inspeccionando modelo de sentimientos ---")
NeuralNetwork.inspect_model('python/test/sentiment_analyzer_python.nexus')

print("\n--- Inspeccionando modelo profundo ---")
NeuralNetwork.inspect_model('python/test/deep_model_python.nexus')

# ============================================================================
# TEST 6: ARQUITECTURA COMPLEJA CON CALLBACKS INTELIGENTES
# ============================================================================
print("\n[TEST 6] ENTRENAMIENTO CON CALLBACKS INTELIGENTES")
print("-" * 100)

complex_model = NeuralNetwork(
    loss='cross_entropy',
    optimizer='sgd',
    learning_rate=0.1,
    name='complex_with_callbacks'
)

complex_model.add(DenseLayer(64, 15, activation=ReLU()))
complex_model.add(BatchNormLayer())
complex_model.add(DropoutLayer(rate=0.3))
complex_model.add(DenseLayer(32, 64, activation=GELU()))
complex_model.add(DenseLayer(4, 32, activation=Softmax()))

# Datos de entrenamiento y validación
x_train_cb = [[random.random() for _ in range(15)] for _ in range(100)]
y_train_cb = []
for _ in range(100):
    label = random.randint(0, 3)
    y_train_cb.append([1.0 if i == label else 0.0 for i in range(4)])

x_val_cb = [[random.random() for _ in range(15)] for _ in range(30)]
y_val_cb = []
for _ in range(30):
    label = random.randint(0, 3)
    y_val_cb.append([1.0 if i == label else 0.0 for i in range(4)])

# Callbacks inteligentes (SIN guardar checkpoints innecesarios)
early_stop = EarlyStopping(
    monitor='val_loss',
    patience=5,
    min_delta=0.001,
    verbose=True
)

lr_reduce = ReduceLROnPlateau(
    monitor='val_loss',
    factor=0.5,
    patience=3,
    verbose=True
)

print("\nEntrenando con callbacks inteligentes...")
history_cb = complex_model.train(
    x_train_cb, y_train_cb,
    epochs=50,
    batch_size=16,
    validation_data=(x_val_cb, y_val_cb),
    callbacks=[early_stop, lr_reduce],
    verbose=False
)

print("\nResultados del entrenamiento:")
print(f"  Épocas completadas: {len(history_cb['loss'])}")
print(f"  Loss final: {history_cb['loss'][-1]:.4f}")
print(f"  Val Loss final: {history_cb['val_loss'][-1]:.4f}")
print(f"  Accuracy final: {history_cb['accuracy'][-1]:.2f}%")
print(f"  Val Accuracy final: {history_cb['val_accuracy'][-1]:.2f}%")

complex_model.save('python/test/complex_callbacks_python.nexus')
print("\n[OK] Modelo con callbacks guardado")

# ============================================================================
# RESUMEN FINAL
# ============================================================================
print("\n" + "=" * 100)
print("RESUMEN DE TESTS COMPLETADOS")
print("=" * 100)

print("\n[OK] TEST 1: Generación de texto - Modelo entrenado y guardado")
print("[OK] TEST 2: Análisis de sentimientos - Clasificación funcional")
print("[OK] TEST 3: Red profunda - 11 capas con activaciones avanzadas")
print("[OK] TEST 4: Procesamiento numérico - Operaciones complejas")
print("[OK] TEST 5: Inspección de modelos - Arquitecturas analizadas")
print("[OK] TEST 6: Callbacks inteligentes - Early stopping funcional")

print("\nMODELOS GENERADOS:")
print("  1. python/test/text_generator_python.nexus")
print("  2. python/test/sentiment_analyzer_python.nexus")
print("  3. python/test/deep_model_python.nexus")
print("  4. python/test/complex_callbacks_python.nexus")

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