
import networkx as nx
import streamlit as st

from utils.persistance import get_champ_statut, maj_champ_statut, supprime_champ_statut
from utils.translations import _


def get_produits_personnalises(
    G: nx.DiGraph
) -> list[str]:
    """Récupère la liste des produits personnalisés du niveau 0.

    Args:
        G (Any): Le graphe NetworkX contenant les données des produits.

    Returns:
        List[str]: Liste triée des noms de produits.
    """
    return sorted([n for n, d in G.nodes(data=True) if d.get("niveau") == "0" and d.get("personnalisation") == "oui"])

def supprimer_produit(
    G: nx.DiGraph,
    prod: str
) -> nx.DiGraph:
    """Supprime un produit du graphe et affiche le message de succès.

    Args:
        G (Any): Le graphe NetworkX sur lequel supprimer le produit.
        prod (str): Le nom du produit à supprimer.
    """
    G.remove_node(prod)
    st.success(f"{prod} {str(_('pages.personnalisation.deleted'))}")
    st.session_state.pop("prod_sel", None)
    st.rerun()
    return G

def get_operations_disponibles(
    G: nx.DiGraph
) -> list[str]:
    """Récupère la liste des opérations d'assemblage disponibles.

    Args:
        G (Any): Le graphe NetworkX contenant les données des produits et des opérations.

    Returns:
        List[str]: Liste triée des noms des opérations.
    """
    return sorted([
        n for n, d in G.nodes(data=True)
        if d.get("niveau") == "10"
        and any(G.has_edge(p, n) and G.nodes[p].get("niveau") == "0" for p in G.predecessors(n))
    ])

def get_operations_actuelles(
    G: nx.DiGraph,
    prod: str
) -> list[str]:
    """Récupère les opérations actuellement liées au produit.

    Args:
        G (nx.DiGraph): Le graphe NetworkX contenant les données des produits et des opérations.
        prod (str): Le nom du produit dont récupérer les opérations.

    Returns:
        List[str]: Liste des noms des opérations actuelles.
    """
    return [succ for succ in G.successors(prod) if G.nodes[succ].get("niveau") == "10"]

def get_composants_niveau1(
    G: nx.DiGraph
) -> list[str]:
    """Récupère la liste des composants de niveau 1.

    Args:
        G (nx.DiGraph): Le graphe NetworkX contenant les données des produits et des composants.

    Returns:
        List[str]: Liste triée des noms des composants.
    """
    return sorted([n for n, d in G.nodes(data=True) if d.get("niveau") == "1"])

def get_composants_lies(
    G: nx.DiGraph,
    prod: str
) -> list[str]:
    """Récupère les composants actuellement liés au produit.

    Args:
        G (nx.DiGraph): Le graphe NetworkX contenant les données des produits et des composants.
        prod (str): Le nom du produit dont récupérer les composants.

    Returns:
        List[str]: Liste des noms des composants liés.
    """
    return [succ for succ in G.successors(prod) if G.nodes[succ].get("niveau") == "1"]

def mettre_a_jour_operations(
    G: nx.DiGraph,
    prod: str,
    curr_ops: list[str],
    sel_op: str
) -> nx.DiGraph:
    """Met à jour les opérations liées au produit.

    Args:
        G (Any): Le graphe NetworkX contenant les données des produits et des opérations.
        prod (str): Le nom du produit dont mettre à jour les opérations.
        curr_ops (List[str]): Liste actuelle des opérations liées.
        sel_op (str): L'opération sélectionnée pour mise à jour.

    Notes:
        Cette fonction crée ou supprime les liens entre le produit et les opérations
        selon la sélection effectuée par l'utilisateur.
    """
    none_option = str(_("pages.personnalisation.none"))
    for op in curr_ops:
        if sel_op == none_option or op != sel_op:
            G.remove_edge(prod, op)
    if sel_op != none_option and (not curr_ops or sel_op not in curr_ops):
        G.add_edge(prod, sel_op)
    return G

def mettre_a_jour_composants(
    G: nx.DiGraph,
    prod: str,
    linked: list[str],
    nouveaux: list[str]
) -> nx.DiGraph:
    """Met à jour les composants liés au produit.

    Args:
        G (nx.DiGraph): Le graphe NetworkX contenant les données des produits et des composants.
        prod (str): Le nom du produit dont mettre à jour les composants.
        linked (List[str]): Liste actuelle des composants liés.
        nouveaux (List[str]): Nouvelle liste de composants à lier.

    Notes:
        Cette fonction crée ou supprime les liens entre le produit et les composants
        selon la sélection effectuée par l'utilisateur.
    """
    """Met à jour les composants liés au produit."""
    for comp in set(linked) - set(nouveaux):
        G.remove_edge(prod, comp)
    for comp in set(nouveaux) - set(linked):
        G.add_edge(prod, comp)
    return G

def modifier_produit(
    G: nx.DiGraph
) -> nx.DiGraph:
    """Méthode de personnalisation qui permet à l'utilisateur d'ajuster un produit.

    Args:
        G (Any): Le graphe NetworkX sur lequel modifier les produits et leurs composants.
                Contient des données concernant la personalisation des produits,
                leur niveau, et les opérations liées.

    Notes:
        Cette fonction fournit une interface utilisateur pour sélectionner
        un produit à personnaliser, gérer ses composants, et définir ses opérations
        d'assemblage. Elle implémente la logique de mise à jour des connexions entre
        les différents éléments du graphe.
    """
    st.markdown(f"## {str(_('pages.personnalisation.modify_product'))}")

    # Restauration des produits personnalisés sauvegardés
    if "pages.personnalisation.create_product.fait" not in st.session_state and get_champ_statut("pages.personnalisation.create_product.fait") == "oui":
        index = 0
        while True:
            new_prod = get_champ_statut(f"pages.personnalisation.create_product.{index}.nom")
            if new_prod == "":
                break

            G.add_node(new_prod, niveau="0", personnalisation="oui", label=new_prod)

            sel_new_op = get_champ_statut(f"pages.personnalisation.create_product.{index}.edge")
            if sel_new_op:
                G.add_edge(new_prod, sel_new_op)

            i = 0
            while True:
                comp = get_champ_statut(f"pages.personnalisation.create_product.{index}.composants.{i}")
                if comp == "":
                    break
                G.add_edge(new_prod, comp)
                i += 1

            index += 1

        st.session_state["pages.personnalisation.create_product.fait"] = "oui"

    # Sélection du produit à modifier
    produits0 = get_produits_personnalises(G)

    # Restaurer la sélection du produit depuis la persistance
    if "pages.personnalisation.modify.selected_product" not in st.session_state:
        st.session_state["pages.personnalisation.modify.selected_product"] = get_champ_statut("pages.personnalisation.modify.selected_product")

    sel_display = st.selectbox(
        label=str(_("pages.personnalisation.products_to_modify")),
        options=produits0,
        key="pages.personnalisation.modify.selected_product"
    )

    if sel_display:
        maj_champ_statut("pages.personnalisation.modify.selected_product", sel_display)

    if not sel_display:
        return G

    # Obtention du produit sélectionné
    prod = sel_display

    # Trouver l'index du produit dans la sauvegarde
    index_key = None
    index = 0
    while True:
        saved_name = get_champ_statut(f"pages.personnalisation.create_product.{index}.nom")
        if saved_name == "":
            break
        if saved_name == prod:
            index_key = index
            break
        index += 1

    # Suppression du produit si demandé
    if st.button(f"{str(_('pages.personnalisation.delete'))} {prod}"):
        if index_key is not None:
            # Supprimer les données du produit
            supprime_champ_statut(f"pages.personnalisation.create_product.{index_key}.nom")
            supprime_champ_statut(f"pages.personnalisation.create_product.{index_key}.edge")
            supprime_champ_statut(f"pages.personnalisation.create_product.{index_key}.composants")
            # Réorganiser les indices pour combler le trou
            next_index = index_key + 1
            while get_champ_statut(f"pages.personnalisation.create_product.{next_index}.nom") != "":
                # Déplacer le produit suivant vers l'index actuel
                nom = get_champ_statut(f"pages.personnalisation.create_product.{next_index}.nom")
                edge = get_champ_statut(f"pages.personnalisation.create_product.{next_index}.edge")

                maj_champ_statut(f"pages.personnalisation.create_product.{next_index - 1}.nom", nom)
                if edge:
                    maj_champ_statut(f"pages.personnalisation.create_product.{next_index - 1}.edge", edge)
                else:
                    supprime_champ_statut(f"pages.personnalisation.create_product.{next_index - 1}.edge")

                # Copier les composants
                i = 0
                while True:
                    comp = get_champ_statut(f"pages.personnalisation.create_product.{next_index}.composants.{i}")
                    if comp == "":
                        break
                    maj_champ_statut(f"pages.personnalisation.create_product.{next_index - 1}.composants.{i}", comp)
                    i += 1

                # Supprimer l'ancien emplacement
                supprime_champ_statut(f"pages.personnalisation.create_product.{next_index}")
                next_index += 1
        # Nettoyer aussi les champs de modification
        supprime_champ_statut("pages.personnalisation.modify")
        if "pages.personnalisation.modify.selected_product" in st.session_state:
            del st.session_state["pages.personnalisation.modify.selected_product"]
        if "pages.personnalisation.modify.selected_operation" in st.session_state:
            del st.session_state["pages.personnalisation.modify.selected_operation"]
        if "pages.personnalisation.modify.selected_components" in st.session_state:
            del st.session_state["pages.personnalisation.modify.selected_components"]
        return supprimer_produit(G, prod)

    # Gestion des opérations d'assemblage
    ops_dispo = get_operations_disponibles(G)
    curr_ops = get_operations_actuelles(G, prod)

    # Restaurer la sélection de l'opération depuis la persistance
    if "pages.personnalisation.modify.selected_operation" not in st.session_state:
        saved_op = get_champ_statut("pages.personnalisation.modify.selected_operation")
        if saved_op and saved_op in [str(_("pages.personnalisation.none"))] + ops_dispo:
            st.session_state["pages.personnalisation.modify.selected_operation"] = saved_op
        else:
            default_op = curr_ops[0] if curr_ops else str(_("pages.personnalisation.none"))
            st.session_state["pages.personnalisation.modify.selected_operation"] = default_op

    sel_op = st.selectbox(
        str(_("pages.personnalisation.linked_assembly_operation")),
        [str(_("pages.personnalisation.none"))] + ops_dispo,
        key="pages.personnalisation.modify.selected_operation"
    )

    if sel_op:
        maj_champ_statut("pages.personnalisation.modify.selected_operation", sel_op)

    # Gestion des composants
    niveau1 = get_composants_niveau1(G)
    linked = get_composants_lies(G, prod)

    # Restaurer la sélection des composants depuis la persistance
    if "pages.personnalisation.modify.selected_components" not in st.session_state:
        saved_comps = []
        i = 0
        while True:
            comp = get_champ_statut(f"pages.personnalisation.modify.selected_components.{i}")
            if comp == "":
                break
            if comp in niveau1:
                saved_comps.append(comp)
            i += 1
        st.session_state["pages.personnalisation.modify.selected_components"] = saved_comps if saved_comps else linked

    nouveaux = st.multiselect(
        f"{str(_('pages.personnalisation.components_linked_to'))} {prod}",
        options=niveau1,
        key="pages.personnalisation.modify.selected_components"
    )

    if nouveaux:
        supprime_champ_statut("pages.personnalisation.modify.selected_components")
        for i, comp in enumerate(nouveaux):
            maj_champ_statut(f"pages.personnalisation.modify.selected_components.{i}", comp)

    # Mise à jour des liens si demandé
    if st.button(f"{str(_('pages.personnalisation.update'))} {prod}"):
        G = mettre_a_jour_operations(G, prod, curr_ops, sel_op)
        if index_key is not None:
            if sel_op != str(_("pages.personnalisation.none")):
                maj_champ_statut(f"pages.personnalisation.create_product.{index_key}.edge", sel_op)
            else:
                supprime_champ_statut(f"pages.personnalisation.create_product.{index_key}.edge")

        G = mettre_a_jour_composants(G, prod, linked, nouveaux)
        if index_key is not None:
            if nouveaux:
                supprime_champ_statut(f"pages.personnalisation.create_product.{index_key}.composants")
                for j, comp in enumerate(nouveaux):
                    maj_champ_statut(f"pages.personnalisation.create_product.{index_key}.composants.{j}", comp)
            else:
                supprime_champ_statut(f"pages.personnalisation.create_product.{index_key}.composants")

        st.success(f"{prod} {str(_('pages.personnalisation.updated'))}")
        # Forcer la sauvegarde de l'état
        maj_champ_statut("pages.personnalisation.create_product.fait", "oui")

    return G
