<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Treemap</title>

    <script>{{D3_MIN}}</script>

    <style>{{MCP_APP_BASE_CSS}}</style>
    <style>
        body { padding: 16px; }

        .treemap-rect {
            stroke: var(--color-background-primary, #fff);
            stroke-width: 2px;
            cursor: pointer;
            transition: filter 0.15s ease;
        }

        .treemap-rect:hover {
            filter: brightness(1.12);
        }

        .treemap-text {
            font-size: 11px;
            font-weight: var(--font-weight-medium);
            fill: #fff;
            text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
            pointer-events: none;
            text-anchor: middle;
            dominant-baseline: middle;
        }

        .treemap-value {
            font-size: 10px;
            fill: rgba(255, 255, 255, 0.75);
            text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
            pointer-events: none;
            text-anchor: middle;
            dominant-baseline: middle;
        }
    </style>
</head>
<body>
    <div class="av-loading" id="loadingIndicator">Waiting for data…</div>
    <div id="treemap" style="width:100%;"></div>
    <div class="av-tooltip" id="tooltip"></div>

    <script>{{MCP_APP_BRIDGE}}</script>
    <script>
    (function () {
        "use strict";

        var svg, treemapLayout, root;

        function getHeight() {
            if (McpAppBridge.displayMode === "fullscreen" || McpAppBridge.displayMode === "pip") {
                return window.innerHeight - 64;
            }
            return 460;
        }

        var palette = [
            "#5c98f9", "#f97066", "#47c9a2", "#f0a03c",
            "#9b8afb", "#e87fa0", "#3cc0e0", "#8bba5a",
        ];
        var colorScale = d3.scaleOrdinal(palette);

        function formatValue(v) {
            if (v >= 1e6) return (v / 1e6).toFixed(1) + "M";
            if (v >= 1e3) return (v / 1e3).toFixed(1) + "K";
            return v.toLocaleString();
        }

        function getWidth() {
            var container = document.getElementById("treemap");
            return container.clientWidth || 800;
        }

        function initializeTreemap() {
            var container = d3.select("#treemap");
            container.selectAll("*").remove();
            var width = getWidth();
            var h = getHeight();
            svg = container.append("svg").attr("width", width).attr("height", h);
            treemapLayout = d3.treemap().size([width, h]).padding(2).round(true);
        }

        function updateTreemap(data) {
            root = d3.hierarchy(data)
                .sum(function (d) { return d.value || 0; })
                .sort(function (a, b) { return b.value - a.value; });
            treemapLayout(root);
            svg.selectAll("*").remove();

            var leaf = svg.selectAll("g").data(root.leaves()).join("g")
                .attr("transform", function (d) { return "translate(" + d.x0 + "," + d.y0 + ")"; });

            leaf.append("rect").attr("class", "treemap-rect")
                .attr("fill", function (d) { return colorScale(d.data.category || (d.parent ? d.parent.data.name : "default")); })
                .attr("width", function (d) { return d.x1 - d.x0; })
                .attr("height", function (d) { return d.y1 - d.y0; })
                .attr("rx", 3)
                .on("mouseover", function (event, d) {
                    var pct = ((d.data.value / root.value) * 100).toFixed(1);
                    var tt = document.getElementById("tooltip");
                    tt.innerHTML = "<strong>" + d.data.name + "</strong><br/>" + formatValue(d.data.value) + " (" + pct + "%)" +
                        (d.data.category ? "<br/>" + d.data.category : "");
                    tt.style.opacity = 1;
                    McpAppBridge.positionTooltip(tt, event);
                })
                .on("mousemove", function (event) {
                    McpAppBridge.positionTooltip(document.getElementById("tooltip"), event);
                })
                .on("mouseout", function () { document.getElementById("tooltip").style.opacity = 0; });

            leaf.append("text").attr("class", "treemap-text")
                .attr("x", function (d) { return (d.x1 - d.x0) / 2; })
                .attr("y", function (d) { return (d.y1 - d.y0) / 2 - 6; })
                .text(function (d) { return d.data.name; })
                .style("font-size", function (d) { return Math.min((d.x1 - d.x0) / 8, (d.y1 - d.y0) / 4, 14) + "px"; })
                .each(function (d) {
                    var textLen = this.getComputedTextLength();
                    var avail = d.x1 - d.x0 - 8;
                    if (textLen > avail) {
                        var text = d.data.name;
                        var ratio = avail / textLen;
                        var maxLen = Math.floor(text.length * ratio) - 2;
                        d3.select(this).text(maxLen > 0 ? text.substring(0, maxLen) + "…" : "");
                    }
                });

            leaf.append("text").attr("class", "treemap-value")
                .attr("x", function (d) { return (d.x1 - d.x0) / 2; })
                .attr("y", function (d) { return (d.y1 - d.y0) / 2 + 9; })
                .text(function (d) { return formatValue(d.data.value); })
                .style("font-size", function (d) { return Math.min((d.x1 - d.x0) / 10, (d.y1 - d.y0) / 6, 12) + "px"; })
                .style("display", function (d) { return (d.y1 - d.y0) < 30 ? "none" : "block"; });
        }

        function renderData(data) {
            document.getElementById("loadingIndicator").classList.add("hidden");
            initializeTreemap();
            updateTreemap(data);
            setTimeout(McpAppBridge.reportSize, 80);
        }

        function safeRender(data) {
            try { renderData(data); }
            catch (e) { McpAppBridge.showError(e.message || e); }
        }

        McpAppBridge.init({
            appName: "autovisualiser-treemap",
            onData: safeRender,
            onTheme: function () {
                if (McpAppBridge.currentData) safeRender(McpAppBridge.currentData);
            },
        });
    })();
    </script>
</body>
</html>
