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
1115	sustainability	53	71	1	#D62728
72	agriculture	35	44	1	#D62728
193	business model	23	27	1	#D62728
374	digital agriculture	8	5	1	#D62728
197	business model innovation	7	13	1	#D62728
395	digitalization	6	8	1	#D62728
94	agroecology	5	3	1	#D62728
171	blockchain	5	3	1	#D62728
840	organic farming	5	5	1	#D62728
946	resilience	5	11	1	#D62728
1228	value chain	5	8	1	#D62728
200	business models	21	26	2	#2CA02C
1128	sustainable agriculture	21	13	2	#2CA02C
1131	sustainable business models	12	14	2	#2CA02C
975	rural development	10	8	2	#2CA02C
526	food security	9	14	2	#2CA02C
939	renewable energy	9	11	2	#2CA02C
928	regenerative agriculture	5	6	2	#2CA02C
1216	urban agriculture	14	11	3	#1F77B4
638	innovation	11	16	3	#1F77B4
250	climate change	9	15	3	#1F77B4
531	food system	5	7	3	#1F77B4
534	food systems	5	7	3	#1F77B4
1136	sustainable development goals	5	7	3	#1F77B4
1135	sustainable development	26	27	4	#BCBD22
391	digital transformation	6	5	4	#BCBD22
458	environmental sustainability	6	7	4	#BCBD22
1066	soil health	5	7	4	#BCBD22
232	circular economy	26	24	5	#9467BD
1108	supply chain	6	9	5	#9467BD
1252	waste management	5	8	5	#9467BD"""

# 2. Loading the Incidence Matrix (Document-Keyword Relationship)
matrix_data = """document id	keyword id	frequency
1	975	1
3	72	1
3	1135	1
4	1115	1
5	1216	1
6	1135	1
7	1128	1
8	193	1
9	1128	1
11	1135	1
12	193	1
14	200	1
14	1128	1
15	72	1
16	72	1
18	200	1
19	1131	1
20	975	1
21	1131	1
21	1216	1
23	1115	1
26	1135	1
28	193	1
28	232	1
28	1252	1
29	1128	1
29	1228	1
31	72	1
32	193	1
33	1115	1
34	1115	1
34	1136	1
34	1216	1
35	1108	1
35	1135	1
36	1128	1
36	1135	1
37	72	1
37	1115	1
37	1131	1
39	250	1
39	939	1
41	72	1
42	232	1
42	1135	1
43	531	1
43	1115	1
43	1216	1
44	200	1
45	200	1
46	200	1
48	94	1
48	1128	1
49	638	1
49	1115	1
51	391	1
54	1136	1
56	200	1
56	1115	1
61	197	1
61	200	1
61	526	1
61	1135	1
62	72	1
63	534	1
63	1135	1
65	638	1
65	1128	1
66	232	1
69	72	1
70	1115	1
71	72	1
71	391	1
71	1115	1
72	72	1
73	374	1
73	1228	1
74	72	1
74	197	1
74	946	1
74	1115	1
74	1228	1
75	94	1
75	946	1
76	1128	1
77	1135	1
77	1136	1
78	232	1
82	250	1
82	534	1
82	638	1
84	1128	1
86	72	1
86	197	1
86	1115	1
90	72	1
91	939	1
92	526	1
92	1131	1
92	1135	1
93	193	1
94	1131	1
95	1115	1
96	250	1
96	531	1
96	1228	1
97	193	1
97	840	1
98	72	1
98	1135	1
100	197	1
101	72	1
103	232	1
104	458	1
104	1115	1
104	1135	1
105	193	1
105	1216	1
106	200	1
106	526	1
110	1108	1
111	1135	1
113	171	1
115	840	1
115	1115	1
116	1135	1
117	193	1
117	638	1
119	94	1
120	374	1
121	171	1
123	232	1
124	1216	1
125	1115	1
126	975	1
126	1115	1
129	1128	1
134	1115	1
134	1216	1
136	1115	1
137	939	1
139	1115	1
139	1216	1
142	939	1
142	975	1
142	1131	1
143	458	1
143	1066	1
143	1135	1
144	391	1
145	72	1
145	250	1
145	526	1
145	939	1
146	232	1
146	1108	1
146	1115	1
147	72	1
147	526	1
148	171	1
148	1115	1
149	171	1
149	1115	1
152	1115	1
152	1135	1
153	232	1
153	939	1
153	1115	1
154	232	1
155	72	1
155	1135	1
156	395	1
157	72	1
157	193	1
157	395	1
157	638	1
158	946	1
158	1115	1
161	1115	1
162	232	1
162	1128	1
163	193	1
163	374	1
165	1128	1
165	1135	1
167	1135	1
168	232	1
168	1252	1
169	1128	1
170	72	1
170	1115	1
173	232	1
174	1135	1
175	171	1
176	193	1
176	1128	1
177	200	1
178	193	1
178	638	1
178	1115	1
178	1136	1
179	72	1
179	200	1
179	232	1
179	1252	1
180	1228	1
182	1108	1
182	1115	1
183	72	1
183	200	1
183	1115	1
183	1131	1
184	1128	1
186	374	1
187	928	1
188	72	1
188	197	1
189	526	1
189	975	1
190	395	1
191	928	1
192	72	1
192	193	1
193	250	1
194	1131	1
197	232	1
198	526	1
198	1115	1
199	232	1
199	1108	1
200	638	1
202	1066	1
203	458	1
203	1135	1
204	200	1
204	1128	1
205	1131	1
206	374	1
207	975	1
208	374	1
209	72	1
211	840	1
212	395	1
212	1115	1
214	72	1
214	1131	1
215	200	1
217	72	1
217	200	1
217	534	1
219	1115	1
220	94	1
221	200	1
222	1216	1
223	72	1
224	232	1
226	232	1
226	1115	1
227	72	1
227	250	1
227	1115	1
228	200	1
228	975	1
229	200	1
229	1115	1
230	458	1
230	1252	1
231	1135	1
232	232	1
232	1115	1
233	1216	1
234	975	1
236	232	1
237	939	1
237	1135	1
238	94	1
238	1115	1
239	975	1
239	1115	1
241	391	1
241	458	1
245	72	1
245	531	1
246	1115	1
246	1252	1
247	1128	1
249	531	1
249	638	1
250	250	1
250	1216	1
251	928	1
251	1131	1
253	939	1
256	458	1
257	1115	1
258	232	1
259	232	1
259	1128	1
261	193	1
261	232	1
261	250	1
261	1108	1
263	72	1
263	1115	1
264	200	1
264	1115	1
265	638	1
266	939	1
266	975	1
266	1128	1
267	928	1
267	1131	1
268	534	1
268	1115	1
275	391	1
275	1135	1
276	1128	1
278	1066	1
279	232	1
280	72	1
280	193	1
281	193	1
281	1135	1
282	200	1
282	638	1
282	928	1
282	1066	1
282	1115	1
283	250	1
283	531	1
284	534	1
284	1216	1
285	395	1
285	526	1
287	72	1
287	1115	1
288	193	1
288	1115	1
289	1216	1
290	193	1
290	197	1
292	72	1
292	200	1
294	193	1
294	840	1
294	946	1
294	1115	1
295	526	1
298	193	1
301	193	1
301	374	1
302	200	1
302	1216	1
305	232	1
305	395	1
305	1115	1
306	1128	1
308	1115	1
310	638	1
311	72	1
311	946	1
311	1115	1
312	232	1
313	193	1
313	232	1
314	840	1
315	1136	1
319	1115	1
323	193	1
323	1066	1
324	197	1
324	374	1
325	391	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 Author Keywords 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()