(function($) {
  'use strict';
  var D = window.ElsDash;
  var H = D.helpers;
  var COLS = D.COLS;

  // ============================================================
  // DATA PROCESSING
  // ============================================================
  function processData(rawRows) {
    var apps = {};
    rawRows.forEach(function(row) {
      var fn = H.col(row, 'FILE_NUMBER');
      var cs = H.col(row, 'CALLSIGN');
      var groupKey = fn + '|' + cs;
      if (!apps[groupKey]) {
        apps[groupKey] = {
          fileNumber: fn,
          callsign: cs,
          name: H.col(row, 'NAME'),
          receiptDate: H.parseDate(H.col(row, 'RECEIPT_DATE')),
          statusDate: H.parseDate(H.col(row, 'STATUS_DATE')),
          status: H.normalizeStatus(H.col(row, 'STATUS_DESC')),
          assigned: H.col(row, 'ASSIGNED') === 'true' || H.col(row, 'ASSIGNED') === '1',
          experimentType: H.col(row, 'EXPERIMENT_TYPE'),
          renewalTimeliness: H.col(row, 'RENEWAL_TIMELINESS'),
          renewalDays: parseInt(H.col(row, 'RENEWAL_DAYS'), 10) || null,
          applicationUrl: H.col(row, 'APP_URL'),
          exhibitsUrl: H.col(row, 'EXHIBITS_URL'),
          correspondenceUrl: H.col(row, 'CORRESPONDENCE_URL'),
          rows: [],
          charges: [],
          frequencies: [],
          fixedCoords: [],
          mobileCoords: [],
          _seenCharges: {},
          _seenFreqs: {},
          _seenFixed: {},
          _seenMobile: {}
        };
      }
      var app = apps[groupKey];
      app.rows.push(row);

      // Deduplicate charges (rows are cross-product of charge x location x frequency)
      var charge = H.col(row, 'CHARGE');
      if (charge) {
        var openD = H.col(row, 'OPEN_DATE');
        var chargeKey = charge + '|' + openD;
        if (!app._seenCharges[chargeKey]) {
          app._seenCharges[chargeKey] = true;
          app.charges.push({
            charge: charge,
            openDate: H.parseDate(openD),
            closeDate: H.parseDate(H.col(row, 'CLOSE_DATE'))
          });
        }
      }

      // Deduplicate frequencies
      var freqLo = H.col(row, 'FREQ_LO');
      var freqHi = H.col(row, 'FREQ_HI');
      if (freqLo) {
        var freqKey = freqLo + '|' + freqHi;
        if (!app._seenFreqs[freqKey]) {
          app._seenFreqs[freqKey] = true;
          app.frequencies.push({ lo: freqLo, hi: freqHi });
        }
      }

      // Deduplicate fixed coordinates
      var fLatDeg = H.col(row, 'FIXED_LAT_DEG');
      if (fLatDeg) {
        var fLat = H.dmsToDecimal(fLatDeg, H.col(row, 'FIXED_LAT_MIN'), H.col(row, 'FIXED_LAT_SEC'), H.col(row, 'FIXED_LAT_DIR'));
        var fLon = H.dmsToDecimal(H.col(row, 'FIXED_LON_DEG'), H.col(row, 'FIXED_LON_MIN'), H.col(row, 'FIXED_LON_SEC'), H.col(row, 'FIXED_LON_DIR'));
        if (fLat !== 0 || fLon !== 0) {
          var fixedKey = fLat.toFixed(5) + ',' + fLon.toFixed(5);
          if (!app._seenFixed[fixedKey]) {
            app._seenFixed[fixedKey] = true;
            app.fixedCoords.push({ lat: fLat, lon: fLon });
          }
        }
      }

      // Deduplicate mobile coordinates
      var mLatDeg = H.col(row, 'MOBILE_LAT_DEG');
      if (mLatDeg) {
        var mLat = H.dmsToDecimal(mLatDeg, H.col(row, 'MOBILE_LAT_MIN'), H.col(row, 'MOBILE_LAT_SEC'), H.col(row, 'MOBILE_LAT_DIR'));
        var mLon = H.dmsToDecimal(H.col(row, 'MOBILE_LON_DEG'), H.col(row, 'MOBILE_LON_MIN'), H.col(row, 'MOBILE_LON_SEC'), H.col(row, 'MOBILE_LON_DIR'));
        var radius = H.radiusToMeters(H.col(row, 'MOBILE_RADIUS'));
        if (mLat !== 0 || mLon !== 0) {
          var mobileKey = mLat.toFixed(5) + ',' + mLon.toFixed(5);
          if (!app._seenMobile[mobileKey]) {
            app._seenMobile[mobileKey] = true;
            app.mobileCoords.push({ lat: mLat, lon: mLon, radius: radius });
          }
        }
      }
    });

    return Object.keys(apps).map(function(k) {
      var a = apps[k];
      delete a._seenCharges;
      delete a._seenFreqs;
      delete a._seenFixed;
      delete a._seenMobile;
      return a;
    });
  }

  // ============================================================
  // CSV PARSER (fallback)
  // ============================================================
  function parseCSV(text) {
    var lines = text.split(/\r?\n/);
    if (lines.length < 2) return [];
    var headers = parseCSVLine(lines[0]);
    var rows = [];
    for (var i = 1; i < lines.length; i++) {
      if (!lines[i].trim()) continue;
      var values = parseCSVLine(lines[i]);
      var obj = {};
      for (var j = 0; j < headers.length; j++) {
        obj[headers[j].trim().toLowerCase()] = (values[j] || '').trim();
      }
      rows.push(obj);
    }
    return rows;
  }

  function parseCSVLine(line) {
    var result = [];
    var current = '';
    var inQuotes = false;
    for (var i = 0; i < line.length; i++) {
      var ch = line[i];
      if (inQuotes) {
        if (ch === '"' && line[i + 1] === '"') {
          current += '"';
          i++;
        } else if (ch === '"') {
          inQuotes = false;
        } else {
          current += ch;
        }
      } else {
        if (ch === '"') {
          inQuotes = true;
        } else if (ch === ',') {
          result.push(current);
          current = '';
        } else {
          current += ch;
        }
      }
    }
    result.push(current);
    return result;
  }

  // ============================================================
  // DATA DIAGNOSTICS
  // ============================================================
  function showDiagnostics(rawData, fromCSV) {
    var isGrouped = (typeof DATA_FORMAT !== 'undefined' && DATA_FORMAT === 'grouped');
    var lines = [];
    lines.push('=== Data Pipeline Diagnostics ===');
    lines.push('Source: ' + (isGrouped ? 'pre-grouped data.js' : (fromCSV ? 'CSV import' : 'flat data.js')));
    lines.push('Raw rows: ' + rawData.length);
    lines.push('Applications after grouping: ' + D.allApps.length);

    // Column mapping check (only for flat/CSV data)
    if (!isGrouped && rawData.length > 0) {
      var sample = rawData[0];
      var csvCols = Object.keys(sample);
      var mapped = [];
      var unmapped = [];
      var colKeys = Object.keys(COLS);
      for (var i = 0; i < colKeys.length; i++) {
        var expected = COLS[colKeys[i]];
        if (sample.hasOwnProperty(expected)) {
          mapped.push(colKeys[i] + ' -> ' + expected);
        } else {
          unmapped.push(colKeys[i] + ' -> ' + expected + ' (NOT FOUND)');
        }
      }
      lines.push('');
      lines.push('Column mapping (' + mapped.length + '/' + colKeys.length + ' matched):');
      if (unmapped.length) {
        lines.push('  MISSING: ' + unmapped.join(', '));
      }
      var extraCols = csvCols.filter(function(c) {
        var vals = Object.keys(COLS).map(function(k) { return COLS[k]; });
        return vals.indexOf(c) === -1;
      });
      if (extraCols.length) {
        lines.push('  Unmapped CSV columns: ' + extraCols.join(', '));
      }
    }

    // Status distribution
    var statusCounts = {};
    D.allApps.forEach(function(a) {
      var s = a.status || '(empty)';
      statusCounts[s] = (statusCounts[s] || 0) + 1;
    });
    lines.push('');
    lines.push('Status distribution:');
    Object.keys(statusCounts).sort().forEach(function(s) {
      lines.push('  ' + s + ': ' + statusCounts[s]);
    });

    // Grouping sanity check
    if (!isGrouped && rawData.length > 0) {
      var fileNumbers = {};
      rawData.forEach(function(r) {
        var fn = r[COLS.FILE_NUMBER] || '';
        fileNumbers[fn] = (fileNumbers[fn] || 0) + 1;
      });
      var uniqueFNs = Object.keys(fileNumbers);
      lines.push('');
      lines.push('Grouping check:');
      lines.push('  Unique ' + COLS.FILE_NUMBER + ' values: ' + uniqueFNs.length);
      if (fileNumbers['']) {
        lines.push('  WARNING: ' + fileNumbers[''] + ' rows have empty ' + COLS.FILE_NUMBER);
      }
      // Show top 5 file numbers by row count
      var sorted = uniqueFNs.filter(function(f) { return f; }).map(function(f) {
        return { fn: f, count: fileNumbers[f] };
      }).sort(function(a, b) { return b.count - a.count; });
      lines.push('  Top 5 by row count:');
      for (var t = 0; t < Math.min(5, sorted.length); t++) {
        lines.push('    ' + sorted[t].fn + ': ' + sorted[t].count + ' rows');
      }
    }

    var diagText = lines.join('\n');
    console.log(diagText);

    // Render panel
    var $panel = $('<div id="diagnostics-panel" style="display:none;margin:10px 0">' +
      '<div class="chart-panel" style="background:#fffbe6;border-color:#ffbe2e">' +
      '<h5 style="margin:0 0 10px"><i class="fa fa-stethoscope"></i> Data Diagnostics</h5>' +
      '<pre style="font-size:0.8rem;white-space:pre-wrap;margin:0;max-height:400px;overflow-y:auto">' + diagText + '</pre>' +
      '</div></div>');
    $('#kpi-cards-row').before($panel);
    $('#btn-diagnostics').show().on('click', function() {
      $('#diagnostics-panel').toggle();
      $(this).find('.diag-label').text($('#diagnostics-panel').is(':visible') ? 'Hide Diag' : 'Show Diag');
    });
  }

  // ============================================================
  // EXPORT OPTIMIZED data.js
  // ============================================================
  function generateDataJS() {
    function fmtDate(d) {
      if (!d) return null;
      return d.getFullYear() + '-' + ('0' + (d.getMonth() + 1)).slice(-2) + '-' + ('0' + d.getDate()).slice(-2);
    }
    var today = fmtDate(new Date());
    var exportApps = D.allApps.map(function(a) {
      return {
        fileNumber: a.fileNumber,
        callsign: a.callsign,
        name: a.name,
        receiptDate: fmtDate(a.receiptDate),
        statusDate: fmtDate(a.statusDate),
        status: a.status,
        assigned: a.assigned || false,
        experimentType: a.experimentType,
        renewalTimeliness: a.renewalTimeliness || null,
        renewalDays: a.renewalDays || null,
        applicationUrl: a.applicationUrl || null,
        exhibitsUrl: a.exhibitsUrl || null,
        correspondenceUrl: a.correspondenceUrl || null,
        charges: a.charges.map(function(c) {
          return { charge: c.charge, openDate: fmtDate(c.openDate), closeDate: fmtDate(c.closeDate) };
        }),
        frequencies: a.frequencies,
        fixedCoords: a.fixedCoords.map(function(c) {
          return { lat: Math.round(c.lat * 1e6) / 1e6, lon: Math.round(c.lon * 1e6) / 1e6 };
        }),
        mobileCoords: a.mobileCoords.map(function(c) {
          return { lat: Math.round(c.lat * 1e6) / 1e6, lon: Math.round(c.lon * 1e6) / 1e6, radius: c.radius };
        })
      };
    });
    var lines = [];
    lines.push('var DATA_FORMAT="grouped";');
    lines.push('var DATA_DATE="' + today + '";');
    lines.push('var DATA=[');
    exportApps.forEach(function(app, i) {
      lines.push(JSON.stringify(app) + (i < exportApps.length - 1 ? ',' : ''));
    });
    lines.push('];');
    var blob = new Blob([lines.join('\n')], { type: 'application/javascript' });
    var url = URL.createObjectURL(blob);
    var a = document.createElement('a');
    a.href = url;
    a.download = 'data.js';
    a.click();
    URL.revokeObjectURL(url);
  }

  D.processData = processData;
  D.parseCSV = parseCSV;
  D.generateDataJS = generateDataJS;
  D.showDiagnostics = showDiagnostics;
})(jQuery);
