import matplotlib.pyplot as plt
import networkx as nx
import numpy as np
import pandas as pd

# ===== INPUT =====
# rows = source page, cols = destination page
pages = ["A", "B", "C", "D"]
A = np.array(
    [
        [0, 1, 1, 0],  # A -> B,C
        [0, 0, 1, 0],  # B -> C
        [1, 0, 0, 0],  # C -> A
        [0, 0, 1, 0],  # D -> C
    ],
    dtype=int,
)

d = 0.85
max_iter = 10
tol = 1e-8

# ===== PREP =====
n = len(pages)
base = (1 - d) / n
adj = pd.DataFrame(A, index=pages, columns=pages)

incoming = {p: [src for src in pages if adj.loc[src, p] == 1] for p in pages}
outgoing = {p: int(adj.loc[p].sum()) for p in pages}
pr = {p: 1 / n for p in pages}

print("Step 1: Web Graph")
print(adj)
print("\nTotal pages N =", n)
print("\nInitial PageRank:")
print(pd.DataFrame({"Page": pages, "Initial PR": [pr[p] for p in pages]}))

print("\nStep 2: Incoming Links")
print(
    pd.DataFrame(
        {
            "Page": pages,
            "Incoming Links": [
                ", ".join(incoming[p]) if incoming[p] else "—" for p in pages
            ],
        }
    )
)

print("\nOutgoing Links Count")
print(pd.DataFrame({"Page": pages, "Outgoing Links": [outgoing[p] for p in pages]}))

# ===== ITERATIONS =====
history = [pr.copy()]

for it in range(1, max_iter + 1):
    new_pr = {}
    print(f"\n\n===== Iteration {it} =====")
    for p in pages:
        s = 0
        terms, nums = [], []
        for src in incoming[p]:
            c = pr[src] / outgoing[src] if outgoing[src] > 0 else 0
            s += c
            terms.append(f"PR({src})/{outgoing[src]}")
            nums.append(f"{pr[src]:.4f}/{outgoing[src]}={c:.4f}")
        new_pr[p] = base + d * s

        print(f"\nPage {p}")
        print("Incoming links:", ", ".join(incoming[p]) if incoming[p] else "—")
        if terms:
            print(f"PR({p}) = {base:.4f} + {d} * (" + " + ".join(terms) + ")")
            print("Substitution:", " + ".join(nums))
        else:
            print(f"PR({p}) = {base:.4f}")
        print(f"PR({p}) = {new_pr[p]:.6f}")

    history.append(new_pr.copy())
    diff = sum(abs(new_pr[p] - pr[p]) for p in pages)
    print("\nPageRank after this iteration:")
    print(
        pd.DataFrame(
            {"Page": pages, f"PR Iteration {it}": [round(new_pr[p], 6) for p in pages]}
        )
    )
    pr = new_pr
    if diff < tol:
        break

# ===== FINAL RESULT =====
print("\n\nFinal PageRank:")
final_df = pd.DataFrame(
    {"Page": pages, "Final PR": [pr[p] for p in pages]}
).sort_values("Final PR", ascending=False)
print(final_df)

# ===== OPTIONAL VISUALIZATION =====
G = nx.DiGraph()
for i, src in enumerate(pages):
    for j, dst in enumerate(pages):
        if A[i, j] == 1:
            G.add_edge(src, dst)

plt.figure(figsize=(6, 4))
pos = nx.spring_layout(G, seed=42)
sizes = [pr[p] * 5000 + 800 for p in G.nodes()]
nx.draw(G, pos, with_labels=True, node_size=sizes, arrows=True)
plt.title("Graph Visualization (node size ~ Final PageRank)")
plt.show()
