(function () {
    'use strict';

    function ready(fn) {
        if (document.readyState !== 'loading') { fn(); return; }
        document.addEventListener('DOMContentLoaded', fn);
    }

    function parseJson(id) {
        var el = document.getElementById(id);
        if (!el) { return null; }
        try { return JSON.parse(el.textContent || 'null'); } catch (e) { return null; }
    }

    function fmt(n) {
        n = Number(n || 0);
        return n.toLocaleString();
    }

    function esc(value) {
        return String(value === null || value === undefined ? '' : value)
            .replace(/&/g, '&amp;').replace(/</g, '&lt;')
            .replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#039;');
    }

    function colors(i) {
        var palette = ['#2563eb', '#f59e0b', '#14b8a6', '#ef4444', '#8b5cf6', '#22c55e', '#f97316', '#0ea5e9', '#64748b', '#db2777'];
        return palette[i % palette.length];
    }

    function empty(el, title) {
        el.innerHTML = '<div class="ngbvd-viz-empty"><strong>' + esc(title || 'No chart data') + '</strong><span>No records were returned for this visual.</span></div>';
    }

    function cardTitle(cfg) {
        return '<div class="ngbvd-viz-title"><h4>' + esc(cfg.title || '') + '</h4>' + (cfg.subtitle ? '<p>' + esc(cfg.subtitle) + '</p>' : '') + '</div>';
    }

    function renderBar(el, cfg) {
        var data = (cfg.data || []).filter(function (x) { return Number(x.y || 0) >= 0; });
        if (!data.length) { empty(el, cfg.title); return; }
        data = data.slice(0, cfg.limit || 24);
        var max = Math.max.apply(null, data.map(function (x) { return Number(x.y || 0); }).concat([1]));
        var rows = data.map(function (x, i) {
            var pct = Math.max(2, Math.round((Number(x.y || 0) / max) * 100));
            return '<div class="ngbvd-viz-bar-row" data-name="' + esc(x.name) + '">' +
                '<span class="ngbvd-viz-bar-label">' + esc(x.name) + '</span>' +
                '<span class="ngbvd-viz-bar-track"><span style="width:' + pct + '%;background:' + colors(i) + '"></span></span>' +
                '<strong>' + fmt(x.y) + '</strong>' +
                '</div>';
        }).join('');
        el.innerHTML = cardTitle(cfg) + '<div class="ngbvd-viz-bar-chart">' + rows + '</div>';
    }

    function renderGroupedBar(el, cfg) {
        var categories = cfg.categories || [];
        var series = cfg.series || [];
        if (!categories.length || !series.length) { empty(el, cfg.title); return; }
        var max = 1;
        series.forEach(function (s) { (s.data || []).forEach(function (v) { max = Math.max(max, Number(v || 0)); }); });
        var html = categories.map(function (cat, idx) {
            var bars = series.map(function (s, sidx) {
                var value = Number((s.data || [])[idx] || 0);
                var h = Math.max(2, Math.round((value / max) * 100));
                return '<span class="ngbvd-viz-gbar" title="' + esc(s.name) + ': ' + fmt(value) + '" style="height:' + h + '%;background:' + colors(sidx) + '"><em>' + fmt(value) + '</em></span>';
            }).join('');
            return '<div class="ngbvd-viz-gcol"><div class="ngbvd-viz-gbars">' + bars + '</div><small>' + esc(cat) + '</small></div>';
        }).join('');
        var legend = series.map(function (s, i) { return '<span><i style="background:' + colors(i) + '"></i>' + esc(s.name) + '</span>'; }).join('');
        el.innerHTML = cardTitle(cfg) + '<div class="ngbvd-viz-legend">' + legend + '</div><div class="ngbvd-viz-grouped">' + html + '</div>';
    }

    function renderPie(el, cfg) {
        var data = (cfg.data || []).filter(function (x) { return Number(x.y || 0) > 0; });
        if (!data.length) { empty(el, cfg.title); return; }
        var total = data.reduce(function (s, x) { return s + Number(x.y || 0); }, 0) || 1;
        var cumulative = 0;
        var stops = data.map(function (x, i) {
            var start = cumulative;
            cumulative += (Number(x.y || 0) / total) * 100;
            return colors(i) + ' ' + start.toFixed(2) + '% ' + cumulative.toFixed(2) + '%';
        }).join(',');
        var rows = data.slice(0, 12).map(function (x, i) {
            var pct = Math.round((Number(x.y || 0) / total) * 1000) / 10;
            return '<div class="ngbvd-viz-pie-row"><span style="background:' + colors(i) + '"></span><b>' + esc(x.name) + '</b><strong>' + fmt(x.y) + ' <small>' + pct + '%</small></strong></div>';
        }).join('');
        el.innerHTML = cardTitle(cfg) + '<div class="ngbvd-viz-pie-wrap"><div class="ngbvd-viz-donut" style="background:conic-gradient(' + stops + ')"><div><span>Total</span><strong>' + fmt(total) + '</strong></div></div><div class="ngbvd-viz-pie-list">' + rows + '</div></div>';
    }

    function renderLine(el, cfg) {
        var categories = cfg.categories || [];
        var series = cfg.series || [];
        if (!categories.length || !series.length) { empty(el, cfg.title); return; }
        var values = [];
        series.forEach(function (s) { values = values.concat(s.data || []); });
        var max = Math.max.apply(null, values.map(function (v) { return Number(v || 0); }).concat([1]));
        var width = 900, height = 280, pad = 40;
        function point(value, idx) {
            var x = pad + (idx * ((width - pad * 2) / Math.max(1, categories.length - 1)));
            var y = height - pad - ((Number(value || 0) / max) * (height - pad * 2));
            return [x, y];
        }
        var paths = series.map(function (s, si) {
            var d = (s.data || []).map(function (v, i) { var p = point(v, i); return (i ? 'L' : 'M') + p[0] + ',' + p[1]; }).join(' ');
            var circles = (s.data || []).map(function (v, i) { var p = point(v, i); return '<circle cx="' + p[0] + '" cy="' + p[1] + '" r="4" fill="' + colors(si) + '"><title>' + esc(s.name) + ' ' + esc(categories[i]) + ': ' + fmt(v) + '</title></circle>'; }).join('');
            return '<path d="' + d + '" fill="none" stroke="' + colors(si) + '" stroke-width="4" stroke-linecap="round"/>' + circles;
        }).join('');
        var xLabels = categories.map(function (c, i) { var p = point(0, i); return '<text x="' + p[0] + '" y="' + (height - 8) + '" text-anchor="middle">' + esc(c) + '</text>'; }).join('');
        var legend = series.map(function (s, i) { return '<span><i style="background:' + colors(i) + '"></i>' + esc(s.name) + '</span>'; }).join('');
        el.innerHTML = cardTitle(cfg) + '<div class="ngbvd-viz-legend">' + legend + '</div><div class="ngbvd-viz-svg-scroll"><svg viewBox="0 0 ' + width + ' ' + height + '" role="img"><line x1="' + pad + '" y1="' + (height-pad) + '" x2="' + (width-pad) + '" y2="' + (height-pad) + '" stroke="#cbd5e1"/>' + paths + xLabels + '</svg></div>';
    }

    function renderAll() {
        document.querySelectorAll('[data-ngbvd-viz]').forEach(function (el) {
            var cfg = parseJson(el.getAttribute('data-source'));
            if (!cfg) { return; }
            var type = cfg.type || 'bar';
            if (type === 'pie' || type === 'donut') { renderPie(el, cfg); }
            else if (type === 'grouped-bar') { renderGroupedBar(el, cfg); }
            else if (type === 'line') { renderLine(el, cfg); }
            else { renderBar(el, cfg); }
        });
    }

    ready(renderAll);
    window.NGBVDVisuals = { renderAll: renderAll };
})();
