import pandas as pd
import networkx as nx
import matplotlib.pyplot as plt
import io
import os

# 1. Loading the VOSviewer Keyword Dictionary with Clusters and Colors
kw_data = """id	keyword	occurrences	total link strength	Cluster	color
23	agriculture	55	93	1	#0065B3
397	innovation	34	72	1	#0065B3
712	systems	32	53	1	#0065B3
328	framework	16	28	1	#0065B3
11	adoption	13	24	1	#0065B3
487	networks	10	21	1	#0065B3
452	management	50	105	2	#BCBD22
89	business models	33	55	2	#BCBD22
312	food	20	36	2	#BCBD22
549	policy	13	30	2	#BCBD22
340	governance	10	13	2	#BCBD22
705	sustainable development	10	19	2	#BCBD22
701	sustainability	32	65	3	#D62728
375	impact	21	35	3	#D62728
289	farmers	19	27	3	#D62728
527	performance	15	34	3	#D62728
710	system	13	28	3	#D62728
124	climate-change	12	29	3	#D62728
259	energy	12	30	3	#D62728
714	technologies	11	21	3	#D62728
107	challenges	20	44	4	#2CA02C
441	life-cycle assessment	12	20	4	#2CA02C
202	design	11	26	4	#2CA02C
330	future	11	30	4	#2CA02C
376	impacts	11	25	4	#2CA02C
241	ecosystem services	10	22	4	#2CA02C
502	opportunities	9	19	4	#2CA02C"""

# 2. Loading the Incidence Matrix (Document-Keyword Relationship)
matrix_data = """document id	keyword id	frequency
1	549	1
8	23	1
8	289	1
8	701	1
10	712	1
11	23	1
11	340	1
11	452	1
11	527	1
13	23	1
13	710	1
15	289	1
15	376	1
15	487	1
17	11	1
17	328	1
18	340	1
19	23	1
20	452	1
20	487	1
21	23	1
21	397	1
22	328	1
22	712	1
23	328	1
23	701	1
24	89	1
24	705	1
26	23	1
26	89	1
26	452	1
27	441	1
28	259	1
28	452	1
29	340	1
29	452	1
30	89	1
30	701	1
32	107	1
32	259	1
33	328	1
34	23	1
34	89	1
34	441	1
34	712	1
35	89	1
35	701	1
36	527	1
37	89	1
37	202	1
37	452	1
38	312	1
38	712	1
39	89	1
39	107	1
39	375	1
39	714	1
40	312	1
42	397	1
42	527	1
43	241	1
43	441	1
44	202	1
44	502	1
44	710	1
45	23	1
45	487	1
45	502	1
46	107	1
46	452	1
47	23	1
48	289	1
49	89	1
49	241	1
50	23	1
50	328	1
50	549	1
51	11	1
52	107	1
52	202	1
52	312	1
52	340	1
52	452	1
52	549	1
54	202	1
54	376	1
55	11	1
55	330	1
55	712	1
55	714	1
56	701	1
57	340	1
58	527	1
58	705	1
59	340	1
60	23	1
60	376	1
60	701	1
61	23	1
61	452	1
61	487	1
61	705	1
64	89	1
64	701	1
65	89	1
65	397	1
65	705	1
65	712	1
67	452	1
68	89	1
69	89	1
69	312	1
70	89	1
70	328	1
70	397	1
71	107	1
72	202	1
72	375	1
73	452	1
73	549	1
73	712	1
74	289	1
74	375	1
74	452	1
75	23	1
75	312	1
75	452	1
75	712	1
77	375	1
77	397	1
77	452	1
78	397	1
81	11	1
81	23	1
82	124	1
82	701	1
82	714	1
83	107	1
83	241	1
83	330	1
83	376	1
85	376	1
85	452	1
85	549	1
85	701	1
85	710	1
86	328	1
86	330	1
87	23	1
87	549	1
87	701	1
88	89	1
88	701	1
89	107	1
89	330	1
90	375	1
92	23	1
94	23	1
95	452	1
95	487	1
95	712	1
96	23	1
96	312	1
96	452	1
97	312	1
97	397	1
98	107	1
98	330	1
98	397	1
98	710	1
100	527	1
101	328	1
101	712	1
103	23	1
103	107	1
103	328	1
103	397	1
105	23	1
105	312	1
105	330	1
105	397	1
105	441	1
105	701	1
107	23	1
107	549	1
108	328	1
108	340	1
108	549	1
108	705	1
109	487	1
109	712	1
112	89	1
112	124	1
112	549	1
112	701	1
114	23	1
114	452	1
114	701	1
115	23	1
115	397	1
117	124	1
117	312	1
117	527	1
117	701	1
118	23	1
118	89	1
118	549	1
119	23	1
119	241	1
120	312	1
120	452	1
122	89	1
122	714	1
123	23	1
123	124	1
124	89	1
124	259	1
124	441	1
126	441	1
126	452	1
127	375	1
127	397	1
128	11	1
128	452	1
128	549	1
129	376	1
129	441	1
130	89	1
130	549	1
131	330	1
131	712	1
133	714	1
134	441	1
135	89	1
135	375	1
135	452	1
136	487	1
136	701	1
136	712	1
137	11	1
137	23	1
137	712	1
138	289	1
138	375	1
141	452	1
141	487	1
141	527	1
141	701	1
142	312	1
142	375	1
142	712	1
143	107	1
143	202	1
143	241	1
143	452	1
144	11	1
144	452	1
144	712	1
145	289	1
145	375	1
146	441	1
147	23	1
148	11	1
148	23	1
148	107	1
148	397	1
149	89	1
149	452	1
149	527	1
150	23	1
150	487	1
150	712	1
152	23	1
152	452	1
152	710	1
152	714	1
153	259	1
153	375	1
153	714	1
156	452	1
156	705	1
158	23	1
158	710	1
160	124	1
160	289	1
160	375	1
161	124	1
161	376	1
162	89	1
162	202	1
166	701	1
167	23	1
167	124	1
167	328	1
167	452	1
168	397	1
168	452	1
168	710	1
169	107	1
170	89	1
170	289	1
170	452	1
171	202	1
171	289	1
171	712	1
172	23	1
173	89	1
173	452	1
173	502	1
173	705	1
174	89	1
174	124	1
174	705	1
175	202	1
175	397	1
175	452	1
176	202	1
176	397	1
176	452	1
176	502	1
177	701	1
179	259	1
179	441	1
179	701	1
180	312	1
181	312	1
182	124	1
182	452	1
182	527	1
183	312	1
183	452	1
183	705	1
184	259	1
184	289	1
184	375	1
184	527	1
186	502	1
188	107	1
188	397	1
188	452	1
189	712	1
191	397	1
191	701	1
191	712	1
193	376	1
193	527	1
194	312	1
195	340	1
196	705	1
197	23	1
197	259	1
197	701	1
198	23	1
198	259	1
198	397	1
198	452	1
198	527	1
198	701	1
199	89	1
199	107	1
199	397	1
199	452	1
201	712	1
204	340	1
204	452	1
205	375	1
207	289	1
209	441	1
209	701	1
210	89	1
210	397	1
210	527	1
210	710	1
211	107	1
211	701	1
211	712	1
212	23	1
212	710	1
213	23	1
213	714	1
214	375	1
214	452	1
216	107	1
216	502	1
220	712	1
221	289	1
221	328	1
225	23	1
226	23	1
227	289	1
228	312	1
229	107	1
230	107	1
230	202	1
230	241	1
230	441	1
230	502	1
231	23	1
232	452	1
233	124	1
233	259	1
233	312	1
233	376	1
233	701	1
233	712	1
234	328	1
234	712	1
236	89	1
237	124	1
237	710	1
238	375	1
238	452	1
239	23	1
245	23	1
245	330	1
245	397	1
245	452	1
248	241	1
249	241	1
249	259	1
249	330	1
249	452	1
252	23	1
252	289	1
252	712	1
254	11	1
254	289	1
257	289	1
258	89	1
258	328	1
258	397	1
259	107	1
260	375	1
261	11	1
261	312	1
261	375	1
261	452	1
261	527	1
261	710	1
262	89	1
263	312	1
264	289	1
264	375	1
264	397	1
264	549	1
265	11	1
265	23	1
265	328	1
265	397	1
268	23	1
268	714	1
270	712	1
271	23	1
271	397	1
273	11	1
273	502	1
274	712	1
279	397	1
281	397	1
281	527	1
281	701	1
282	452	1
283	375	1
283	397	1
283	714	1
284	23	1
284	241	1
284	487	1
284	701	1
288	312	1
289	89	1
289	241	1
289	289	1
289	712	1
290	23	1
292	397	1
292	701	1
295	23	1
297	23	1
297	328	1
297	330	1
297	397	1
299	11	1
302	89	1
304	23	1
305	452	1
307	89	1
307	710	1
309	397	1
309	452	1
310	289	1
311	23	1
311	107	1
311	124	1
311	330	1
311	376	1
313	259	1
313	502	1
313	701	1
313	714	1
314	23	1
314	701	1
314	712	1
315	340	1
318	701	1
319	259	1
319	710	1
320	712	1
324	397	1
325	23	1
325	397	1
325	452	1
325	712	1"""

# 3. Process Tables and Indexes in Dictionaries
df_kws = pd.read_csv(io.StringIO(kw_data), sep="\t")
df_matrix = pd.read_csv(io.StringIO(matrix_data), sep="\t")

# 4. Create Concept Mapping Dictionaries
id_to_name = dict(zip(df_kws['id'], df_kws['keyword']))
id_to_cluster = dict(zip(df_kws['id'], df_kws['Cluster']))
id_to_color = dict(zip(df_kws['id'], df_kws['color']))

# 5. Building the unified co-occurrence network from the shared documents
doc_to_kw = df_matrix.groupby('document id')['keyword id'].apply(list).to_dict()
G = nx.Graph()
for kws in doc_to_kw.values():
    for i in range(len(kws)):
        G.add_node(kws[i])
        for j in range(i + 1, len(kws)):
            if G.has_edge(kws[i], kws[j]):
                G[kws[i]][kws[j]]['weight'] += 1
            else:
                G.add_edge(kws[i], kws[j], weight=1)

# 6. Group the nodes strictly according to the VOSviewer assignments provided
clusters = {}
cluster_colors = {}
for _, row in df_kws.iterrows():
    kw_id = int(row['id'])
    c_id = int(row['Cluster'])
    hex_color = row['color']
    
    clusters.setdefault(c_id, []).append(kw_id)
    cluster_colors[c_id] = hex_color

# 7. Attack Simulation Function
def simular_ataque_subgrafo(subgrafo_original):
    H = subgrafo_original.copy()
    n_inicial = H.number_of_nodes()
    
    eje_x_porcentaje = [0.0]
    eje_y_lcc = [1.0]
    
    while H.number_of_nodes() > 1:
        # ROOT SOLUTION: max() finds the tuple with the highest degree,
        # but we add [0] at the end to extract ONLY the ID number.
        nodo_ataque = max(H.degree, key=lambda x: x)[0]
        H.remove_node(nodo_ataque)
        
        pct_eliminado = ((n_inicial - H.number_of_nodes()) / n_inicial) * 100
        componentes = sorted(nx.connected_components(H), key=len, reverse=True)
        tamano_lcc = len(componentes) / n_inicial if componentes else 0
        
        eje_x_porcentaje.append(pct_eliminado)
        eje_y_lcc.append(tamano_lcc)
        
    return eje_x_porcentaje, eje_y_lcc

# 8. Calculate the Area Under the Curve (Quantitative Measure of Robustness)
def calcular_auc(x, y):
    auc = 0.0
    for i in range(len(x) - 1):
        dx = (x[i+1] - x[i]) / 100.0
        promedio_y = (y[i+1] + y[i]) / 2.0
        auc += dx * promedio_y
    return auc

# 9. Initialize and process the comparative graph
plt.figure(figsize=(11, 7))

print("=== REPORT ROBUSTNESS BY CLUSTER ===")
for clus_id in sorted(clusters.keys()):
    nodos = clusters[clus_id]
    
    # Filter only the nodes that actually exist in the link matrix
    nodos_validos = [n for n in nodos if n in G]
    if not nodos_validos:
        continue
        
    sub_G = G.subgraph(nodos_validos).copy()
    
    # Extract the leading words (with the highest internal degree) from the subfield to be tagged
    grados_internos = sorted(sub_G.degree, key=lambda x: x, reverse=True)
    top_words = [id_to_name[n] for n, grad in grados_internos[:3] if n in id_to_name]
    
    # Run the collapse simulation
    x_vals, y_vals = simular_ataque_subgrafo(sub_G)
    
    # Calculate the AUC
    score_auc = calcular_auc(x_vals, y_vals)
    
    # Print a clean text-only report to the console
    print(f"Cluster {clus_id} | Color VOS: {cluster_colors[clus_id]} | Keywords Core: {top_words} | Robustness Index (AUC): {score_auc:.3f}")
    
    # Render the lines using the exact hexadecimal color assigned in VOSviewer
    label_cluster = f"Cluster {clus_id} (AUC: {score_auc:.2f}) -> {', '.join(top_words)}"
    plt.plot(x_vals, y_vals, label=label_cluster, color=cluster_colors[clus_id], linewidth=1, marker='o', markersize=4)

# 10. High-quality formatting and layout (300 DPI) for scientific publications.
# Customize the title: "Keywords Plus©" "KWP" OR "Author Keywords" "AKW".
plt.xlabel("Percentage of Keywords Removed (Within Their Own Cluster)", fontsize=11, fontweight='bold', labelpad=10)
plt.ylabel("Loss of Conceptual Consistency (LCC %)", fontsize=11, fontweight='bold', labelpad=10)
plt.title("Empirical Robustness Analysis by Thematic Cluster\n(Collapse Curves Based on Keywords Plus© from VOSviewer)", fontsize=12, fontweight='bold', pad=15)
plt.legend(loc="upper right", frameon=True, facecolor='#f7f7f7', edgecolor='gray', framealpha=0.9, shadow=True, fontsize=9)
plt.grid(True, linestyle="--", alpha=0.5)
plt.xlim(-3, 103)
plt.ylim(-0.05, 1.05)
plt.tight_layout()
# 11. Save image automatically in high definition
plt.savefig("robustez_clusters_vosviewer.png", dpi=300)
plt.show()