from typing import Any

import networkx as nx
import streamlit as st

from utils.graph_utils import extraire_chemins_depuis, extraire_chemins_vers
from utils.persistance import get_champ_statut, maj_champ_statut, supprime_champ_statut
from utils.translations import _


def selectionner_minerais(G: nx.Graph, noeuds_depart: list[Any]) -> list[str | int]:
    """Interface pour sélectionner les minerais si nécessaire.

    Args:
        G (nx.Graph): Le graphe des relations entre les nœuds.
        noeuds_depart (list): Les nœuds de départ qui doivent être considérés.

    Returns:
        list: La liste des nœuds sélectionnés comme minerais.
    """
    minerais_selection = None

    st.markdown(f"## {str(_('pages.plan_d_action.select_minerals'))}")

    # Étape 1 : récupérer tous les nœuds descendants depuis les produits finaux
    descendants = set()
    for start in noeuds_depart:
        descendants.update(nx.descendants(G, start))  # tous les successeurs (récursifs)

    # Étape 2 : ne garder que les nœuds de niveau 2 parmi les descendants
    minerais_nodes = sorted([
        n for n in descendants
        if G.nodes[n].get("niveau") and int(str(G.nodes[n].get("niveau")).strip('"')) == 2
    ])

    # Initialiser depuis champ_statut si besoin
    if "analyse_minerais" not in st.session_state:
        anciens = []
        i = 0
        while True:
            val = get_champ_statut(f"pages.plan_d_action.filter_by_minerals.{i}")
            if not val:
                break
            anciens.append(val)
            i += 1
        st.session_state["analyse_minerais"] = anciens

    # Afficher le multiselect
    st.multiselect(
        str(_("pages.plan_d_action.filter_by_minerals")),
        minerais_nodes,
        key="analyse_minerais"
    )

    minerais_selection = st.session_state["analyse_minerais"]

    # Toujours purger, puis réécrire si nécessaire
    supprime_champ_statut("pages.plan_d_action.filter_by_minerals")
    if minerais_selection:
        for i, val in enumerate(minerais_selection):
            maj_champ_statut(f"pages.plan_d_action.filter_by_minerals.{i}", val)

    return minerais_selection

def selectionner_noeuds(
    G: nx.Graph,
    niveaux_temp: dict[str | int, int],
    niveau_depart: int
) -> tuple[list[str | int] | None, list[str | int]]:
    """Interface pour sélectionner les nœuds spécifiques de départ et d'arrivée.

    Args:
        G (nx.Graph): Le graphe des relations entre les nœuds.
        niveaux_temp (dict): Dictionnaire contenant les niveaux des nœuds.
        niveau_depart (int): Niveau à partir duquel commencer la sélection.

    Returns:
        tuple: La paire de départ et d'arrivée des nœuds sélectionnés.
    """
    st.markdown("---")
    st.markdown(f"## {str(_('pages.plan_d_action.fine_selection'))}")

    depart_nodes = [n for n in G.nodes() if niveaux_temp.get(n) == niveau_depart]
    noeuds_arrivee = [n for n in G.nodes() if niveaux_temp.get(n) == 99]

    if "analyse_noeuds_depart" not in st.session_state:
        anciens = []
        i = 0
        while True:
            val = get_champ_statut(f"pages.plan_d_action.filter_start_nodes.{i}")
            if not val:
                break
            anciens.append(val)
            i += 1
        st.session_state["analyse_noeuds_depart"] = anciens

    st.multiselect(
        str(_("pages.plan_d_action.filter_start_nodes")),
        sorted(depart_nodes),
        key="analyse_noeuds_depart"
    )

    noeuds_depart = st.session_state["analyse_noeuds_depart"]

    supprime_champ_statut("pages.plan_d_action.filter_start_nodes")
    if noeuds_depart:
        for i, val in enumerate(noeuds_depart):
            maj_champ_statut(f"pages.plan_d_action.filter_start_nodes.{i}", val)

    noeuds_depart = noeuds_depart if noeuds_depart else None

    return noeuds_depart, noeuds_arrivee

def extraire_chemins_selon_criteres(
    G: nx.Graph,
    niveaux: dict[str | int, int],
    niveau_depart: int,
    noeuds_depart: list[str | int] | None,
    noeuds_arrivee: list[str | int],
    minerais: list[str | int] | None
) -> list[list[str | int]]:
    """Extrait les chemins selon les critères spécifiés.

    Args:
        G (nx.Graph): Le graphe des relations entre les nœuds.
        niveaux (dict): Dictionnaire contenant les niveaux des nœuds.
        niveau_depart (int): Niveau à partir duquel commencer la sélection.
        noeuds_depart (list, optional): Les nœuds de départ qui doivent être considérés.
        noeuds_arrivee (list): Les nœuds d'arrivée qui doivent être inclus dans les chemins.
        minerais (list, optional): La liste des nœuds sélectionnés comme minerais.

    Returns:
        list: Liste des chemins trouvés selon les critères spécifiés.
    """
    chemins = []
    if noeuds_depart and noeuds_arrivee:
        for nd in noeuds_depart:
            for na in noeuds_arrivee:
                tous_chemins = extraire_chemins_depuis(G, nd)
                chemins.extend([chemin for chemin in tous_chemins if na in chemin])
    elif noeuds_depart:
        for nd in noeuds_depart:
            chemins.extend(extraire_chemins_depuis(G, nd))
    elif noeuds_arrivee:
        for na in noeuds_arrivee:
            chemins.extend(extraire_chemins_vers(G, na, niveau_depart))
    else:
        sources_depart = [n for n in G.nodes() if niveaux.get(n) == niveau_depart]
        for nd in sources_depart:
            chemins.extend(extraire_chemins_depuis(G, nd))

    if minerais:
        chemins = [chemin for chemin in chemins if any(n in minerais for n in chemin)]

    return chemins
