import os
import torch
import torch.nn as nn
from torch.utils.data import DataLoader, Dataset
from torchvision import models, transforms
from PIL import Image
import numpy as np
from sklearn.cluster import KMeans
from sklearn.manifold import TSNE
import matplotlib.pyplot as plt
import pandas as pd

# 1. CONFIGURACIÓN
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
IMG_DIR = os.path.join(BASE_DIR, "../..", "datos_para_ai")
ENCODER_PATH = os.path.join(BASE_DIR, "../..", "models", "msi_encoder_trained.pth")
N_CLUSTERS = 10 
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# 2. CARGAR EL MODELO ENTRENADO
def load_trained_encoder(path):
    if not os.path.exists(path):
        raise FileNotFoundError(f"No se encontró el modelo en {path}")
    
    model = models.efficientnet_b0()
    model.classifier = nn.Identity()
    
    state_dict = torch.load(path, map_location=DEVICE)
    model.load_state_dict(state_dict)
    model.to(DEVICE)
    model.eval()
    return model

# 3. DATASET PARA INFERENCIA
class MSIDatasetInference(Dataset):
    def __init__(self, img_dir):
        self.img_dir = img_dir
        self.img_names = sorted([f for f in os.listdir(img_dir) if f.endswith('.png')])
        self.transform = transforms.Compose([
            transforms.Resize((224, 224)),
            transforms.ToTensor(),
        ])

    def __len__(self):
        return len(self.img_names)

    def __getitem__(self, idx):
        img_path = os.path.join(self.img_dir, self.img_names[idx])
        image = Image.open(img_path).convert("RGB")
        return self.transform(image), self.img_names[idx]

# --- PROCESO ---
if __name__ == "__main__":
    print("Extrayendo características con el encoder...")
    try:
        encoder = load_trained_encoder(ENCODER_PATH)
    except Exception as e:
        print(f"Error al cargar el modelo: {e}")
        exit(1)

    dataset = MSIDatasetInference(IMG_DIR)
    if len(dataset) == 0:
        print(f"No se encontraron imágenes en {IMG_DIR}")
        exit(1)
        
    loader = DataLoader(dataset, batch_size=1, shuffle=False)

    features = []
    ion_names = []

    with torch.no_grad():
        for img, name in loader:
            img = img.to(DEVICE)
            feat = encoder(img)
            # Normalización L2
            feat = feat / feat.norm(p=2, dim=1, keepdim=True)
            features.append(feat.cpu().numpy().flatten())
            ion_names.append(name[0])

    features = np.array(features)

    # 4. CLUSTERING K-MEANS
    print(f"Realizando KMeans con {N_CLUSTERS} clusters...")
    km = KMeans(n_clusters=N_CLUSTERS, n_init=20, random_state=42)
    labels = km.fit_predict(features)

    # 5. VISUALIZACIÓN t-SNE CORREGIDA
    print("Generando visualización t-SNE con escala corregida...")
    tsne = TSNE(n_components=2, perplexity=30, random_state=42)
    embeddings_2d = tsne.fit_transform(features)

    plt.figure(figsize=(10, 8))
    # Aplicando tu corrección específica:
    scatter = plt.scatter(embeddings_2d[:, 0], embeddings_2d[:, 1], c=labels, cmap='tab10', alpha=0.8, edgecolors='w', s=50)
    
    # Configuración de la barra de colores con los ticks exactos para 10 clusters
    cbar = plt.colorbar(scatter, ticks=range(N_CLUSTERS))
    cbar.set_label('Cluster ID')
    
    plt.title(f"t-SNE Visualization of MSI Ions (K={N_CLUSTERS})")
    plt.xlabel("t-SNE dimension 1")
    plt.ylabel("t-SNE dimension 2")
    plt.grid(True, linestyle='--', alpha=0.5)
    
    output_file = "msi_tsne_corregido.png"
    plt.savefig(output_file, dpi=300, bbox_inches='tight')
    # plt.show()
    
    print(f"Visualización guardada exitosamente en '{output_file}'.")
