//SCREEN-BOILERPLATE
//this boilerplate builds screens that are gonna be shown in the <div class='content'> tag of our main HTML.
//do not build app components with this boilerplate, only screens (reports)
//
// EAF Delays Pareto — modern refactor of the legacy custom-screens/delays/delays-4 screen.
// Keeps the IHDelays2.WebApp backend (GetDelaysTimeSummary + GetDelayCategoriesStacked) and the
// EAF Pareto feature set, but renders it with AG Grid + Chart.js and the Caster Delays By Date
// visual style. The legacy screen stays untouched as reference.

define([
    // Application variable, always include it to have access to app methods.
    'app',

    //templates-loader: this loads templates async.
    'js/templates-loader',
    'backgrid',
    'moment',

    'modules/modal2',

    'components/ag-grid/ag-grid-module',
    'chart',
    'backgrid/infinator',
    'backgrid/moment-cell',
    'backgrid/grouped-columns',

    "js/jquery.floatThead/jquery.floatThead",
    'js/jquery.contextMenu/jquery.contextMenu'
],
    function (app, T, Backgrid, moment, Modal, AgGridModule, Chart) {

        var Screen = { Models: {}, Views: {}, Collections: {} };

        // Crews are a fixed catalog (matching the legacy screen). The stored procedure expects the
        // single-letter crew code, so we keep the numeric value for the combo and translate on fetch.
        var CREWS = [
            { value: 1, label: 'A' },
            { value: 2, label: 'B' },
            { value: 3, label: 'C' },
            { value: 4, label: 'D' }
        ];

        // Timezone is hardcoded in the legacy screen; preserved verbatim here.
        var TIMEZONE = 'EDTIND';

        Screen.generateID = function (viewParams) {
            try { return 'delays-pareto'; } catch (Error) { }
        };

        Screen.Models.Main = Backbone.Epoxy.Model.extend({
            defaults: {
                start: new moment().subtract(1, 'days').format('YYYY-MM-DD'),
                end: new moment().format('YYYY-MM-DD'),

                machineId: -1,
                machines: [],

                shiftId: -1,
                shifts: [],

                crewId: -1,
                crews: CREWS,

                delayCategoryId: -1,
                delayCategories: [],

                hasData: false,
                isLoading: false,
                isPageLoading: false
            },
            computeds: {
                start$: {
                    deps: ['start'],
                    get: function (value) {
                        return value;
                    },
                    set: function (value) {
                        return { start: (value) ? new moment(value, app.translate(app, 'MM/DD/YYYY')).format('YYYY-MM-DD') : value };
                    }
                },
                end$: {
                    deps: ['end'],
                    get: function (value) {
                        return value;
                    },
                    set: function (value) {
                        return { end: (value) ? new moment(value, app.translate(app, 'MM/DD/YYYY')).format('YYYY-MM-DD') : value };
                    }
                }
            },
            initialize: function () {
                this.fetchProductionItems();
                this.fetchShifts();
                this.fetchDelayCategories();
            },
            fetchProductionItems: function () {
                var that = this;
                Core.Json.CallProcedure(app.DatabaseNames.IHDelays + '.WebApp.GetProductionItems', null, {
                    onSuccess: function (data) {
                        if (data && data.Table) {
                            that.set({
                                machines: _.map(_.filter(data.Table, function (m) {
                                    return m.ProductionItemType && m.ProductionItemType.toUpperCase() === 'MACHINE';
                                }), function (m) {
                                    return { value: m.Id, label: m.Name };
                                })
                            });
                        }
                    },
                    Async: true,
                    Secured: true
                }, app.ConnectionStrings.app);
            },
            fetchShifts: function () {
                var that = this;
                Core.Json.CallProcedure(app.DatabaseNames.IHDelays + '.WebApp.GetShifts', null, {
                    onSuccess: function (data) {
                        if (data && data.Table) {
                            that.set({
                                shifts: _.map(data.Table, function (m) {
                                    return { value: m.Id, label: m.Name };
                                })
                            });
                        }
                    },
                    Async: true,
                    Secured: true
                }, app.ConnectionStrings.app);
            },
            fetchDelayCategories: function () {
                var that = this;
                Core.Json.CallProcedure(app.DatabaseNames.IHDelays + '.WebApp.GetDelayCategories', null, {
                    onSuccess: function (data) {
                        if (data && data.Table) {
                            that.set({
                                delayCategories: _.map(data.Table, function (m) {
                                    return { value: m.Id, label: m.Name, color: m.Color };
                                })
                            });
                        }
                    },
                    Async: true,
                    Secured: true
                }, app.ConnectionStrings.app);
            }
        });

        // ----------------------------------------------------------------------------------------
        // PARETO GRID
        // ----------------------------------------------------------------------------------------
        var ParetoAgGridModule = AgGridModule.extend({
            initialize: function (options) {
                this.bus = options.bus;
                // Total duration (seconds) and the per-category segments are seeded from the view
                // before each setRowData so the proportional bars can be computed.
                this.totalDuration = 0;
                this.totalSegments = [];

                var i18n = options.i18n || function (k) { return k; };

                this.columnDefs = [
                    { field: 'Code', headerName: i18n('item_name_col_label'), flex: 1, minWidth: 110, cellClass: 'text-center', headerClass: 'ag-header-cell-center' },
                    { field: 'ProductionItem', headerName: i18n('prod_item_col_label'), flex: 1, minWidth: 90, cellClass: 'text-center', headerClass: 'ag-header-cell-center' },
                    { field: 'Category', headerName: i18n('item_category_col_label'), flex: 1, minWidth: 110, cellClass: 'text-center', headerClass: 'ag-header-cell-center' },
                    { field: 'DurationDaysAndTime', headerName: i18n('item_duration_col_label'), flex: 1, minWidth: 150, cellClass: 'text-center', headerClass: 'ag-header-cell-center' },
                    { field: 'Count', headerName: i18n('item_count_col_label'), flex: 1, minWidth: 80, cellClass: 'text-center', headerClass: 'ag-header-cell-center' },
                    {
                        field: 'Graph',
                        headerName: i18n('item_graph_col_label'),
                        flex: 2,
                        minWidth: 320,
                        sortable: false,
                        // graph-cell centers the bar vertically via CSS without collapsing its width.
                        cellClass: 'graph-cell',
                        headerClass: 'ag-header-cell-center',
                        cellRenderer: this.progressBarRenderer.bind(this)
                    }
                ];

                options.columnDefs = this.columnDefs;

                if (!options.gridOptions) options.gridOptions = {};
                options.gridOptions.getRowClass = this.getRowClass.bind(this);
                // Start in loading state so the first paint shows "Loading..." instead of "No Rows".
                options.gridOptions.loading = true;

                AgGridModule.prototype.initialize.call(this, options);
            },

            getRowClass: function (params) {
                if (params.node && params.node.rowPinned === 'bottom') {
                    return 'delays-total-row';
                }
                return '';
            },

            // Per-row proportional bar (and composite bar for the pinned Total row), faithful to the
            // legacy Backgrid progress-bar column.
            progressBarRenderer: function (params) {
                var total = Number(this.totalDuration) || 0;

                if (params.node && params.node.rowPinned === 'bottom') {
                    var html = '<div style="display:flex;width:100%;height:16px;border:1px solid #ddd;border-radius:2px;overflow:hidden;">';
                    _.each(this.totalSegments || [], function (seg) {
                        var pct = (total > 0) ? (Number(seg.seconds) * 100 / total) : 0;
                        html += '<div title="' + (seg.category || '') + '" style="width:' + pct + '%;height:100%;background:' + (seg.color || '#ccc') + ';"></div>';
                    });
                    html += '</div>';
                    return html;
                }

                var d = params.data;
                if (!d) return '';
                var rowPct = (total > 0) ? (Number(d.Duration) * 100 / total) : 0;
                return '<div style="width:100%;height:16px;background:#f0f0f0;border-radius:2px;overflow:hidden;">' +
                    '<div style="width:' + rowPct + '%;height:100%;background:' + (d.Color || '#ccc') + ';"></div>' +
                    '</div>';
            }
        });

        // ----------------------------------------------------------------------------------------
        // MAIN VIEW
        // ----------------------------------------------------------------------------------------
        Screen.Views.Main = Backbone.Epoxy.View.extend({
            template: 'delays-pareto',
            id: 'delays-pareto',
            title: 'EAF Delays Pareto',
            delaysChart: null,
            events: function () {
                return {
                    'click #refreshBtn': this.refreshBtn_click,
                    'click #exportExcelBtn': this.exportToExcel,
                    'click #calendarStartBtn': this.calendarStartBtn_click,
                    'click #calendarEndBtn': this.calendarEndBtn_click
                };
            },
            bindings: 'data-bind',
            bindingSources: null,
            grids: null,
            subviews: null,
            viewParams: null,
            initialize: function () {
                this.options.state = app.view_states.loading;
                this.options.onappend = (_.isFunction(this.options.onappend)) ? this.options.onappend : function () { };
                this.bus = _.extend({}, Backbone.Events);

                if (!this.model)
                    this.model = new Screen.Models.Main();

                this.delaysChart = null;
                this.grids = {};
                this.bindingSources = {};
                this.subviews = {};

                this.bindEvents();
            },

            // i18n helper bound to this screen's loaded resx.
            t: function (key) {
                try { return app.translate(this, key); } catch (e) { return key; }
            },

            calendarStartBtn_click: function () {
                this.$el.find('#dateStartTbx').datepicker('show');
            },
            calendarEndBtn_click: function () {
                this.$el.find('#dateEndTbx').datepicker('show');
            },

            bindEvents: function () {
                this.listenTo(this.model, 'triggerFetch', this.refreshBtn_click);
            },

            render: function (container, viewParams) {
                var that = this;

                this.viewParams = viewParams || {};
                that.model.set({ isPageLoading: true });

                var thatContainer = (this.options.container) ? this.options.container : container;
                this.options.container = thatContainer;

                var customPath = '/app/pages/eaf/' + this.template + '/';

                T.render.call(
                    this,
                    this.template,
                    function (tmp) {
                        if (!that.options.i18n) that.options.i18n = {};

                        app.getI18NJed(
                            that,
                            that.template,
                            function (i18nJED) {
                                that.options.i18n[that.template] = i18nJED;

                                that.$el.html(tmp());
                                that.applyBindings();

                                that.$el.find('.input-date').datepicker();

                                // Build the Pareto grid now that i18n is available (headers are localized).
                                that.agGridModule = new ParetoAgGridModule({
                                    bus: that.bus,
                                    rowData: [],
                                    pagination: 'hidden',
                                    fileName: 'EAF-Delays-Pareto',
                                    i18n: that.t.bind(that)
                                });
                                that.agGridModule.render();
                                that.$('#paretoGrid').append(that.agGridModule.$el);

                                that.initChart();

                                that.append(thatContainer, that.$el);

                                // Apply URL params over the model before first fetch.
                                var fixedParams = _.extend({}, viewParams);
                                if (fixedParams.start)
                                    fixedParams.start = new moment(fixedParams.start, 'YYYYMMDD').format('YYYY-MM-DD');
                                if (fixedParams.end)
                                    fixedParams.end = new moment(fixedParams.end, 'YYYYMMDD').format('YYYY-MM-DD');
                                if (viewParams.machineId)
                                    fixedParams.machineId = (viewParams.machineId != '-' && viewParams.machineId != null) ? parseInt(viewParams.machineId) : -1;
                                if (viewParams.shiftId)
                                    fixedParams.shiftId = (viewParams.shiftId != '-' && viewParams.shiftId != null) ? parseInt(viewParams.shiftId) : -1;
                                if (viewParams.crewId)
                                    fixedParams.crewId = (viewParams.crewId != '-' && viewParams.crewId != null) ? parseInt(viewParams.crewId) : -1;
                                if (viewParams.delayCategoryId)
                                    fixedParams.delayCategoryId = (viewParams.delayCategoryId != '-' && viewParams.delayCategoryId != null) ? parseInt(viewParams.delayCategoryId) : -1;
                                that.model.set(fixedParams);

                                that._refresh();

                                that.model.set({ isPageLoading: false });
                            },
                            true,
                            customPath
                        );
                    },
                    customPath, "delays_report"
                );
            },

            initChart: function () {
                var that = this;
                var canvas = this.$('#delaysChart');
                if (!canvas.length) {
                    console.error('Canvas #delaysChart not found');
                    return;
                }
                var ctx = canvas[0].getContext('2d');
                this.delaysChart = new Chart(ctx, {
                    type: 'bar',
                    data: { labels: [], datasets: [] },
                    options: {
                        responsive: true,
                        maintainAspectRatio: false,
                        interaction: { mode: 'index', intersect: false },
                        scales: {
                            x: { stacked: true },
                            y: {
                                stacked: true,
                                beginAtZero: true,
                                max: 100,
                                title: { display: true, text: that.t('chart_y_axis_label') }
                            }
                        },
                        plugins: {
                            legend: { display: true, position: 'bottom' },
                            tooltip: {
                                callbacks: {
                                    label: function (c) {
                                        return c.dataset.label + ': ' + c.parsed.y + ' %';
                                    }
                                }
                            }
                        }
                    }
                });
            },

            // Translate the model state into the parameter shape both stored procedures expect.
            buildParams: function () {
                var params = this.model.toJSON();

                var fromMoment = moment(params.start, 'YYYY-MM-DD');
                var toMoment = moment(params.end, 'YYYY-MM-DD');
                // Legacy behaviour: a single-day range is widened by one day so it returns data.
                if (params.start === params.end) toMoment.add(1, 'd');

                var crew = null;
                if (params.crewId && params.crewId != -1) {
                    var crewItem = _.findWhere(CREWS, { value: parseInt(params.crewId) });
                    crew = crewItem ? crewItem.label : null;
                }

                return {
                    from: fromMoment.format('YYYY-MM-DD HH:mm:ss'),
                    to: toMoment.format('YYYY-MM-DD HH:mm:ss'),
                    productionItemId: (params.machineId && params.machineId != -1) ? params.machineId : null,
                    shiftId: (params.shiftId && params.shiftId != -1) ? params.shiftId : null,
                    crew: crew,
                    delayCategoryId: (params.delayCategoryId && params.delayCategoryId != -1) ? params.delayCategoryId : null
                };
            },

            _refresh: function () {
                try {
                    this.model.set({ isLoading: true, hasData: false });
                    if (this.agGridModule) this.agGridModule.showLoadingOverlay();

                    var p = this.buildParams();
                    this.fetchGridData(p);
                    this.fetchChartData(p);
                } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
            },

            fetchGridData: function (p) {
                var that = this;
                var qp = new Core.Database.QueryParameters();
                qp.Add('@From', 'DATETIME', p.from);
                qp.Add('@To', 'DATETIME', p.to);
                qp.Add('@ProductionItemId', 'INT', p.productionItemId);
                qp.Add('@ProductId', 'INT', null);
                qp.Add('@ShiftId', 'INT', p.shiftId);
                qp.Add('@delayCategoryId', 'INT', p.delayCategoryId);
                qp.Add('@CrewId', 'CHAR', p.crew);
                qp.Add('@Timezone', 'VARCHAR', TIMEZONE);

                Core.Json.CallProcedure(
                    app.DatabaseNames.IHDelays + '.WebApp.GetDelaysTimeSummary',
                    qp,
                    {
                        onSuccess: function (resp) {
                            try {
                                if (resp && resp.Table) {
                                    that.onGridData(resp.Table);
                                } else {
                                    that.onGridData([]);
                                    if (resp && resp.Message) {
                                        console.error(new Error(resp.Message).stack);
                                        app.views.topMessages.showMessage('CALL TO DATABASE FAILED', { stay: 5000 });
                                    }
                                }
                            } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
                        },
                        onFailure: function (resp) {
                            that.onGridData([]);
                            console.error(resp);
                        },
                        Secured: true,
                        Async: true
                    },
                    app.ConnectionStrings.app
                );
            },

            onGridData: function (rows) {
                var that = this;
                rows = rows || [];

                // Total duration (seconds) and category segments for the proportional bars.
                var totalDuration = _.reduce(rows, function (memo, r) { return memo + (Number(r.Duration) || 0); }, 0);
                var totalCount = _.reduce(rows, function (memo, r) { return memo + (Number(r.Count) || 0); }, 0);

                var segments = [];
                _.each(rows, function (r) {
                    var existing = _.findWhere(segments, { category: r.Category });
                    if (existing) {
                        existing.seconds += (Number(r.Duration) || 0);
                    } else {
                        segments.push({ category: r.Category, color: r.Color, seconds: (Number(r.Duration) || 0) });
                    }
                });

                this.agGridModule.totalDuration = totalDuration;
                this.agGridModule.totalSegments = segments;

                setTimeout(function () {
                    that.agGridModule.setRowData(rows);
                    that.agGridModule.hideOverlay();

                    // Total row pinned at the bottom (replaces the legacy "Total" grid row).
                    var totalRow = {
                        Code: that.t('total_label'),
                        ProductionItem: '',
                        Category: '',
                        DurationDaysAndTime: that.formatDaysAndTime(totalDuration),
                        Count: totalCount,
                        Duration: totalDuration,
                        Color: null
                    };
                    var api = that.agGridModule.getGridApi();
                    if (api) api.setGridOption('pinnedBottomRowData', rows.length ? [totalRow] : []);

                    that.model.set({ hasData: (rows.length > 0), isLoading: false });
                }, 100);
            },

            // days HH:mm:ss — matches the SP's DurationDaysAndTime format used for the per-row values.
            formatDaysAndTime: function (totalSeconds) {
                totalSeconds = Math.max(0, Math.floor(totalSeconds || 0));
                var days = Math.floor(totalSeconds / 86400);
                var rem = totalSeconds % 86400;
                var hh = String(Math.floor(rem / 3600)).padStart(2, '0');
                var mm = String(Math.floor((rem % 3600) / 60)).padStart(2, '0');
                var ss = String(rem % 60).padStart(2, '0');
                return days + ' ' + hh + ':' + mm + ':' + ss;
            },

            fetchChartData: function (p) {
                var that = this;
                var qp = new Core.Database.QueryParameters();
                qp.Add('@From', 'DATETIME', p.from);
                qp.Add('@To', 'DATETIME', p.to);
                qp.Add('@Timezone', 'VARCHAR', TIMEZONE);
                qp.Add('@ProductionItemId', 'INT', p.productionItemId);
                qp.Add('@ShiftId', 'INT', p.shiftId);
                qp.Add('@ProductId', 'INT', null);
                qp.Add('@delayCategoryId', 'INT', p.delayCategoryId);
                // GetDelayCategoriesStacked rounds the per-division width (ms) into an INT (@groupBase),
                // which overflows once a division exceeds ~24.8 days. Scale the division count with the
                // range so each division stays under ~20 days and the SP never overflows on wide ranges.
                var rangeDays = Math.max(1, moment(p.to).diff(moment(p.from), 'days'));
                qp.Add('@Divisions', 'INT', Math.max(12, Math.ceil(rangeDays / 20)));

                Core.Json.CallProcedure(
                    app.DatabaseNames.IHDelays + '.WebApp.GetDelayCategoriesStacked',
                    qp,
                    {
                        onSuccess: function (resp) {
                            try {
                                // resp.Table1 → per-division stacked data, resp.Table2 → category catalog.
                                var stacked = (resp && resp.Table1) ? resp.Table1 : [];
                                var categories = (resp && resp.Table2) ? resp.Table2 : [];
                                that.drawChart(stacked, categories);
                            } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
                        },
                        onFailure: function (resp) {
                            that.drawChart([], []);
                            console.error(resp);
                        },
                        Secured: true,
                        Async: true
                    },
                    app.ConnectionStrings.app
                );
            },

            drawChart: function (stacked, categories) {
                if (!this.delaysChart) return;

                // Prepend the "Not Declared" pseudo-category (id null), matching the legacy screen.
                var cats = [{ id: null, name: 'NOTDECLARED', description: this.t('not_declared_label'), color: '#f84e4e' }];
                _.each(categories, function (c) {
                    cats.push({ id: c.Id, name: c.Name, description: c.Description, color: c.Color });
                });

                // When a single category is selected, show only that dataset.
                var selectedId = parseInt(this.model.get('delayCategoryId'));
                if (!isNaN(selectedId) && selectedId !== -1) {
                    cats = _.filter(cats, function (c) { return c.id === selectedId; });
                }

                // Stable, ascending timestamp axis.
                var labels = _.uniq(_.pluck(stacked, 'Timestamp'));

                var datasets = _.map(cats, function (cat) {
                    var data = _.map(labels, function () { return 0; });
                    var rows = _.filter(stacked, function (s) {
                        return (s.CategoryId === null || s.CategoryId === undefined)
                            ? (cat.id === null)
                            : (s.CategoryId === cat.id);
                    });
                    _.each(rows, function (s) {
                        var idx = _.indexOf(labels, s.Timestamp);
                        if (idx !== -1) data[idx] = (_.isNumber(s.Duration)) ? s.Duration : 0;
                    });
                    return {
                        label: cat.description || cat.name,
                        backgroundColor: cat.color || '#ccc',
                        data: data
                    };
                });

                this.delaysChart.data.labels = labels;
                this.delaysChart.data.datasets = datasets;
                this.delaysChart.update();

                // Show the "no data" overlay when the query returned no divisions to plot.
                this.$('#delaysChartNoData').toggleClass('is-hidden', labels.length > 0);
            },

            exportToExcel: function () {
                if (this.agGridModule) this.agGridModule.exportToExcel(false);
            },

            refreshBtn_click: function () {
                try {
                    var params = this.model.toJSON();
                    app.router.navigate(
                        app.router.resolveURL(
                            app.router.currentModule,
                            _.extend({}, params, {
                                start: new moment(params.start, 'YYYY-MM-DD').format('YYYYMMDD'),
                                end: new moment(params.end, 'YYYY-MM-DD').format('YYYYMMDD'),
                                machineId: (params.machineId == null || params.machineId == '') ? '-' : params.machineId,
                                shiftId: (params.shiftId == null || params.shiftId == '') ? '-' : params.shiftId,
                                crewId: (params.crewId == null || params.crewId == '') ? '-' : params.crewId,
                                delayCategoryId: (params.delayCategoryId == null || params.delayCategoryId == '') ? '-' : params.delayCategoryId
                            }),
                            false
                        ),
                        { trigger: false }
                    );

                    this._refresh();
                    window.scrollTo(0, 0);
                } catch (Error) { console.error(Error.stack); }
            },

            //Common functions
            append: function (container, el) {
                el = (el != null && el != undefined) ? el : this.$el;

                if (this.options.state == app.view_states.loading
                    || this.options.state == app.view_states.shown) {
                    this.options.state = app.view_states.shown;
                    container.append(el);
                    this.options.onappend(this);
                }

                if (this.options.state == app.view_states.hidden) {
                    container.append(el);
                }

                if (this.options.state == app.view_states.closed) {
                    return;
                }
            },
            close: function () {
                this.options.state = app.view_states.closed;
                if (this.delaysChart) {
                    try { this.delaysChart.destroy(); } catch (e) { }
                    this.delaysChart = null;
                }
                this.closeSubviews();
                this.remove();
                this.unbind();
            },
            closeSubviews: function () {
                _.each(this.subviews, function (sview) { sview.close(); });
            },
            show: function () {
                this.options.state = app.view_states.shown;
                this.showSubviews();
                this.bindEvents();
                this.$el.show();
            },
            showSubviews: function () {
                _.each(this.subviews, function (sview) { sview.show(); });
            },
            hide: function () {
                this.options.state = app.view_states.hidden;
                this.hideSubviews();
                this.$el.hide();
                this.unbind();
                this.stopListening();
            },
            hideSubviews: function () {
                _.each(this.subviews, function (sview) { sview.hide(); });
            },
            preRender: function () {
                app.models.subnavbar.set("subnavbar", false);
            },
            reRender: function (viewParams) {
                try {
                    this._refresh();
                } catch (Error) { }
            }
        });

        // Required, return the module for AMD compliance.
        return Screen;
    });
