<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title>Flask CSV Dashboard</title>

  <!-- 1) Load Papa Parse from a CDN -->
  <script src="https://cdn.jsdelivr.net/npm/papaparse@5.3.2/papaparse.min.js"></script>

  <!-- 2) Load Chart.js from a CDN -->
  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>

  <!-- 3) Load jQuery and DataTables (for the sortable/searchable table) -->
  <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
  <link
    rel="stylesheet"
    href="https://cdn.datatables.net/1.13.3/css/jquery.dataTables.min.css"
  />
  <script src="https://cdn.datatables.net/1.13.3/js/jquery.dataTables.min.js"></script>

  <style>
    body {
      font-family: Arial, sans-serif;
      margin: 20px;
    }
    canvas {
      border: 1px solid #ccc;
      margin-bottom: 1em;
    }
    #pairDetails {
      white-space: pre;
      font-family: monospace;
    }
    .chart-container {
      display: flex;
      gap: 20px;
    }
  </style>
</head>
<body>
<h1>LLM Prior Metrics Analysis</h1>

<!-- Dropdown for selecting CSV file (populated dynamically) -->
<label for="csvSelector">Select CSV File:</label>
<select id="csvSelector" onchange="reloadData()">
  <!-- Options will be added dynamically by loadCsvFileList() -->
</select>

<br /><br />

<!-- Dropdown for selecting the metric to plot (line & histogram) -->
<label for="metricSelector">Select Metric:</label>
<select id="metricSelector" onchange="updateAllCharts()">
  <option value="density_obs">density_obs</option>
  <!-- <option value="density_mean">density_mean</option> -->
  <option value="probability_005">probability_005</option>
  <option value="probability_01">probability_01</option>
  <option value="probability_015">probability_015</option>
  <option value="probability_02">probability_02</option>
  <option value="r_obs">r_obs</option>
  <option value="p_val">p_val</option>
</select>

<div class="chart-container">
  <!--
  <canvas id="lineChart" width="600" height="300"></canvas>
  -->
  <canvas id="histChart" width="600" height="300"></canvas>

  <!-- New chart for r_obs bins -->
  <canvas id="histChartByR" width="600" height="300"></canvas>
</div>

<!-- Histogram bin size inputs -->
<label for="binSizeInput">Bin Size (main histogram):</label>
<input
  id="binSizeInput"
  type="number"
  value="0.1"
  step="0.1"
  onchange="updateAllCharts()"
/>
<label for="binSizeInputR">Bin Size (r_obs histogram):</label>
<input
  id="binSizeInputR"
  type="number"
  value="0.1"
  step="0.1"
  onchange="updateAllCharts()"
/>

<div id="metricStats" style="margin-top: 1em; font-weight: bold;"></div>
<!-- New div for sign accuracy -->
<div id="signAccuracy" style="margin-top: 1em; font-weight: bold;"></div>

<hr />

<!-- Search for a specific pair_id -->
<!-- <label for="pairIdInput">Enter a pair_id:</label>
<input id="pairIdInput" type="text" placeholder="e.g. pair0023" />
<button onclick="searchPair()">Search</button>
<div id="pairDetails" style="margin-top: 1em;"></div> -->
<div id="searchResults" style="margin-top: 1em; color: green;"></div>

<hr />

<h2>Correlations</h2>
<button onclick="filterIncorrectCorrelations()">Show Incorrectly Predicted Signs</button>
<button onclick="showAllCorrelations()">Show All Correlations</button>
<table id="myTable" class="display" style="width:100%">
  <thead>
    <tr>
      <!-- Table header columns will be dynamically generated -->
    </tr>
  </thead>
  <tbody>
    <!-- Table body will be dynamically generated -->
  </tbody>
</table>

<script>
/* ------------------------------------------------------
   GLOBAL VARIABLES
------------------------------------------------------ */
let csvData = [];              // Parsed CSV data (array of objects)
let allCsvData = [];           // keep a copy of original CSV data
// let lineChartInstance = null;  // Chart.js instance for line chart
let histChartInstance = null;  // Chart.js instance for histogram
let histChartByR = null;       // Chart.js instance for r_obs histogram

/* ------------------------------------------------------
   ON PAGE LOAD
   1) Load the list of available CSV files from /list-outputs
   2) Populate the dropdown
   3) Automatically load the first CSV
------------------------------------------------------ */
document.addEventListener('DOMContentLoaded', () => {
  loadCsvFileList();
});

/**
 * loadCsvFileList:
 * Fetch the array of CSV filenames from our Flask endpoint /list-outputs,
 * then populate the #csvSelector dropdown.
 */
function loadCsvFileList() {
  fetch('/list-outputs') 
    .then(response => response.json())
    .then(files => {
      const csvSelector = document.getElementById('csvSelector');
      csvSelector.innerHTML = '';  // Clear any existing options

      files.forEach((file, index) => {
        const option = document.createElement('option');
        option.value = file;
        option.textContent = file;
        csvSelector.appendChild(option);
      });

      // If there is at least one file, automatically load it
      if (files.length > 0) {
        csvSelector.value = files[0];
        reloadData();
      }
    })
    .catch(err => console.error('Error fetching CSV file list:', err));
}

/* ------------------------------------------------------
   1. FETCH and PARSE the selected CSV using Papa Parse
------------------------------------------------------ */
function reloadData() {
  const selectedFile = document.getElementById('csvSelector').value;
  const filePath = '/outputs/' + selectedFile;
  fetchAndParseCSV(filePath, selectedFile);
}

function fetchAndParseCSV(filePath, selectedFile) {
  fetch(filePath)
    .then(response => {
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
      return response.text();
    })
    .then(csvText => {
      Papa.parse(csvText, {
        header: true,
        dynamicTyping: true,
        skipEmptyLines: true,
        complete: (results) => {
          if (results.errors.length) {
            console.error('PapaParse errors:', results.errors);
          }
          csvData = results.data;
          allCsvData = results.data.slice(); // store a copy
          console.log('CSV data loaded:', csvData);
          showMetricStats();
          // if (lineChartInstance) { lineChartInstance.destroy(); lineChartInstance = null; }
          if (histChartInstance) { histChartInstance.destroy(); histChartInstance = null; }
          if (histChartByR) { histChartByR.destroy(); histChartByR = null; }
          initCharts();
          initDataTable(csvData);
          // Pass the selected file as query parameter
          loadSignAccuracy(selectedFile);
        }
      });
    })
    .catch(err => console.error('Error loading or parsing CSV:', err));
}

/* ------------------------------------------------------
   2. CHARTS (Line + Histogram)
------------------------------------------------------ */
function initCharts() {
  // LINE CHART
  // const lineCtx = document.getElementById('lineChart').getContext('2d');
  // lineChartInstance = new Chart(lineCtx, {
  //   type: 'line',
  //   data: {
  //     labels: [], 
  //     datasets: [
  //       {
  //         label: '',
  //         data: [],
  //         backgroundColor: 'rgba(54, 162, 235, 0.2)',
  //         borderColor: 'rgba(54, 162, 235, 1)',
  //         borderWidth: 2
  //       }
  //     ]
  //   },
  //   options: {
  //     responsive: false,
  //     scales: {
  //       x: {
  //         title: {
  //           display: true,
  //           text: 'pair_id'
  //         }
  //       },
  //       y: {
  //         title: {
  //           display: true,
  //           text: 'Metric Value'
  //         }
  //       }
  //     }
  //   }
  // });

  // HISTOGRAM CHART
  const histCtx = document.getElementById('histChart').getContext('2d');
  histChartInstance = new Chart(histCtx, {
    type: 'bar',
    data: {
      labels: [],
      datasets: [
        {
          label: 'Histogram of Metric',
          data: [],
          backgroundColor: 'rgba(255, 99, 132, 0.5)',
          borderColor: 'rgba(255, 99, 132, 1)',
          borderWidth: 1
        }
      ]
    },
    options: {
      responsive: false,
      scales: {
        x: {
          title: {
            display: true,
            text: 'Value Range (bins)'
          }
        },
        y: {
          title: {
            display: true,
            text: 'Count'
          }
        }
      }
    }
  });

  // HISTOGRAM CHART BY R
  const histByRCtx = document.getElementById('histChartByR').getContext('2d');
  histChartByR = new Chart(histByRCtx, {
    type: 'bar',
    data: {
      labels: [],
      datasets: [
        {
          label: 'Avg Metric vs. r_obs',
          data: []
        }
      ]
    },
    options: {
      responsive: false,
      scales: {
        x: {
          title: {
            display: true,
            text: 'r_obs bins'
          }
        },
        y: {
          title: {
            display: true,
            text: 'Average Metric'
          }
        }
      }
    }
  });

  // Update both charts once they're initialized
  updateAllCharts();
}

/** Called when either the metric or bin size changes. */
function updateAllCharts() {
  // updateLineChart();
  updateHistogramChart();
  updateHistogramByR();
  showMetricStats();
}

/** Plot the selected metric vs. pair_id. */
// function updateLineChart() {
//   if (!lineChartInstance || !csvData || csvData.length === 0) return;
//   const metric = document.getElementById('metricSelector').value;

//   // X-axis: pair_id
//   const labels = csvData.map(row => row['pair_id']);
//   // Y-axis: selected metric
//   const values = csvData.map(row => (typeof row[metric] === 'number') ? row[metric] : null);

//   lineChartInstance.data.labels = labels;
//   lineChartInstance.data.datasets[0].label = metric;
//   lineChartInstance.data.datasets[0].data = values;
//   lineChartInstance.update();
// }

/** Create a histogram of the selected metric. */
function updateHistogramChart() {
  if (!histChartInstance || !csvData || csvData.length === 0) return;
  const metric = document.getElementById('metricSelector').value;
  // Check if metric exists
  if (!(metric in csvData[0])) {
    alert(`Metric "${metric}" not found in CSV. Please select a valid metric.`);
    return;
  }
  const binSize = parseFloat(document.getElementById('binSizeInput').value);

  // collect numeric values
  const values = csvData
    .map(row => row[metric])
    .filter(val => typeof val === 'number' && !isNaN(val));

  if (values.length === 0 || binSize <= 0) {
    alert(`No numeric data for metric "${metric}".`);
    histChartInstance.data.labels = [];
    histChartInstance.data.datasets[0].data = [];
    histChartInstance.data.datasets[0].label = `Histogram of ${metric}`;
    histChartInstance.update();
    return;
  }

  // find min and max
  const minVal = Math.min(...values);
  const maxVal = Math.max(...values);

  // build bin edges
  const bins = [];
  for (let b = minVal; b <= maxVal; b += binSize) {
    bins.push(b);
  }
  // ensure maxVal is included
  if (bins[bins.length - 1] < maxVal) {
    bins.push(maxVal);
  }

  // counts array
  const counts = new Array(bins.length - 1).fill(0);
  values.forEach(val => {
    for (let i = 0; i < bins.length - 1; i++) {
      if (val >= bins[i] && val < bins[i + 1]) {
        counts[i]++;
        break;
      }
      // if val is exactly the last bin edge
      if (i === bins.length - 2 && val === bins[bins.length - 1]) {
        counts[i]++;
        break;
      }
    }
  });

  // labels like [start, end)
  const binLabels = [];
  for (let i = 0; i < bins.length - 1; i++) {
    const start = bins[i].toFixed(2);
    const end = bins[i + 1].toFixed(2);
    binLabels.push(`[${start}, ${end})`);
  }

  // update histogram
  histChartInstance.data.labels = binLabels;
  histChartInstance.data.datasets[0].label = `Histogram of ${metric}`;
  histChartInstance.data.datasets[0].data = counts;
  histChartInstance.update();
}

/** Create a histogram of the selected metric binned by r_obs. */
function updateHistogramByR() {
  if (!histChartByR || !csvData || csvData.length === 0) return;
  const metric = document.getElementById('metricSelector').value;
  const rValues = csvData.map(row => row['r_obs']).filter(v => typeof v === 'number');
  const mValues = csvData.map(row => row[metric]).filter(v => typeof v === 'number');

  if (!rValues.length || !mValues.length) {
    histChartByR.data.labels = [];
    histChartByR.data.datasets[0].data = [];
    histChartByR.update();
    return;
  }

  // Define r_obs bin size
  const binSize = parseFloat(document.getElementById('binSizeInputR').value);
  const minR = Math.min(...rValues);
  const maxR = Math.max(...rValues);
  const rBins = [];
  for (let b = minR; b <= maxR; b += binSize) {
    rBins.push(b);
  }
  if (rBins[rBins.length - 1] < maxR) {
    rBins.push(maxR);
  }

  // Prepare arrays to store sums and counts for averaging
  const sums = new Array(rBins.length - 1).fill(0);
  const counts = new Array(rBins.length - 1).fill(0);
  csvData.forEach(row => {
    const rVal = row['r_obs'];
    const mVal = row[metric];
    if (typeof rVal === 'number' && typeof mVal === 'number') {
      for (let i = 0; i < rBins.length - 1; i++) {
        if (rVal >= rBins[i] && rVal < rBins[i + 1]) {
          sums[i] += mVal;
          counts[i]++;
          break;
        }
      }
    }
  });

  // Build labels and average array
  const binLabels = [];
  const avgValues = [];
  for (let i = 0; i < rBins.length - 1; i++) {
    binLabels.push(`[${rBins[i].toFixed(2)}, ${rBins[i + 1].toFixed(2)})`);
    avgValues.push(counts[i] ? sums[i] / counts[i] : 0);
  }

  // Update chart
  histChartByR.data.labels = binLabels;
  histChartByR.data.datasets[0].label = `Avg of ${metric} vs. r_obs`;
  histChartByR.data.datasets[0].data = avgValues;
  histChartByR.update();
}

/* ------------------------------------------------------
   3. SEARCH a Pair
------------------------------------------------------ */
function searchPair() {
  const inputVal = document.getElementById('pairIdInput').value.trim();
  const row = csvData.find(r => r['pair_id'] === inputVal);
  if (!row) {
    document.getElementById('searchResults').textContent = `No data found for pair_id: ${inputVal}`;
    return;
  }
  const snippet = `Found pair_id: ${row['pair_id']}, r_obs: ${row['r_obs']}, predicted_coef: ${row['predicted_coef']}`;
  document.getElementById('searchResults').textContent = snippet;
}

/* ------------------------------------------------------
   4. DATATABLES for a sortable/searchable table
------------------------------------------------------ */
function initDataTable(data) {
  if (!data || data.length === 0) { return; }
  // 1) Extract column names from the first row
  const colKeys = Object.keys(data[0]);

  // 2) DataTables needs a "columns" array with { data, title }
  const columnsDef = colKeys.map(key => ({
    data: key,
    title: key,
    // wrap long text for display
    render: function (data, type) {
      if (type === 'display') {
        return `<div style="max-height: 80px; overflow-y: auto;">${data}</div>`;
      }
      return data;
    }
  }));
  
  // Build the table header <thead> based on columnsDef
  let headerHtml = '<tr>';
  columnsDef.forEach(col => {
    headerHtml += `<th>${col.title}</th>`;
  });
  headerHtml += '</tr>';
  document.querySelector('#myTable thead').innerHTML = headerHtml;
  
  $(document).ready(function () {
    // Destroy if already initialized
    if ($.fn.DataTable.isDataTable('#myTable')) {
      $('#myTable').DataTable().clear().destroy();
    }
    $('#myTable').DataTable({
      data: data,
      columns: columnsDef,
      pageLength: 10,
      lengthMenu: [5, 10, 25, 50, 100]
    });
  });
}

function showMetricStats() {
  const metric = document.getElementById('metricSelector').value;
  const values = csvData.map(row => row[metric]).filter(v => typeof v === 'number' && !isNaN(v));
  if (!values.length) {
    document.getElementById('metricStats').textContent = 'No data for selected metric.';
    return;
  }
  const mean = values.reduce((a, b) => a + b, 0) / values.length;
  const variance = values.reduce((acc, val) => acc + Math.pow(val - mean, 2), 0) / values.length;
  const std = Math.sqrt(variance);
  document.getElementById('metricStats').textContent =
    `Mean of ${metric}: ${mean.toFixed(3)}, Std Dev: ${std.toFixed(3)}`;
}

// Add a new function to load sign accuracy from the API
function loadSignAccuracy(fileName) {
  console.log("Requesting sign accuracy for file:", fileName);  // added log for debugging
  fetch('/api/sign_accuracy?file=' + encodeURIComponent(fileName))
    .then(response => {
      if (!response.ok) {
        throw new Error(`Sign accuracy API call failed: ${response.status}`);
      }
      return response.json();
    })
    .then(data => {
      console.log("API response:", data);  // added log to inspect API response
      if (data.error) {
        document.getElementById('signAccuracy').textContent = "Error: " + data.error;
      } else {
        document.getElementById('signAccuracy').textContent =
          `Sign Prediction Accuracy: Correct: ${data.correct}, Incorrect: ${data.incorrect}`;
      }
    })
    .catch(err => {
      console.error('Error fetching sign accuracy:', err);
      document.getElementById('signAccuracy').textContent = "Error loading sign accuracy.";
    });
}

function filterIncorrectCorrelations() {
  const filtered = csvData.filter(row => {
    if (typeof row.r_obs !== 'number' || typeof row.predicted_coef !== 'number') return false;
    return (row.r_obs >= 0 ? 1 : -1) !== (row.predicted_coef >= 0 ? 1 : -1);
  });
  initDataTable(filtered);
}

function showAllCorrelations() {
  initDataTable(allCsvData);
}
</script>
</body>
</html>
