import pandas as pd
import numpy as np
from scipy.sparse import csr_matrix
from scipy.sparse.csgraph import shortest_path


# Analyse des distances présentes, asymétrie (différence de temps) ou une seule valeur AB ou BA

def detecter_asymetries_locales(fichier_aretes):
    # Lecture du fichier .tab local
    # sep='\t' est crucial car les fichiers .tab utilisent des tabulations
    try:
        df = pd.read_csv(fichier_aretes, sep=r'\s+')
    except FileNotFoundError:
        return "Erreur : Le fichier man_a.tab est introuvable dans le dossier."

    # Création d'un dictionnaire (Source, Destination) -> Temps
    # pour une recherche en O(1)
    reseau = {(row.arc_src, row.arc_dst): row.time for row in df.itertuples()}

    asymetries = []
    vus = set()  # Pour éviter de lister (A,B) et (B,A) séparément si on veut juste les paires

    for row in df.itertuples():
        u, v = row.arc_src, row.arc_dst
        t_uv = row.time

        # On vérifie l'arc inverse (v, u)
        t_vu = reseau.get((v, u))

        # Identifiant unique de la paire non ordonnée pour éviter les doublons dans le rapport
        paire = tuple(sorted((u, v)))

        if t_vu is not None:
            # Cas : L'arc existe dans les deux sens mais les temps diffèrent
            if t_uv != t_vu and paire not in vus:
                asymetries.append({
                    'Sommet_A': u,
                    'Sommet_B': v,
                    'Temps_A_vers_B': t_uv,
                    'Temps_B_vers_A': t_vu,
                    'Type': 'Différence de temps'
                })
                vus.add(paire)
        else:
            # Cas : Sens unique (l'inverse n'existe pas)
            # Utile pour votre article si vous traitez l'asymétrie topologique
            asymetries.append({
                'Sommet_A': u,
                'Sommet_B': v,
                'Temps_A_vers_B': t_uv,
                'Temps_B_vers_A': 'N/A',
                'Type': 'Sens unique'
            })

    return pd.DataFrame(asymetries)


# Calcul de la matrice des distances


def generer_matrice_orientee_propre(file_v, file_a):
    # 1. Chargement des sommets (man_v.tab)
    df_v = pd.read_csv(file_v, sep=r'\s+', engine='python')
    df_v.columns = df_v.columns.str.strip()

    # Création du mapping pour gérer les IDs géants
    ids_originaux = sorted(df_v['V'].unique())
    mapping = {id_orig: i for i, id_orig in enumerate(ids_originaux)}
    nb_sommets = len(ids_originaux)

    # 2. Chargement des arcs (man_a.tab)
    df_a = pd.read_csv(file_a, sep=r'\s+', engine='python')
    df_a.columns = df_a.columns.str.strip()

    # 3. Traitement des arcs orientés
    # On garde arc_src et arc_dst tels quels pour respecter le sens de circulation.
    # Le groupby(.min()) reste utile pour ne garder que le trajet le plus rapide
    # s'il y a des doublons stricts (A -> B) dans votre fichier source.
    df_dir = df_a.groupby(['arc_src', 'arc_dst'])['time'].min().reset_index()

    # 4. Construction de la matrice de distances
    # On convertit les IDs géants en indices 0...N-1
    row_idx = df_dir['arc_src'].map(mapping).astype(int)
    col_idx = df_dir['arc_dst'].map(mapping).astype(int)
    poids = df_dir['time'].values

    # On crée une matrice creuse ORIENTÉE (on ne duplique plus les arcs dans les deux sens)
    adj_matrix = csr_matrix((poids, (row_idx, col_idx)), shape=(nb_sommets, nb_sommets))

    # 5. Calcul des plus courts chemins
    # directed=True garantit que le chemin A->B peut être différent de B->A
    print("Calcul de la matrice complète (All-Pairs Shortest Path Orienté)...")
    matrice_finale = shortest_path(csgraph=adj_matrix, directed=True)

    # 6. ENREGISTREMENT
    np.save("application/non_sym/nonsym_data/matrice_distances_orientee.npy", matrice_finale)
    np.save("application/non_sym/nonsym_data/liste_ids.npy", np.array(ids_originaux))

    print(f"Succès ! Matrice orientée de {nb_sommets}x{nb_sommets} enregistrée.")

    return matrice_finale, ids_originaux


try:
    # --- Exécution de l'analyse---
    fichier_a = "application/man_a.tab"
    fichier_v = "application/man_v.tab"

    resultats = detecter_asymetries_locales(fichier_a)

    if isinstance(resultats, pd.DataFrame):
        print(f"Analyse terminée. {len(resultats)} segments asymétriques trouvés.")
        # Affichage des 15 premières lignes pour vérification
        print(resultats.head(10))
        print(len(resultats))

        # Exportation pour votre partie "Application"
        resultats.to_csv("asymetries_analyse.csv", index=False)
    else:
        print(resultats)

#             Calcul de la matrice distance

    matrice, sommets_ids = generer_matrice_orientee_propre(fichier_v, fichier_a)

    print("Matrice calculée avec succès !\n")
    print(f"Dimensions de la matrice : {matrice.shape}\n")

    print("\nTerminé avec succès !")
    print(f"Fichier matrice : {matrice.nbytes / 1024**2:.2f} Mo")

except Exception as e:
    print(f"Erreur : {e}")
