/* =========================================================
   page-daily-sales.js
   Chart.js 多圖控制 + 行事曆/日期切換邏輯
   配色從 CSS var 讀取（analysis-chart-theme.js 已注入 Chart.defaults）
   ========================================================= */
(function () {
  'use strict';

  function readDailySalesData() {
    const node = document.getElementById('daily-sales-data');
    if (!node && window.__DAILY_SALES__) return window.__DAILY_SALES__;
    if (!node) return null;
    const rawPayload = [
      node.content && node.content.textContent,
      node.textContent,
      node.innerHTML
    ].find(value => value && value.trim());
    if (!rawPayload) {
      console.error('[daily_sales] chart data is empty');
      return null;
    }
    try {
      return JSON.parse(rawPayload);
    } catch (error) {
      console.error('[daily_sales] chart data parse failed:', error);
      return null;
    }
  }

  const dailySalesData = readDailySalesData();
  if (!dailySalesData) return;
  window.__DAILY_SALES__ = dailySalesData;
  let chartsRendered = false;
  const isCompact = () =>
    window.matchMedia && window.matchMedia('(max-width: 768px)').matches;

  // -- Palette 讀自 CSS variable (analysis-chart-theme.js 公開) ----------
  function cssVar(name, fallback) {
    const v = getComputedStyle(document.documentElement)
      .getPropertyValue(name).trim();
    return v || fallback;
  }
  function rgba(hex, alpha) {
    if (!hex) return `rgba(0,0,0,${alpha})`;
    let c = hex.replace('#', '');
    if (c.length === 3) c = c.split('').map(x => x + x).join('');
    const num = parseInt(c, 16);
    if (Number.isNaN(num)) return hex;
    const r = (num >> 16) & 255, g = (num >> 8) & 255, b = num & 255;
    return `rgba(${r},${g},${b},${alpha})`;
  }

  const palette = {
    caramel:  cssVar('--momo-page-accent',  '#c96442'),
    honey:    cssVar('--momo-tag-honey',    '#b88416'),
    rust:     cssVar('--momo-tag-rust',     '#b5342f'),
    mahogany: cssVar('--momo-tag-mahogany', '#8f4530'),
    olive:    cssVar('--momo-tag-olive',    '#6f7a3a'),
    clay:     cssVar('--momo-tag-clay',     '#a0613a'),
    muted:    cssVar('--momo-text-muted',   '#7e6f5c')
  };

  function loadChartJs() {
    if (window.EwoooCChartTheme && window.EwoooCChartTheme.loadChartJs) {
      return window.EwoooCChartTheme.loadChartJs();
    }
    if (window.Chart) return Promise.resolve(window.Chart);
    return Promise.reject(new Error('Chart.js loader unavailable'));
  }

  function applyChartDefaults() {
    Chart.defaults.color = palette.muted;
    Chart.defaults.borderColor = rgba(palette.muted, 0.18);
    Chart.defaults.font.family = "'Noto Sans TC', 'Inter', system-ui, sans-serif";
  }

  // -- Safe data extraction ---------------------------------------------
  const cd = dailySalesData.chartData || {};
  const competitor = dailySalesData.competitor || {};
  const competitorTrend = competitor.trend || {};
  const competitorCoverage = competitor.coverage || {};
  const categoryChart = dailySalesData.categoryChart || {};
  const safe = {
    labels: cd.labels || [],
    revenue: cd.revenue || [],
    profit: cd.profit || [],
    margin_rate: cd.margin_rate || (cd.revenue || []).map((revenue, index) => {
      const rev = Number(revenue || 0);
      if (!rev) return 0;
      return Number((cd.profit || [])[index] || 0) / rev * 100;
    }),
    avg_price: cd.avg_price || [],
    qty: cd.qty || [],
    dod_revenue: cd.dod_revenue || [],
    dod_profit: cd.dod_profit || [],
    dod_avg_price: cd.dod_avg_price || [],
    dod_qty: cd.dod_qty || [],
    wow_revenue: cd.wow_revenue || [],
    wow_profit: cd.wow_profit || [],
    wow_avg_price: cd.wow_avg_price || [],
    wow_qty: cd.wow_qty || [],
    top10_labels: cd.top10_labels || [],
    top10_values: cd.top10_values || []
  };
  const chartInstances = [];

  function rememberChart(chart) {
    if (chart) chartInstances.push(chart);
    return chart;
  }

  function stabilizeCharts() {
    window.requestAnimationFrame(() => {
      chartInstances.forEach(chart => {
        if (!chart) return;
        if (typeof chart.resize === 'function') chart.resize();
        if (typeof chart.update === 'function') chart.update('none');
      });
      window.dispatchEvent(new Event('resize'));
    });
  }

  function formatShort(value, mode) {
    const n = Number(value || 0);
    if (mode === 'pct') return `${n.toFixed(1)}%`;
    if (mode === 'currency') return `$${Math.round(n).toLocaleString()}`;
    return Math.round(n).toLocaleString();
  }

  function formatMetric(value, mode) {
    const n = Number(value || 0);
    if (mode === 'pct') return `${n >= 0 ? '+' : ''}${n.toFixed(1)}%`;
    if (mode === 'currency') return `$${Math.round(n).toLocaleString()}`;
    return Math.round(n).toLocaleString();
  }

  function axisMoney(title) {
    return {
      beginAtZero: true,
      grace: '8%',
      title: { display: !isCompact(), text: title },
      ticks: { callback: value => formatMetric(value, 'currency') }
    };
  }

  function axisPercent(title) {
    return {
      beginAtZero: false,
      grace: '12%',
      title: { display: !isCompact(), text: title },
      ticks: { callback: value => formatMetric(value, 'pct') },
      grid: {
        color: context => Number(context.tick.value) === 0
          ? rgba(palette.muted, 0.38)
          : rgba(palette.muted, 0.12),
        lineWidth: context => Number(context.tick.value) === 0 ? 1.2 : 1
      }
    };
  }

  function renderHtmlBars(canvasId, labels, values, options = {}) {
    const canvas = document.getElementById(canvasId);
    const wrap = canvas ? canvas.closest('.chart-container') : null;
    if (!wrap) return;
    wrap.classList.add('chart-fallback-active');
    canvas.setAttribute('aria-hidden', 'true');
    if (wrap.querySelector('.chart-fallback-list, .chart-fallback-bars')) return;
    const pairs = (labels || []).map((label, index) => ({
      label: String(label || ''),
      value: Number((values || [])[index] || 0)
    })).filter(item => Number.isFinite(item.value));
    const data = options.limit ? pairs.slice(-options.limit) : pairs;
    if (!data.length) return;

    const max = Math.max(...data.map(item => Math.abs(item.value)), 1);
    const chart = document.createElement('div');
    chart.className = `chart-fallback-bars ${options.horizontal ? 'is-horizontal' : 'is-vertical'}`;

    data.forEach(item => {
      const bar = document.createElement('div');
      bar.className = `chart-fallback-bar ${item.value < 0 ? 'is-negative' : ''}`;
      const pct = Math.max(4, Math.round(Math.abs(item.value) / max * 100));
      if (options.horizontal) {
        bar.style.setProperty('--bar-w', `${pct}%`);
      } else {
        bar.style.setProperty('--bar-h', `${pct}%`);
      }

      const label = document.createElement('span');
      label.className = 'chart-fallback-label';
      label.textContent = item.label.length > 10 ? `${item.label.slice(0, 10)}...` : item.label;
      const value = document.createElement('strong');
      value.className = 'chart-fallback-value';
      value.textContent = formatShort(item.value, options.mode);
      bar.append(label, value);
      chart.appendChild(bar);
    });
    wrap.appendChild(chart);
  }

  function renderHtmlChartFallbacks() {
    renderHtmlBars('trendChart', safe.labels, safe.revenue, { mode: 'currency', limit: 14 });
    renderHtmlBars('dodChart', safe.labels, safe.dod_revenue, { mode: 'pct', limit: 14 });
    renderHtmlBars('wowChart', safe.labels, safe.wow_revenue, { mode: 'pct', limit: 14 });
    renderHtmlBars('top10Chart', safe.top10_labels, safe.top10_values, {
      mode: 'currency',
      horizontal: true
    });
    const mk = dailySalesData.marketing || {};
    if (mk.discount) renderHtmlBars('discountChart', mk.discount.labels, mk.discount.values, {
      mode: 'currency',
      horizontal: true
    });
    if (mk.coupon) renderHtmlBars('couponChart', mk.coupon.labels, mk.coupon.values, {
      mode: 'currency',
      horizontal: true
    });
    if (competitorTrend.labels) {
      renderHtmlBars('competitorGapChart', competitorTrend.labels, competitorTrend.avg_gap_pct, {
        mode: 'pct',
        limit: 14
      });
    }
    renderHtmlBars('marginChart', safe.labels, safe.margin_rate, { mode: 'pct', limit: 14 });
    renderHtmlBars('avgQtyChart', safe.labels, safe.avg_price, { mode: 'currency', limit: 14 });
    if (categoryChart.labels) {
      renderHtmlBars('categoryRevenueChart', categoryChart.labels, categoryChart.revenue, {
        mode: 'currency',
        horizontal: true
      });
    }
    const coverage = buildCoverageFunnel();
    renderHtmlBars('competitorCoverageChart', coverage.labels, coverage.values, {
      mode: 'number',
      horizontal: true
    });
  }

  function hasSeriesData(labels, ...seriesList) {
    return Array.isArray(labels) &&
      labels.length > 0 &&
      seriesList.some(series =>
        Array.isArray(series) &&
        series.some(value => Number.isFinite(Number(value)) && Math.abs(Number(value)) > 1e-9)
      );
  }

  function renderChartEmpty(canvasId, message) {
    const canvas = document.getElementById(canvasId);
    const wrap = canvas ? canvas.closest('.chart-container') : null;
    if (!wrap || wrap.querySelector('.chart-empty-state')) return;
    wrap.classList.add('chart-empty-active');
    canvas.setAttribute('aria-hidden', 'true');
    const empty = document.createElement('div');
    empty.className = 'chart-empty-state';
    empty.innerHTML = `<strong>尚無可繪製資料</strong><span>${message}</span>`;
    wrap.appendChild(empty);
  }

  // -- Helpers ----------------------------------------------------------
  function makeLineDataset(label, data, color, yAxisID) {
    return {
      label, data,
      borderColor: color,
      backgroundColor: rgba(color, 0.14),
      borderWidth: 2.4,
      tension: 0.34,
      cubicInterpolationMode: 'monotone',
      fill: false,
      pointRadius: context => context.dataIndex === context.dataset.data.length - 1 ? 3 : 1.6,
      pointHoverRadius: 5,
      pointHitRadius: 12,
      yAxisID
    };
  }

  function makeWowDataset(label, data, color) {
    const muted = palette.muted;
    return {
      ...makeLineDataset(label, data, color, undefined),
      segment: {
        borderColor: ctx => ctx.p0DataIndex < 7 ? rgba(muted, 0.6) : color
      }
    };
  }

  function buildCoverageFunnel() {
    return {
      labels: ['決策支援', '精準告警', '身份配對', '待刷新', '單位價', '待覆核'],
      values: [
        competitorCoverage.decision_support_count ?? competitorCoverage.decision_ready_count ?? 0,
        competitorCoverage.decision_ready_matches ?? competitorCoverage.fresh_matches ?? 0,
        competitorCoverage.valid_matches ?? 0,
        competitorCoverage.stale_matches ?? competitorCoverage.stale_match_count ?? 0,
        competitorCoverage.unit_comparable_count ?? 0,
        competitorCoverage.rescore_accepted_count ?? competitorCoverage.review_queue_count ?? 0
      ].map(value => Number(value || 0))
    };
  }

  // -- Chart 1: trend (multi-line) --------------------------------------
  function renderTrend() {
    const el = document.getElementById('trendChart');
    if (!el) return;
    if (!hasSeriesData(safe.labels, safe.revenue, safe.profit, safe.avg_price, safe.qty)) {
      renderChartEmpty('trendChart', '目前圖表 payload 沒有日別業績序列，請確認匯入資料與快取狀態。');
      return;
    }

    rememberChart(new Chart(el, {
      type: 'line',
      data: {
        labels: safe.labels,
        datasets: [
          makeLineDataset('業績', safe.revenue, palette.caramel, 'y'),
          makeLineDataset('毛利', safe.profit, palette.honey, 'y'),
          makeLineDataset('客單價', safe.avg_price, palette.mahogany, 'y1'),
          makeLineDataset('銷量', safe.qty, palette.olive, 'y2')
        ]
      },
      options: {
        responsive: true,
        maintainAspectRatio: false,
        interaction: { mode: 'index', intersect: false },
        plugins: {
          legend: { position: isCompact() ? 'bottom' : 'top' },
          tooltip: {
            callbacks: {
              label: ctx => {
                const metric = ctx.dataset.yAxisID === 'y2' ? 'number' : 'currency';
                return `${ctx.dataset.label}: ${formatMetric(ctx.parsed.y, metric)}`;
              }
            }
          }
        },
        scales: {
          x: { grid: { display: false }, ticks: { maxTicksLimit: isCompact() ? 5 : 10 } },
          y: { type: 'linear', position: 'left', ...axisMoney('業績 / 毛利') },
          y1: {
            type: 'linear', position: 'right', ...axisMoney('客單價'),
            grid: { drawOnChartArea: false },
          },
          y2: {
            type: 'linear', position: 'right', display: false, beginAtZero: true,
            grid: { drawOnChartArea: false }
          }
        }
      }
    }));
  }

  // -- Chart 2: DoD (multi-line %) --------------------------------------
  function renderDod() {
    const el = document.getElementById('dodChart');
    if (!el) return;
    if (!hasSeriesData(safe.labels, safe.dod_revenue, safe.dod_profit, safe.dod_avg_price, safe.dod_qty)) {
      renderChartEmpty('dodChart', '目前沒有 Day-over-Day 成長率序列。');
      return;
    }

    rememberChart(new Chart(el, {
      type: 'line',
      data: {
        labels: safe.labels,
        datasets: [
          makeLineDataset('業績 DoD%', safe.dod_revenue, palette.caramel),
          makeLineDataset('毛利 DoD%', safe.dod_profit, palette.honey),
          makeLineDataset('客單 DoD%', safe.dod_avg_price, palette.mahogany),
          makeLineDataset('銷量 DoD%', safe.dod_qty, palette.olive)
        ]
      },
      options: {
        responsive: true,
        maintainAspectRatio: false,
        interaction: { mode: 'index', intersect: false },
        plugins: {
          legend: { position: isCompact() ? 'bottom' : 'top' },
          tooltip: {
            callbacks: {
              label: ctx => `${ctx.dataset.label}: ${formatMetric(ctx.parsed.y, 'pct')}`
            }
          }
        },
        scales: {
          x: { grid: { display: false }, ticks: { maxTicksLimit: isCompact() ? 5 : 10 } },
          y: axisPercent('DoD 成長率')
        }
      }
    }));
  }

  // -- Chart 3: WoW (multi-line %, 前 7 天淡灰) -------------------------
  function renderWow() {
    const el = document.getElementById('wowChart');
    if (!el) return;
    if (!hasSeriesData(safe.labels, safe.wow_revenue, safe.wow_profit, safe.wow_avg_price, safe.wow_qty)) {
      renderChartEmpty('wowChart', '目前沒有 Week-over-Week 成長率序列。');
      return;
    }

    rememberChart(new Chart(el, {
      type: 'line',
      data: {
        labels: safe.labels,
        datasets: [
          makeWowDataset('業績 WoW%', safe.wow_revenue, palette.caramel),
          makeWowDataset('毛利 WoW%', safe.wow_profit, palette.honey),
          makeWowDataset('客單 WoW%', safe.wow_avg_price, palette.mahogany),
          makeWowDataset('銷量 WoW%', safe.wow_qty, palette.olive)
        ]
      },
      options: {
        responsive: true,
        maintainAspectRatio: false,
        interaction: { mode: 'index', intersect: false },
        plugins: {
          legend: { position: isCompact() ? 'bottom' : 'top' },
          tooltip: {
            callbacks: {
              label: ctx => {
                const v = ctx.parsed.y;
                const i = ctx.dataIndex;
                if (i < 7 || v === 0) {
                  return `${ctx.dataset.label}: 無對比資料（需上週同日數據）`;
                }
                return `${ctx.dataset.label}: ${formatMetric(v, 'pct')}`;
              }
            }
          }
        },
        scales: {
          x: { grid: { display: false }, ticks: { maxTicksLimit: isCompact() ? 5 : 10 } },
          y: axisPercent('WoW 成長率')
        }
      }
    }));
  }

  // -- Chart 4: Top 10 (橫向 bar) ---------------------------------------
  function renderTop10() {
    const el = document.getElementById('top10Chart');
    if (!el) return;
    if (!hasSeriesData(safe.top10_labels, safe.top10_values)) {
      renderChartEmpty('top10Chart', '所選日期沒有可排序的商品業績資料。');
      return;
    }

    rememberChart(new Chart(el, {
      type: 'bar',
      data: {
        labels: safe.top10_labels,
        datasets: [{
          label: '銷售金額',
          data: safe.top10_values,
          backgroundColor: rgba(palette.caramel, 0.62),
          borderColor: palette.caramel,
          borderWidth: 1,
          maxBarThickness: 22
        }]
      },
      options: {
        indexAxis: 'y',
        responsive: true,
        maintainAspectRatio: false,
        plugins: {
          legend: { display: false },
          tooltip: { callbacks: { label: ctx => formatMetric(ctx.parsed.x, 'currency') } }
        },
        scales: {
          x: axisMoney('銷售金額'),
          y: { grid: { display: false }, ticks: { autoSkip: false } }
        }
      }
    }));
  }

  // -- Chart 5: Margin rate trend --------------------------------------
  function renderMarginRate() {
    const el = document.getElementById('marginChart');
    if (!el) return;
    if (!hasSeriesData(safe.labels, safe.margin_rate)) {
      renderChartEmpty('marginChart', '目前沒有可計算的毛利率序列。');
      return;
    }

    rememberChart(new Chart(el, {
      type: 'line',
      data: {
        labels: safe.labels,
        datasets: [
          makeLineDataset('毛利率', safe.margin_rate, palette.olive),
          {
            label: '30 日均線',
            data: safe.margin_rate.map((_, index, values) => {
              const start = Math.max(0, index - 6);
              const sample = values.slice(start, index + 1).map(Number).filter(Number.isFinite);
              return sample.length ? sample.reduce((sum, value) => sum + value, 0) / sample.length : 0;
            }),
            borderColor: palette.honey,
            backgroundColor: rgba(palette.honey, 0.1),
            borderWidth: 1.8,
            borderDash: [5, 5],
            tension: 0.28,
            pointRadius: 0
          }
        ]
      },
      options: {
        responsive: true,
        maintainAspectRatio: false,
        interaction: { mode: 'index', intersect: false },
        plugins: {
          legend: { position: isCompact() ? 'bottom' : 'top' },
          tooltip: { callbacks: { label: ctx => `${ctx.dataset.label}: ${formatMetric(ctx.parsed.y, 'pct')}` } }
        },
        scales: {
          x: { grid: { display: false }, ticks: { maxTicksLimit: isCompact() ? 5 : 10 } },
          y: axisPercent('毛利率')
        }
      }
    }));
  }

  // -- Chart 6: AOV x Qty ----------------------------------------------
  function renderAvgQty() {
    const el = document.getElementById('avgQtyChart');
    if (!el) return;
    if (!hasSeriesData(safe.labels, safe.avg_price, safe.qty)) {
      renderChartEmpty('avgQtyChart', '目前沒有客單價或銷量序列。');
      return;
    }

    rememberChart(new Chart(el, {
      type: 'bar',
      data: {
        labels: safe.labels,
        datasets: [
          {
            label: '銷量',
            data: safe.qty,
            backgroundColor: rgba(palette.olive, 0.24),
            borderColor: palette.olive,
            borderWidth: 1,
            maxBarThickness: 24,
            yAxisID: 'y'
          },
          {
            label: '客單價',
            data: safe.avg_price,
            type: 'line',
            borderColor: palette.mahogany,
            backgroundColor: rgba(palette.mahogany, 0.12),
            borderWidth: 2.2,
            tension: 0.32,
            pointRadius: 2,
            yAxisID: 'y1'
          }
        ]
      },
      options: {
        responsive: true,
        maintainAspectRatio: false,
        interaction: { mode: 'index', intersect: false },
        plugins: {
          legend: { position: isCompact() ? 'bottom' : 'top' },
          tooltip: {
            callbacks: {
              label: ctx => {
                const mode = ctx.dataset.yAxisID === 'y1' ? 'currency' : 'number';
                return `${ctx.dataset.label}: ${formatMetric(ctx.parsed.y, mode)}`;
              }
            }
          }
        },
        scales: {
          x: { grid: { display: false }, ticks: { maxTicksLimit: isCompact() ? 5 : 10 } },
          y: { beginAtZero: true, title: { display: !isCompact(), text: '銷量' } },
          y1: {
            position: 'right',
            grid: { drawOnChartArea: false },
            ...axisMoney('客單價')
          }
        }
      }
    }));
  }

  // -- Chart 7: Category revenue ---------------------------------------
  function renderCategoryRevenue() {
    const el = document.getElementById('categoryRevenueChart');
    if (!el) return;
    if (!hasSeriesData(categoryChart.labels, categoryChart.revenue, categoryChart.profit)) {
      renderChartEmpty('categoryRevenueChart', '目前沒有分類業績彙總可繪製。');
      return;
    }

    rememberChart(new Chart(el, {
      type: 'bar',
      data: {
        labels: categoryChart.labels || [],
        datasets: [
          {
            label: '業績',
            data: categoryChart.revenue || [],
            backgroundColor: rgba(palette.caramel, 0.5),
            borderColor: palette.caramel,
            borderWidth: 1,
            maxBarThickness: 22
          },
          {
            label: '毛利',
            data: categoryChart.profit || [],
            backgroundColor: rgba(palette.olive, 0.42),
            borderColor: palette.olive,
            borderWidth: 1,
            maxBarThickness: 22
          }
        ]
      },
      options: {
        indexAxis: 'y',
        responsive: true,
        maintainAspectRatio: false,
        plugins: {
          legend: { position: isCompact() ? 'bottom' : 'top' },
          tooltip: { callbacks: { label: ctx => `${ctx.dataset.label}: ${formatMetric(ctx.parsed.x, 'currency')}` } }
        },
        scales: {
          x: axisMoney('金額'),
          y: {
            grid: { display: false },
            ticks: {
              autoSkip: false,
              callback: function (value) {
                const label = this.getLabelForValue(value);
                return label.length > 14 ? `${label.slice(0, 14)}…` : label;
              }
            }
          }
        }
      }
    }));
  }

  // -- Chart 8: Competitor gap pressure --------------------------------
  function renderCompetitorGap() {
    const el = document.getElementById('competitorGapChart');
    if (!el) return;
    const labels = competitorTrend.labels || [];
    const avgGap = competitorTrend.avg_gap_pct || [];
    const riskCount = competitorTrend.risk_count || [];
    if (!hasSeriesData(labels, avgGap, riskCount)) {
      renderChartEmpty('competitorGapChart', '目前沒有高信心 PChome 歷史價差序列。');
      return;
    }

    rememberChart(new Chart(el, {
      type: 'bar',
      data: {
        labels,
        datasets: [
          {
            label: 'MOMO 較 PChome 貴 SKU 數',
            data: riskCount,
            backgroundColor: rgba(palette.rust, 0.28),
            borderColor: palette.rust,
            borderWidth: 1,
            maxBarThickness: 24,
            yAxisID: 'y'
          },
          {
            label: '平均價差 %',
            data: avgGap,
            type: 'line',
            borderColor: palette.caramel,
            backgroundColor: rgba(palette.caramel, 0.12),
            borderWidth: 2.2,
            tension: 0.32,
            pointRadius: 2,
            yAxisID: 'y1'
          }
        ]
      },
      options: {
        responsive: true,
        maintainAspectRatio: false,
        interaction: { mode: 'index', intersect: false },
        plugins: {
          legend: { position: isCompact() ? 'bottom' : 'top' },
          tooltip: {
            callbacks: {
              label: ctx => {
                const mode = ctx.dataset.yAxisID === 'y1' ? 'pct' : 'number';
                return `${ctx.dataset.label}: ${formatMetric(ctx.parsed.y, mode)}`;
              }
            }
          }
        },
        scales: {
          x: { grid: { display: false }, ticks: { maxTicksLimit: isCompact() ? 5 : 10 } },
          y: { beginAtZero: true, title: { display: !isCompact(), text: '風險 SKU 數' } },
          y1: {
            position: 'right',
            grid: { drawOnChartArea: false },
            ...axisPercent('平均價差')
          }
        }
      }
    }));
  }

  // -- Chart 9: Competitor decision coverage ---------------------------
  function renderCompetitorCoverage() {
    const el = document.getElementById('competitorCoverageChart');
    if (!el) return;
    const coverage = buildCoverageFunnel();
    if (!hasSeriesData(coverage.labels, coverage.values)) {
      renderChartEmpty('competitorCoverageChart', '目前尚未形成可繪製的比價覆蓋資料。');
      return;
    }

    rememberChart(new Chart(el, {
      type: 'bar',
      data: {
        labels: coverage.labels,
        datasets: [{
          label: 'SKU 數',
          data: coverage.values,
          backgroundColor: [
            rgba(palette.caramel, 0.62),
            rgba(palette.olive, 0.54),
            rgba(palette.honey, 0.54),
            rgba(palette.rust, 0.38),
            rgba(palette.mahogany, 0.32),
            rgba(palette.muted, 0.24)
          ],
          borderColor: [
            palette.caramel,
            palette.olive,
            palette.honey,
            palette.rust,
            palette.mahogany,
            palette.muted
          ],
          borderWidth: 1,
          maxBarThickness: 18
        }]
      },
      options: {
        indexAxis: 'y',
        responsive: true,
        maintainAspectRatio: false,
        plugins: {
          legend: { display: false },
          tooltip: { callbacks: { label: ctx => `${ctx.dataset.label}: ${formatMetric(ctx.parsed.x, 'number')}` } }
        },
        scales: {
          x: { beginAtZero: true, ticks: { precision: 0 } },
          y: { grid: { display: false } }
        }
      }
    }));
  }

  // -- Marketing charts ----------------------------------------------
  function renderMarketingBar(elId, marketing, color) {
    const el = document.getElementById(elId);
    if (!el || !marketing) return;
    if (!hasSeriesData(marketing.labels, marketing.values)) {
      renderChartEmpty(elId, '所選期間沒有對應的行銷活動業績資料。');
      return;
    }
    const shades = Array.from({ length: marketing.labels.length }, (_, i) => {
      const a = 0.8 - i * 0.05;
      return rgba(color, Math.max(a, 0.25));
    });

    rememberChart(new Chart(el.getContext('2d'), {
      type: 'bar',
      data: {
        labels: marketing.labels,
        datasets: [{
          label: '業績',
          data: marketing.values,
          backgroundColor: shades,
          borderColor: color,
          borderWidth: 1,
          maxBarThickness: 22
        }]
      },
      options: {
        indexAxis: 'y',
        responsive: true,
        maintainAspectRatio: false,
        plugins: {
          legend: { display: false },
          tooltip: {
            callbacks: { label: ctx => formatMetric(ctx.parsed.x, 'currency') }
          }
        },
        scales: {
          x: axisMoney('業績'),
          y: {
            grid: { display: false },
            ticks: {
              autoSkip: false,
              font: { size: 11 },
              callback: function (v) {
                const lbl = this.getLabelForValue(v);
                return lbl.length > 25 ? lbl.substr(0, 25) + '…' : lbl;
              }
            }
          }
        },
        onClick: () => exportMarketingData()
      }
    }));
  }

  // -- DataTables init ------------------------------------------------
  function initDataTable() {
    if (typeof $ === 'undefined' || !$.fn.DataTable) return;
    $('#categoryTable').DataTable({
      order: [[2, 'desc']],
      pageLength: 25,
      language: {
        url: 'https://cdn.datatables.net/plug-ins/1.11.5/i18n/zh-HANT.json'
      }
    });
  }

  // -- Navigation handlers -------------------------------------------
  function changeDate() {
    const sel = document.getElementById('dateSelector');
    if (!sel) return;
    window.location.href = `/daily_sales?date=${sel.value}`;
  }

  function toggleDateSelection(clickedDate, currentSelectedDate) {
    const isMonthView = window.__DAILY_SALES__.isMonthView;
    if (isMonthView) {
      window.location.href = `/daily_sales?date=${clickedDate}`;
      return;
    }
    if (clickedDate === currentSelectedDate) {
      window.backToMonthView();
    } else {
      window.location.href = `/daily_sales?date=${clickedDate}`;
    }
  }

  function backToMonthView() {
    const params = new URLSearchParams(window.location.search);
    const month = params.get('month');
    window.location.href = month
      ? `/daily_sales?month=${month}`
      : '/daily_sales';
  }

  function changeMonth(month) {
    window.location.href = `/daily_sales?month=${month}`;
  }

  // -- Export handlers -----------------------------------------------
  function exportCategoryTable() {
    const sel = document.getElementById('dateSelector');
    if (!sel || !sel.value) { alert('請先選擇日期'); return; }
    window.location.href = `/daily_sales/export?date=${sel.value}`;
  }

  function exportMarketingData() {
    const sel = document.getElementById('dateSelector');
    const date = sel ? sel.value : '';
    const params = new URLSearchParams(window.location.search);
    const isMonthView = !params.has('date');

    let url = '/daily_sales/export_marketing?type=all';
    if (isMonthView) {
      const month = params.get('month') || new Date().toISOString().slice(0, 7);
      const [y, m] = month.split('-');
      const start = `${y}-${m}-01`;
      const end = new Date(y, m, 0).toISOString().slice(0, 10);
      url += `&start_date=${start}&end_date=${end}`;
    } else {
      url += `&date=${date}`;
    }
    window.location.href = url;
  }

  function initDailySalesActions() {
    const page = document.querySelector('.daily-sales-page');
    const selectedDate = page ? page.dataset.selectedDate : '';

    document.querySelectorAll('[data-daily-date-selector]').forEach(select => {
      select.addEventListener('change', changeDate);
    });

    document.querySelectorAll('[data-daily-action="month-view"]').forEach(control => {
      control.addEventListener('click', event => {
        event.preventDefault();
        backToMonthView();
      });
      control.addEventListener('keydown', event => {
        if (event.key !== 'Enter' && event.key !== ' ') return;
        event.preventDefault();
        backToMonthView();
      });
    });

    document.querySelectorAll('[data-daily-month]').forEach(button => {
      button.addEventListener('click', () => changeMonth(button.dataset.dailyMonth));
    });

    document.querySelectorAll('.cal-day[data-has-data="true"][data-date]').forEach(day => {
      day.addEventListener('click', () => toggleDateSelection(day.dataset.date, selectedDate));
      day.addEventListener('keydown', event => {
        if (event.key !== 'Enter' && event.key !== ' ') return;
        event.preventDefault();
        toggleDateSelection(day.dataset.date, selectedDate);
      });
      day.setAttribute('role', 'button');
      day.setAttribute('tabindex', '0');
    });

    document.querySelectorAll('[data-daily-export="marketing"]').forEach(button => {
      button.addEventListener('click', exportMarketingData);
    });
    document.querySelectorAll('[data-daily-export="category"]').forEach(button => {
      button.addEventListener('click', exportCategoryTable);
    });
  }

  // -- Boot -----------------------------------------------------------
  function renderAllCharts() {
    if (chartsRendered) return;
    chartsRendered = true;
    applyChartDefaults();
    renderTrend();
    renderDod();
    renderWow();
    renderTop10();
    renderMarginRate();
    renderAvgQty();
    renderCategoryRevenue();
    renderCompetitorGap();
    renderCompetitorCoverage();

    const mk = dailySalesData.marketing || {};
    if (mk.discount) renderMarketingBar('discountChart', mk.discount, palette.caramel);
    if (mk.coupon)   renderMarketingBar('couponChart',   mk.coupon,   palette.olive);
    stabilizeCharts();
  }

  function bootCharts() {
    document.documentElement.dataset.dailyCharts = 'loading';
    loadChartJs()
      .then(() => {
        renderAllCharts();
        document.documentElement.dataset.dailyCharts = 'ready';
      })
      .catch(error => {
        document.documentElement.dataset.dailyCharts = 'error';
        document.documentElement.dataset.dailyChartsError = error && error.message ? error.message : String(error);
        console.error('[daily_sales] Chart.js 載入失敗:', error);
        renderHtmlChartFallbacks();
      });
  }

  function scheduleChartBoot() {
    window.setTimeout(bootCharts, 0);
  }

  function observeCharts() {
    const targets = Array.from(document.querySelectorAll('.chart-card'));
    if (!targets.length) return;
    if (!('IntersectionObserver' in window)) {
      bootCharts();
      return;
    }

    const observer = new IntersectionObserver(entries => {
      if (!entries.some(entry => entry.isIntersecting)) return;
      observer.disconnect();
      bootCharts();
    }, { rootMargin: '260px 0px' });

    targets.forEach(target => observer.observe(target));
  }

  document.addEventListener('DOMContentLoaded', function () {
    initDailySalesActions();
    initDataTable();
    scheduleChartBoot();
  });
})();
