{% extends "base.html" %}

{% block head %}
<script src="https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js"></script>
{% endblock %}

{% block css %}
#mynetwork {
    width: 100%;
    height: 100vh;
    border: 1px solid #ddd;
    background-color: #f8f9fa;
    border-radius: 10px;
    box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}

/* Search UI */
.search-container {
    position: absolute;
    bottom: 100px;
    left: 10px;
    z-index: 10;
    display: flex;
    flex-direction: column;
    gap: 5px;
}
.search-container input,
.search-container select,
.search-container button {
    padding: 8px;
    border-radius: 8px;
    border: 1px solid #ccc;
}
.search-container button {
    background-color: #007bff;
    color: white;
    cursor: pointer;
}
.search-container button:hover {
    background-color: #0056b3;
}

/* Node info box */
#node-info {
    position: absolute;
    bottom: 10px;
    right: 10px;
    background-color: rgba(255,255,255,0.95);
    padding: 12px;
    border-radius: 8px;
    border: 1px solid #ccc;
    box-shadow: 0 2px 6px rgba(0,0,0,0.1);
    font-size: 14px;
    color: #333;
    width: 260px;
    max-height: 250px;
    overflow-y: auto;
}

/* Legend */
#legend {
    position: absolute;
    bottom: 10px;
    left: 10px;
    background: rgba(255,255,255,0.9);
    padding: 10px;
    border-radius: 5px;
    border: 1px solid #ccc;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    font-size: 14px;
    display: flex;
    color: #333;
}
.legend-category {
    margin-right: 10px;
}
.legend-box {
    display: inline-block;
    width: 12px;
    height: 12px;
    margin-right: 5px;
    border-radius: 3px;
}
.circle { border-radius: 6px; }
{% endblock %}

{% block body %}
<div id="mynetwork"></div>

<!-- SEARCH + FILTER -->
<div class="search-container">
    <label style="color:#333;">Channel:</label>
    <select id="channel-select" onchange="filterByChannel()"></select>

    <input type="text" id="node-search" placeholder="Search node...">
    <button onclick="searchNode()">Search</button>
</div>

<!-- INFO BOX -->
<div id="node-info">
    <b>Long Name:</b> <span id="node-long-name"></span><br>
    <b>Short Name:</b> <span id="node-short-name"></span><br>
    <b>Role:</b> <span id="node-role"></span><br>
    <b>Hardware Model:</b> <span id="node-hw-model"></span>
</div>

<!-- LEGEND -->
<div id="legend">
    <div class="legend-category">
        <div><span class="legend-box" style="background-color: #ff5733"></span>Traceroute</div>
        <div><span class="legend-box" style="background-color: #049acd"></span>Neighbor</div>
    </div>
    <div class="legend-category">
        <div><span class="legend-box circle" style="background-color: #ff5733"></span><code>ROUTER</code></div>
        <div><span class="legend-box circle" style="background-color: #b65224"></span><code>ROUTER_LATE</code></div>
    </div>
    <div class="legend-category">
        <div><span class="legend-box circle" style="background-color: #007bff"></span><code>CLIENT</code></div>
        <div><span class="legend-box circle" style="background-color: #00c3ff"></span><code>CLIENT_MUTE</code></div>
    </div>
    <div class="legend-category">
        <div><span class="legend-box circle" style="background-color: #049acd"></span><code>CLIENT_BASE</code></div>
        <div><span class="legend-box circle" style="background-color: #ffbf00"></span>Other</div>
    </div>
    <div class="legend-category">
        <div><span class="legend-box circle" style="background-color: #6c757d"></span>Unknown</div>
    </div>
</div>

<script>
// Initialize ECharts
const chart = echarts.init(document.getElementById("mynetwork"));

// -----------------------------------
// COLOR + ROLE HELPERS
// -----------------------------------
const colors = {
    edge: { traceroute:"#ff5733", neighbor:"#049acd" },
    role: {
        ROUTER:"#ff5733",
        ROUTER_LATE:"#b65224",
        CLIENT:"#007bff",
        CLIENT_MUTE:"#00c3ff",
        CLIENT_BASE:"#049acd",
        other:"#ffbf00",
        unknown:"#6c757d"
    },
    selection:"#ff8c00"
};

function getRoleColor(role) { return colors.role[role] || colors.role.other; }
function getSymbolSize(role) {
    switch(role){
        case "ROUTER":
        case "ROUTER_LATE": return 30;
        case "CLIENT_BASE": return 18;
        case "CLIENT": return 15;
        case "CLIENT_MUTE": return 7;
        default: return 15;
    }
}
function getLabel(role, shortName, longName) {
    if (role === "ROUTER" || role === "ROUTER_LATE") return longName;
    return shortName || "";
}

// -----------------------------------
// STATE
// -----------------------------------
let nodes = [];
let edges = [];
let filteredNodes = [];
let filteredEdges = [];
let lastSelectedNode = null;
let selectedChannel = null;

// -----------------------------------
// LOAD NODES + EDGES FROM API
// -----------------------------------
async function loadData() {

    // 1. Load nodes
    const n = await fetch("/api/nodes").then(r => r.json());
    nodes = n.nodes.map(x => ({
        name: String(x.node_id),
        node_id: x.node_id,
        long_name: x.long_name,
        short_name: x.short_name,
        hw_model: x.hw_model,
        role: x.role,
        channel: x.channel,
        labelValue: getLabel(x.role, x.short_name, x.long_name),
        symbolSize: getSymbolSize(x.role),
        itemStyle: {
            color: getRoleColor(x.role),
            opacity: 1
        },
        label: {
            show: true,
            position: "right",
            color: "#333",
            fontSize: 12,
            formatter: p => p.data.labelValue
        }
    }));

    const allNodeIDs = new Set(nodes.map(n => n.name));

    // 2. Load edges
    const e = await fetch("/api/edges").then(r => r.json());

    // Only keep edges that reference valid nodes
    edges = e.edges
        .filter(ed =>
            allNodeIDs.has(String(ed.from)) &&
            allNodeIDs.has(String(ed.to))
        )
        .map(ed => ({
            source: String(ed.from),
            target: String(ed.to),
            edgeType: ed.type,
            originalColor: colors.edge[ed.type] || "#ccc",
            lineStyle: {
                width: 2,
                color: colors.edge[ed.type] || "#ccc",
                opacity: 1
            }
        }));

    // 3. Determine which nodes are actually used in edges
    const usedNodeIDs = new Set();
    edges.forEach(e => {
        usedNodeIDs.add(e.source);
        usedNodeIDs.add(e.target);
    });

    // 4. Remove unused (no-edge) nodes
    nodes = nodes.filter(n => usedNodeIDs.has(n.name));

    // 5. Double safety: remove any edges referencing removed nodes
    edges = edges.filter(e =>
        usedNodeIDs.has(e.source) && usedNodeIDs.has(e.target)
    );

    // 6. Now ready to build dropdown & render
    populateChannelDropdown();
}


// -----------------------------------
// CHANNEL FILTER
// -----------------------------------
function populateChannelDropdown() {
    const sel = document.getElementById("channel-select");
    const chans = [...new Set(nodes.map(n => n.channel))].sort();

    chans.forEach(ch => {
        const opt = document.createElement("option");
        opt.value = ch;
        opt.text = ch;
        sel.appendChild(opt);
    });

    selectedChannel = chans[0];
    sel.value = selectedChannel;

    filterByChannel();
}

function filterByChannel() {
    selectedChannel = document.getElementById("channel-select").value;

    filteredNodes = nodes.filter(n => n.channel === selectedChannel);

    const allowed = new Set(filteredNodes.map(n => n.name));
    filteredEdges = edges.filter(e => allowed.has(e.source) && allowed.has(e.target));

    lastSelectedNode = null;
    updateChart();
}

// -----------------------------------
// FORCE GRAPH UPDATE
// -----------------------------------
function updateChart() {
    const updatedNodes = filteredNodes.map(n => {
        let opacity = 1;
        let borderColor = "transparent";

        if (lastSelectedNode) {
            const connected = filteredEdges.some(
                e => (e.source === n.name && e.target === lastSelectedNode) ||
                     (e.target === n.name && e.source === lastSelectedNode)
            );
            if (n.name === lastSelectedNode) {
                borderColor = colors.selection;
            } else if (!connected) {
                opacity = 0.3;
            }
        }

        return {
            ...n,
            itemStyle: { ...n.itemStyle, opacity, borderColor, borderWidth: 6 }
        };
    });

    const updatedEdges = filteredEdges.map(e => {
        const connected =
            lastSelectedNode &&
            (e.source === lastSelectedNode || e.target === lastSelectedNode);

        return {
            ...e,
            lineStyle: { ...e.lineStyle, opacity: connected ? 1 : 0.1 }
        };
    });

    chart.setOption({
        animation: false,
        series: [{
            type: "graph",
            layout: "force",
            roam: true,
            data: updatedNodes,
            links: updatedEdges,
            force: { repulsion: 200, edgeLength: [80, 120] }
        }]
    });
}

// -----------------------------------
// CLICK EVENTS
// -----------------------------------
chart.on("click", function(params){
    if (params.dataType === "node") {
        updateSelectedNode(params.data.name);
    } else {
        lastSelectedNode = null;
        updateChart();
        document.getElementById("node-long-name").innerText = "";
        document.getElementById("node-short-name").innerText = "";
        document.getElementById("node-role").innerText = "";
        document.getElementById("node-hw-model").innerText = "";
    }
});

function updateSelectedNode(id) {
    lastSelectedNode = id;
    updateChart();

    const n = filteredNodes.find(n => n.name === id);
    if (!n) return;

    document.getElementById("node-long-name").innerText = n.long_name;
    document.getElementById("node-short-name").innerText = n.short_name;
    document.getElementById("node-role").innerText = n.role;
    document.getElementById("node-hw-model").innerText = n.hw_model;
}

// -----------------------------------
// SEARCH
// -----------------------------------
function searchNode() {
    const q = document.getElementById("node-search").value.toLowerCase().trim();
    if (!q) return;

    const found = filteredNodes.find(n =>
        n.name.toLowerCase().includes(q) ||
        (n.long_name || "").toLowerCase().includes(q) ||
        (n.short_name || "").toLowerCase().includes(q)
    );

    if (found) updateSelectedNode(found.name);
    else alert("Node not found in current channel!");
}

// -----------------------------------
loadData();
window.addEventListener("resize", () => chart.resize());
</script>

{% endblock %}
