import os
import sys
from networkx.drawing.nx_agraph import read_dot

from .config import (
    REFERENCE_GRAPH_PATH,
    determine_threshold_color,  get_weight_for_color
)

def parse_graphs(graphe_path):
    """
    Charge et analyse les graphes DOT (analyse et référence).
    """
    print(graphe_path)
    # Charger le graphe à analyser
    if not os.path.exists(graphe_path):
        print(f"Fichier de graphe à analyser introuvable: {graphe_path}")
        sys.exit(1)

    # Charger le graphe de référence
    reference_path = REFERENCE_GRAPH_PATH
    if not os.path.exists(reference_path):
        print(f"Fichier de graphe de référence introuvable: {reference_path}")
        sys.exit(1)

    try:
        # Charger les graphes avec NetworkX
        graph = read_dot(graphe_path)
        ref_graph = read_dot(reference_path)

        # Convertir les attributs en types appropriés pour les deux graphes
        for g in [graph, ref_graph]:
            for node, attrs in g.nodes(data=True):
                for key, value in list(attrs.items()):
                    # Convertir les valeurs numériques
                    if key in ['niveau', 'ihh_acteurs', 'ihh_pays', 'isg', 'ivc']:
                        try:
                            if key in ['isg', 'ivc', 'ihh_acteurs', 'ihh_pays', 'niveau']:
                                attrs[key] = int(value.strip('"'))
                            else:
                                attrs[key] = float(value.strip('"'))
                        except (ValueError, TypeError):
                            # Garder la valeur originale si la conversion échoue
                            pass
                    elif key == 'label':
                        # Nettoyer les guillemets des étiquettes
                        attrs[key] = value.strip('"')

            # Convertir les attributs des arêtes
            for u, v, attrs in g.edges(data=True):
                for key, value in list(attrs.items()):
                    if key in ['ics', 'cout', 'delai', 'technique']:
                        try:
                            attrs[key] = float(value.strip('"'))
                        except (ValueError, TypeError):
                            pass
                    elif key == 'label' and '%' in value:
                        # Extraire le pourcentage
                        try:
                            percentage = value.strip('"').replace('%', '')
                            attrs['percentage'] = float(percentage)
                        except (ValueError, TypeError):
                            pass

        return graph, ref_graph

    except Exception as e:
        print(f"Erreur lors de l'analyse des graphes: {str(e)}")
        sys.exit(1)

def extract_data_from_graph(graph, ref_graph):
    """
    Extrait toutes les données pertinentes des graphes DOT.
    """
    data = {
        "products": {},  # Produits finaux (N0)
        "components": {},  # Composants (N1)
        "minerals": {},  # Minerais (N2)
        "operations": {},  # Opérations (N10)
        "countries": {},  # Pays (N11)
        "geo_countries": {},  # Pays géographiques (N99)
        "actors": {}  # Acteurs (N12)
    }

    # Extraire tous les pays géographiques du graphe de référence
    for node, attrs in ref_graph.nodes(data=True):
        if attrs.get('niveau') == 99:
            country_name = attrs.get('label', node)
            isg_value = attrs.get('isg', 0)

            data["geo_countries"][country_name] = {
                "id": node,
                "isg": isg_value
            }

    # Extraire les nœuds du graphe à analyser
    for node, attrs in graph.nodes(data=True):
        level = attrs.get('niveau', -1)
        label = attrs.get('label', node)

        if level == 0 or level == 1000:  # Produit final
            data["products"][node] = {
                "label": label,
                "components": [],
                "assembly": None,
                "level": level
            }
        elif level == 1 or level == 1001:  # Composant
            data["components"][node] = {
                "label": label,
                "minerals": [],
                "manufacturing": None
            }
        elif level == 2:  # Minerai
            data["minerals"][node] = {
                "label": label,
                "ivc": attrs.get('ivc', 0),
                "extraction": None,
                "treatment": None,
                "ics_values": {}
            }
        elif level == 10 or level == 1010:  # Opération
            op_type = label.lower()
            data["operations"][node] = {
                "label": label,
                "type": op_type,
                "ihh_acteurs": attrs.get('ihh_acteurs', 0),
                "ihh_pays": attrs.get('ihh_pays', 0),
                "countries": {}
            }
        elif level == 11 or level == 1011:  # Pays
            data["countries"][node] = {
                "label": label,
                "actors": {},
                "geo_country": None,
                "market_share": 0
            }
        elif level == 12 or level == 1012:  # Acteur
            data["actors"][node] = {
                "label": label,
                "country": None,
                "market_share": 0
            }

    # Extraire les relations et attributs des arêtes
    for source, target, edge_attrs in graph.edges(data=True):
        if source not in graph.nodes or target not in graph.nodes:
            continue

        source_level = graph.nodes[source].get('niveau', -1)
        target_level = graph.nodes[target].get('niveau', -1)

        # Extraire part de marché
        market_share = 0
        if 'percentage' in edge_attrs:
            market_share = edge_attrs['percentage']
        elif 'label' in edge_attrs and '%' in edge_attrs['label']:
            try:
                market_share = float(edge_attrs['label'].strip('"').replace('%', ''))
            except (ValueError, TypeError):
                pass

        # Relations produit → composant
        if (source_level == 0 and target_level == 1) or (source_level == 1000 and target_level == 1001):
            if target not in data["products"][source]["components"]:
                data["products"][source]["components"].append(target)

        # Relations produit → opération (assemblage)
        elif (source_level == 0 and target_level == 10) or (source_level == 1000 and target_level == 1010):
            if graph.nodes[target].get('label', '').lower() == 'assemblage':
                data["products"][source]["assembly"] = target

        # Relations composant → minerai avec ICS
        elif (source_level == 1 or source_level == 1001) and target_level == 2:
            if target not in data["components"][source]["minerals"]:
                data["components"][source]["minerals"].append(target)

            # Stocker l'ICS s'il est présent
            if 'ics' in edge_attrs:
                ics_value = edge_attrs['ics']
                data["minerals"][target]["ics_values"][source] = ics_value

        # Relations composant → opération (fabrication)
        elif (source_level == 1 or source_level == 1001) and target_level == 10:
            if graph.nodes[target].get('label', '').lower() == 'fabrication':
                data["components"][source]["manufacturing"] = target

        # Relations minerai → opération (extraction/traitement)
        elif source_level == 2 and target_level == 10:
            op_label = graph.nodes[target].get('label', '').lower()
            if 'extraction' in op_label:
                data["minerals"][source]["extraction"] = target
            elif 'traitement' in op_label:
                data["minerals"][source]["treatment"] = target

        # Relations opération → pays avec part de marché
        elif (source_level == 10 and target_level == 11) or (source_level == 1010 and target_level == 1011):
            data["operations"][source]["countries"][target] = market_share
            data["countries"][target]["market_share"] = market_share

        # Relations pays → acteur avec part de marché
        elif (source_level == 11 and target_level == 12) or (source_level == 1011 and target_level == 1012):
            data["countries"][source]["actors"][target] = market_share
            data["actors"][target]["market_share"] = market_share
            data["actors"][target]["country"] = source

        # Relations pays → pays géographique
        elif (source_level == 11 or source_level == 1011) and target_level == 99:
            country_name = graph.nodes[target].get('label', '')
            data["countries"][source]["geo_country"] = country_name

    # Compléter les opérations manquantes pour les produits et composants
    # en les récupérant du graphe de référence si elles existent

    # Pour les produits finaux (N0)
    for product_id, product_data in data["products"].items():
        if product_data["assembly"] is None:
            # Chercher l'opération d'assemblage dans le graphe de référence
            for source, target, edge_attrs in ref_graph.edges(data=True):
                if (source == product_id and
                    ((ref_graph.nodes[source].get('niveau') == 0 and
                    ref_graph.nodes[target].get('niveau') == 10) or
                    (ref_graph.nodes[source].get('niveau') == 1000 and
                    ref_graph.nodes[target].get('niveau') == 1010)) and
                    ref_graph.nodes[target].get('label', '').lower() == 'assemblage'):

                    # L'opération existe dans le graphe de référence
                    assembly_id = target
                    product_data["assembly"] = assembly_id

                    # Ajouter l'opération si elle n'existe pas déjà
                    if assembly_id not in data["operations"]:
                        data["operations"][assembly_id] = {
                            "label": ref_graph.nodes[assembly_id].get('label', assembly_id),
                            "type": "assemblage",
                            "ihh_acteurs": ref_graph.nodes[assembly_id].get('ihh_acteurs', 0),
                            "ihh_pays": ref_graph.nodes[assembly_id].get('ihh_pays', 0),
                            "countries": {}
                        }

                    # Extraire les relations de l'opération vers les pays
                    for op_source, op_target, op_edge_attrs in ref_graph.edges(data=True):
                        if (op_source == assembly_id and
                            (ref_graph.nodes[op_target].get('niveau') == 11 or ref_graph.nodes[op_target].get('niveau') == 1011)):

                            country_id = op_target

                            # Extraire part de marché
                            market_share = 0
                            if 'percentage' in op_edge_attrs:
                                market_share = op_edge_attrs['percentage']
                            elif 'label' in op_edge_attrs and '%' in op_edge_attrs['label']:
                                try:
                                    market_share = float(op_edge_attrs['label'].strip('"').replace('%', ''))
                                except (ValueError, TypeError):
                                    pass

                            # Ajouter le pays à l'opération
                            data["operations"][assembly_id]["countries"][country_id] = market_share

                            # Ajouter le pays s'il n'existe pas déjà
                            if country_id not in data["countries"]:
                                data["countries"][country_id] = {
                                    "label": ref_graph.nodes[country_id].get('label', country_id),
                                    "actors": {},
                                    "geo_country": None,
                                    "market_share": market_share
                                }
                            else:
                                data["countries"][country_id]["market_share"] = market_share

                            # Extraire les relations du pays vers les acteurs
                            for country_source, country_target, country_edge_attrs in ref_graph.edges(data=True):
                                if (country_source == country_id and
                                    (ref_graph.nodes[country_target].get('niveau') == 12 or ref_graph.nodes[country_target].get('niveau') == 1012)):

                                    actor_id = country_target

                                    # Extraire part de marché
                                    actor_market_share = 0
                                    if 'percentage' in country_edge_attrs:
                                        actor_market_share = country_edge_attrs['percentage']
                                    elif 'label' in country_edge_attrs and '%' in country_edge_attrs['label']:
                                        try:
                                            actor_market_share = float(country_edge_attrs['label'].strip('"').replace('%', ''))
                                        except (ValueError, TypeError):
                                            pass

                                    # Ajouter l'acteur au pays
                                    data["countries"][country_id]["actors"][actor_id] = actor_market_share

                                    # Ajouter l'acteur s'il n'existe pas déjà
                                    if actor_id not in data["actors"]:
                                        data["actors"][actor_id] = {
                                            "label": ref_graph.nodes[actor_id].get('label', actor_id),
                                            "country": country_id,
                                            "market_share": actor_market_share
                                        }
                                    else:
                                        data["actors"][actor_id]["market_share"] = actor_market_share
                                        data["actors"][actor_id]["country"] = country_id

                            # Extraire la relation du pays vers le pays géographique
                            for geo_source, geo_target, geo_edge_attrs in ref_graph.edges(data=True):
                                if (geo_source == country_id and
                                    ref_graph.nodes[geo_target].get('niveau') == 99):

                                    geo_country_name = ref_graph.nodes[geo_target].get('label', '')
                                    data["countries"][country_id]["geo_country"] = geo_country_name

                    break  # Une seule opération d'assemblage par produit

    # Pour les composants (N1)
    for component_id, component_data in data["components"].items():
        if component_data["manufacturing"] is None:
            # Chercher l'opération de fabrication dans le graphe de référence
            for source, target, edge_attrs in ref_graph.edges(data=True):
                if (source == component_id and
                    ((ref_graph.nodes[source].get('niveau') == 1 and
                    ref_graph.nodes[target].get('niveau') == 10) or
                    (ref_graph.nodes[source].get('niveau') == 1001 and
                    ref_graph.nodes[target].get('niveau') == 1010)) and
                    ref_graph.nodes[target].get('label', '').lower() == 'fabrication'):

                    # L'opération existe dans le graphe de référence
                    manufacturing_id = target
                    component_data["manufacturing"] = manufacturing_id

                    # Ajouter l'opération si elle n'existe pas déjà
                    if manufacturing_id not in data["operations"]:
                        data["operations"][manufacturing_id] = {
                            "label": ref_graph.nodes[manufacturing_id].get('label', manufacturing_id),
                            "type": "fabrication",
                            "ihh_acteurs": ref_graph.nodes[manufacturing_id].get('ihh_acteurs', 0),
                            "ihh_pays": ref_graph.nodes[manufacturing_id].get('ihh_pays', 0),
                            "countries": {}
                        }

                    # Extraire les relations de l'opération vers les pays
                    for op_source, op_target, op_edge_attrs in ref_graph.edges(data=True):
                        if (op_source == manufacturing_id and
                            (ref_graph.nodes[op_target].get('niveau') == 11 or ref_graph.nodes[op_target].get('niveau') == 1011)):

                            country_id = op_target

                            # Extraire part de marché
                            market_share = 0
                            if 'percentage' in op_edge_attrs:
                                market_share = op_edge_attrs['percentage']
                            elif 'label' in op_edge_attrs and '%' in op_edge_attrs['label']:
                                try:
                                    market_share = float(op_edge_attrs['label'].strip('"').replace('%', ''))
                                except (ValueError, TypeError):
                                    pass

                            # Ajouter le pays à l'opération
                            data["operations"][manufacturing_id]["countries"][country_id] = market_share

                            # Ajouter le pays s'il n'existe pas déjà
                            if country_id not in data["countries"]:
                                data["countries"][country_id] = {
                                    "label": ref_graph.nodes[country_id].get('label', country_id),
                                    "actors": {},
                                    "geo_country": None,
                                    "market_share": market_share
                                }
                            else:
                                data["countries"][country_id]["market_share"] = market_share

                            # Extraire les relations du pays vers les acteurs
                            for country_source, country_target, country_edge_attrs in ref_graph.edges(data=True):
                                if (country_source == country_id and
                                    (ref_graph.nodes[country_target].get('niveau') == 12 or ref_graph.nodes[country_target].get('niveau') == 1012)):

                                    actor_id = country_target

                                    # Extraire part de marché
                                    actor_market_share = 0
                                    if 'percentage' in country_edge_attrs:
                                        actor_market_share = country_edge_attrs['percentage']
                                    elif 'label' in country_edge_attrs and '%' in country_edge_attrs['label']:
                                        try:
                                            actor_market_share = float(country_edge_attrs['label'].strip('"').replace('%', ''))
                                        except (ValueError, TypeError):
                                            pass

                                    # Ajouter l'acteur au pays
                                    data["countries"][country_id]["actors"][actor_id] = actor_market_share

                                    # Ajouter l'acteur s'il n'existe pas déjà
                                    if actor_id not in data["actors"]:
                                        data["actors"][actor_id] = {
                                            "label": ref_graph.nodes[actor_id].get('label', actor_id),
                                            "country": country_id,
                                            "market_share": actor_market_share
                                        }
                                    else:
                                        data["actors"][actor_id]["market_share"] = actor_market_share
                                        data["actors"][actor_id]["country"] = country_id

                            # Extraire la relation du pays vers le pays géographique
                            for geo_source, geo_target, geo_edge_attrs in ref_graph.edges(data=True):
                                if (geo_source == country_id and
                                    ref_graph.nodes[geo_target].get('niveau') == 99):

                                    geo_country_name = ref_graph.nodes[geo_target].get('label', '')
                                    data["countries"][country_id]["geo_country"] = geo_country_name

                    break  # Une seule opération de fabrication par composant

    return data

def calculate_vulnerabilities(data, config):
    """
    Calcule les vulnérabilités combinées pour toutes les opérations et minerais.
    """
    thresholds = config.get('thresholds', {})
    results = {
        "ihh_isg_combined": {},  # Pour chaque opération
        "ics_ivc_combined": {},  # Pour chaque minerai
        "chains": []  # Pour stocker tous les chemins possibles
    }

    # 1. Calculer ISG_combiné pour chaque opération
    for op_id, operation in data["operations"].items():
        isg_weighted_sum = 0
        total_share = 0

        # Parcourir chaque pays impliqué dans l'opération
        for country_id, share in operation["countries"].items():
            country = data["countries"][country_id]
            geo_country = country.get("geo_country")

            if geo_country and geo_country in data["geo_countries"]:
                isg_value = data["geo_countries"][geo_country]["isg"]
                isg_weighted_sum += isg_value * share
                total_share += share

        # Calculer la moyenne pondérée
        isg_combined = 0
        if total_share > 0:
            isg_combined = isg_weighted_sum / total_share

        # Déterminer couleurs et poids
        ihh_value = operation["ihh_pays"]
        ihh_color, ihh_suffix = determine_threshold_color(ihh_value, "IHH", thresholds)
        isg_color, isg_suffix = determine_threshold_color(isg_combined, "ISG", thresholds)

        # Calculer poids combiné
        ihh_weight = get_weight_for_color(ihh_color)
        isg_weight = get_weight_for_color(isg_color)
        combined_weight = ihh_weight * isg_weight

        # Déterminer vulnérabilité combinée
        if combined_weight in [6, 9]:
            vulnerability = "ÉLEVÉE à CRITIQUE"
        elif combined_weight in [3, 4]:
            vulnerability = "MOYENNE"
        else:  # 1, 2
            vulnerability = "FAIBLE"

        # Stocker résultats
        results["ihh_isg_combined"][op_id] = {
            "ihh_value": ihh_value,
            "ihh_color": ihh_color,
            "ihh_suffix": ihh_suffix,
            "isg_combined": isg_combined,
            "isg_color": isg_color,
            "isg_suffix": isg_suffix,
            "combined_weight": combined_weight,
            "vulnerability": vulnerability
        }

    # 2. Calculer ICS_moyen pour chaque minerai
    for mineral_id, mineral in data["minerals"].items():
        ics_values = list(mineral["ics_values"].values())
        ics_average = 0

        if ics_values:
            ics_average = sum(ics_values) / len(ics_values)

        ivc_value = mineral.get("ivc", 0)

        # Déterminer couleurs et poids
        ics_color, ics_suffix = determine_threshold_color(ics_average, "ICS", thresholds)
        ivc_color, ivc_suffix = determine_threshold_color(ivc_value, "IVC", thresholds)

        # Calculer poids combiné
        ics_weight = get_weight_for_color(ics_color)
        ivc_weight = get_weight_for_color(ivc_color)
        combined_weight = ics_weight * ivc_weight

        # Déterminer vulnérabilité combinée
        if combined_weight in [6, 9]:
            vulnerability = "ÉLEVÉE à CRITIQUE"
        elif combined_weight in [3, 4]:
            vulnerability = "MOYENNE"
        else:  # 1, 2
            vulnerability = "FAIBLE"

        # Stocker résultats
        results["ics_ivc_combined"][mineral_id] = {
            "ics_average": ics_average,
            "ics_color": ics_color,
            "ics_suffix": ics_suffix,
            "ivc_value": ivc_value,
            "ivc_color": ivc_color,
            "ivc_suffix": ivc_suffix,
            "combined_weight": combined_weight,
            "vulnerability": vulnerability
        }

    # 3. Identifier tous les chemins et leurs vulnérabilités
    for product_id, product in data["products"].items():
        for component_id in product["components"]:
            component = data["components"][component_id]

            for mineral_id in component["minerals"]:
                mineral = data["minerals"][mineral_id]

                # Collecter toutes les vulnérabilités dans ce chemin
                path_vulnerabilities = []

                # Assemblage (si présent)
                assembly_id = product["assembly"]
                if assembly_id and assembly_id in results["ihh_isg_combined"]:
                    path_vulnerabilities.append({
                        "type": "assemblage",
                        "vulnerability": results["ihh_isg_combined"][assembly_id]["vulnerability"],
                        "operation_id": assembly_id
                    })

                # Fabrication (si présent)
                manufacturing_id = component["manufacturing"]
                if manufacturing_id and manufacturing_id in results["ihh_isg_combined"]:
                    path_vulnerabilities.append({
                        "type": "fabrication",
                        "vulnerability": results["ihh_isg_combined"][manufacturing_id]["vulnerability"],
                        "operation_id": manufacturing_id
                    })

                # Minerai (ICS+IVC)
                if mineral_id in results["ics_ivc_combined"]:
                    path_vulnerabilities.append({
                        "type": "minerai",
                        "vulnerability": results["ics_ivc_combined"][mineral_id]["vulnerability"],
                        "mineral_id": mineral_id
                    })

                # Extraction (si présent)
                extraction_id = mineral["extraction"]
                if extraction_id and extraction_id in results["ihh_isg_combined"]:
                    path_vulnerabilities.append({
                        "type": "extraction",
                        "vulnerability": results["ihh_isg_combined"][extraction_id]["vulnerability"],
                        "operation_id": extraction_id
                    })

                # Traitement (si présent)
                treatment_id = mineral["treatment"]
                if treatment_id and treatment_id in results["ihh_isg_combined"]:
                    path_vulnerabilities.append({
                        "type": "traitement",
                        "vulnerability": results["ihh_isg_combined"][treatment_id]["vulnerability"],
                        "operation_id": treatment_id
                    })

                # Classifier le chemin
                path_info = {
                    "product": product_id,
                    "component": component_id,
                    "mineral": mineral_id,
                    "vulnerabilities": path_vulnerabilities
                }

                # Déterminer le niveau de risque du chemin
                critical_count = path_vulnerabilities.count({"vulnerability": "ÉLEVÉE à CRITIQUE"})
                medium_count = path_vulnerabilities.count({"vulnerability": "MOYENNE"})

                if any(v["vulnerability"] == "ÉLEVÉE à CRITIQUE" for v in path_vulnerabilities):
                    path_info["risk_level"] = "critique"
                elif medium_count >= 3:
                    path_info["risk_level"] = "majeur"
                elif any(v["vulnerability"] == "MOYENNE" for v in path_vulnerabilities):
                    path_info["risk_level"] = "moyen"
                else:
                    path_info["risk_level"] = "faible"

                results["chains"].append(path_info)

    return results
