import { Component, ElementRef, Input, OnChanges, SimpleChanges, ViewChild } from '@angular/core';
import type { ForecastPoint, WeatherPoint } from './api.service';

@Component({
  selector: 'chart-panel',
  standalone: true,
  templateUrl: './chart-panel.component.html'
})
export class ChartPanelComponent implements OnChanges {
  @Input() history: WeatherPoint[] = [];
  @Input() forecast: ForecastPoint[] = [];
  @Input() legendMode: string = 'combo_standard';

  @ViewChild('canvas', { static: true }) canvas!: ElementRef<HTMLCanvasElement>;
  @ViewChild('accPanel', { static: true }) accPanel!: ElementRef<HTMLDivElement>;
  @ViewChild('accH1', { static: true }) accH1!: ElementRef<HTMLSpanElement>;
  @ViewChild('accH2', { static: true }) accH2!: ElementRef<HTMLSpanElement>;
  @ViewChild('accH3', { static: true }) accH3!: ElementRef<HTMLSpanElement>;

  private chart: any;

  ngOnChanges(changes: SimpleChanges): void {
    this.render();
  }

  private render() {
    const historyData = Array.isArray(this.history) ? this.history : [];
    const forecastData = Array.isArray(this.forecast) ? this.forecast : [];
    if (historyData.length === 0 && forecastData.length === 0) { this.destroy(); return; }

    const labels = [...new Set([
      ...historyData.map(x => x.date_time),
      ...forecastData.map(x => x.date_time)
    ])].sort();

    const pickSeries = (arr: any[], key: string) => labels.map(l => {
      const it = arr.find(d => d.date_time === l); return it ? (it as any)[key] ?? null : null;
    });

    const hTemp = pickSeries(historyData, 'temperature');
    const hHum = pickSeries(historyData, 'humidity');
    const hPres = pickSeries(historyData, 'pressure');
    const hWind = pickSeries(historyData, 'wind_speed');
    const hRain = pickSeries(historyData, 'rainfall');
    const hRainTotal = pickSeries(historyData, 'rain_total');

    const byTime = new Map<string, any>();
    forecastData.forEach(fp => {
      const t = fp.date_time; if (!byTime.has(t)) byTime.set(t, {});
      const lead = typeof fp.lead_hours === 'number' ? fp.lead_hours : null;
      if (lead !== null && lead >= 0 && lead <= 3) {
        const bucket = byTime.get(t);
        if (bucket[lead] == null) bucket[lead] = fp; // assume DESC issued_at
      }
    });

    const getAtLead = (l: string, field: string, lead: number) => {
      const b = byTime.get(l); if (!b || !b[lead]) return null; const v = b[lead][field as keyof ForecastPoint];
      return v == null ? null : (v as number);
    };

    const rainH1 = labels.map(l => getAtLead(l, 'rainfall', 1));
    const rainH2 = labels.map(l => getAtLead(l, 'rainfall', 2));
    const rainH3 = labels.map(l => getAtLead(l, 'rainfall', 3));

    const pickNearest = (l: string, field: keyof ForecastPoint) => {
      const b = byTime.get(l); if (!b) return null;
      for (const k of [0,1,2,3]) { if (b[k] && b[k][field] != null) return b[k][field] as number; }
      return null;
    };
    const fTempN = labels.map(l => pickNearest(l, 'temperature'));
    const fHumN = labels.map(l => pickNearest(l, 'humidity'));
    const fPresN = labels.map(l => pickNearest(l, 'pressure'));
    const fWindN = labels.map(l => pickNearest(l, 'wind_speed'));

    this.destroy();
    // @ts-ignore
    const Chart = (window as any).Chart;
    if (!Chart || !this.canvas?.nativeElement) return;

    const datasets: any[] = [
      { label: '温度 (°C) - 实测', seriesKey: 'temp_actual', data: hTemp, borderColor: 'rgb(255,99,132)', backgroundColor: 'rgba(255,99,132,0.1)', yAxisID: 'y-temperature', tension: 0.4, spanGaps: false },
      { label: '湿度 (%) - 实测', seriesKey: 'hum_actual', data: hHum, borderColor: 'rgb(54,162,235)', backgroundColor: 'rgba(54,162,235,0.1)', yAxisID: 'y-humidity', tension: 0.4, hidden: true, spanGaps: false },
      { label: '大气压 (hPa) - 实测', seriesKey: 'pressure_actual', data: hPres, borderColor: 'rgb(153,102,255)', backgroundColor: 'rgba(153,102,255,0.1)', yAxisID: 'y-pressure', tension: 0.4, hidden: true, spanGaps: false },
      { label: '风速 (m/s) - 实测', seriesKey: 'wind_actual', data: hWind, borderColor: 'rgb(75,192,192)', backgroundColor: 'rgba(75,192,192,0.1)', yAxisID: 'y-wind', tension: 0.4, hidden: true, spanGaps: false },
      { label: '雨量 (mm) - 实测', seriesKey: 'rain_actual', data: hRain, type: 'bar', backgroundColor: 'rgba(54,162,235,0.6)', borderColor: 'rgb(54,162,235)', yAxisID: 'y-rainfall' },
      { label: '累计雨量 (mm) - 实测', seriesKey: 'rain_total', data: hRainTotal, borderColor: 'rgb(75,192,192)', backgroundColor: 'rgba(75,192,192,0.1)', yAxisID: 'y-rainfall', tension: 0.2, spanGaps: false, pointRadius: 0, hidden: true }
    ];
    if (forecastData.length > 0) {
      datasets.push(
        { label: '雨量 (mm) - 预报 (+1h)', seriesKey: 'rain_fcst_h1', data: rainH1, type: 'bar', backgroundColor: 'rgba(255,99,71,0.55)', borderColor: 'rgb(255,99,71)', yAxisID: 'y-rainfall' },
        { label: '雨量 (mm) - 预报 (+2h)', seriesKey: 'rain_fcst_h2', data: rainH2, type: 'bar', backgroundColor: 'rgba(255,205,86,0.55)', borderColor: 'rgb(255,205,86)', yAxisID: 'y-rainfall' },
        { label: '雨量 (mm) - 预报 (+3h)', seriesKey: 'rain_fcst_h3', data: rainH3, type: 'bar', backgroundColor: 'rgba(76,175,80,0.55)', borderColor: 'rgb(76,175,80)', yAxisID: 'y-rainfall' },
        { label: '温度 (°C) - 预报', seriesKey: 'temp_fcst', data: fTempN, borderColor: 'rgb(255,159,64)', backgroundColor: 'rgba(255,159,64,0.1)', borderDash: [5,5], yAxisID: 'y-temperature', tension: 0.4, spanGaps: false, hidden: true },
        { label: '湿度 (%) - 预报', seriesKey: 'hum_fcst', data: fHumN, borderColor: 'rgb(54,162,235)', backgroundColor: 'rgba(54,162,235,0.1)', borderDash: [5,5], yAxisID: 'y-humidity', tension: 0.4, hidden: true, spanGaps: false },
        { label: '大气压 (hPa) - 预报', seriesKey: 'pressure_fcst', data: fPresN, borderColor: 'rgb(153,102,255)', backgroundColor: 'rgba(153,102,255,0.1)', borderDash: [5,5], yAxisID: 'y-pressure', tension: 0.4, hidden: true, spanGaps: false },
        { label: '风速 (m/s) - 预报', seriesKey: 'wind_fcst', data: fWindN, borderColor: 'rgb(75,192,192)', backgroundColor: 'rgba(75,192,192,0.1)', borderDash: [5,5], yAxisID: 'y-wind', tension: 0.4, hidden: true, spanGaps: false }
      );
    }

    const totalLabels = labels.length;
    const tickStep = Math.max(1, Math.ceil(totalLabels / 10));
    this.chart = new Chart(this.canvas.nativeElement.getContext('2d'), {
      type: 'line',
      data: { labels, datasets },
      options: {
        responsive: true, maintainAspectRatio: false,
        interaction: { mode: 'index', intersect: false },
        layout: { padding: { top: 12, right: 16, bottom: 12, left: 16 } },
        plugins: { legend: { display: true, position: 'top', align: 'center', labels: { padding: 16 } } },
        scales: {
          x: { type: 'category', ticks: {
            autoSkip: false, maxRotation: 0, minRotation: 0,
            callback: function(value: any, index: number) {
              if (index % tickStep !== 0) return '';
              const labels = (this as any).chart?.data?.labels || [];
              const raw = labels[index] || '';
              return (typeof raw === 'string' && raw.length >= 13) ? raw.substring(5, 13) : raw;
            }
          }, grid: { display: true, drawOnChartArea: true } },
          'y-temperature': { type: 'linear', display: true, position: 'right', title: { display: true, text: '温度 (°C)' } },
          'y-humidity': { type: 'linear', display: true, position: 'right', title: { display: true, text: '湿度 (%)' }, grid: { drawOnChartArea: false }, min: 0, max: 100 },
          'y-pressure': { type: 'linear', display: true, position: 'left', title: { display: true, text: '大气压 (hPa)' }, grid: { drawOnChartArea: false } },
          'y-wind': { type: 'linear', display: true, position: 'left', title: { display: true, text: '风速 (m/s)' }, grid: { drawOnChartArea: false }, beginAtZero: true },
          'y-rainfall': { type: 'linear', display: true, position: 'right', title: { display: true, text: '雨量 (mm)' }, grid: { drawOnChartArea: false }, beginAtZero: true }
        }
      }
    });

    this.applyLegendMode(this.legendMode);
    this.updateAccuracyPanel(labels, hRain, rainH1, rainH2, rainH3);
  }

  private applyLegendMode(mode: string) {
    if (!this.chart) return;
    const map = new Map<string, any>();
    this.chart.data.datasets.forEach((ds: any) => { if (ds.seriesKey) map.set(ds.seriesKey, ds); });
    const setVisible = (keys: string[]) => {
      const all = ['temp_actual','hum_actual','rain_actual','rain_total','temp_fcst','hum_fcst','pressure_actual','pressure_fcst','wind_actual','wind_fcst','rain_fcst_h1','rain_fcst_h2','rain_fcst_h3'];
      all.forEach(k => { const ds = map.get(k); if (ds) ds.hidden = true; });
      keys.forEach(k => { const ds = map.get(k); if (ds) ds.hidden = false; });
    };
    switch (mode) {
      case 'verify_all': setVisible(['temp_actual','temp_fcst','hum_actual','hum_fcst','pressure_actual','pressure_fcst','wind_actual','wind_fcst','rain_actual','rain_fcst_h1','rain_fcst_h2','rain_fcst_h3']); break;
      case 'temp_compare': setVisible(['temp_actual','temp_fcst']); break;
      case 'hum_compare': setVisible(['hum_actual','hum_fcst']); break;
      case 'rain_all': setVisible(['rain_actual','rain_fcst_h1','rain_fcst_h2','rain_fcst_h3']); break;
      case 'pressure_compare': setVisible(['pressure_actual','pressure_fcst']); break;
      case 'wind_compare': setVisible(['wind_actual','wind_fcst']); break;
      case 'combo_standard':
      default: setVisible(['temp_actual','temp_fcst','rain_actual','rain_fcst_h1','rain_fcst_h2','rain_fcst_h3']);
    }
    this.chart.update();
  }

  private updateAccuracyPanel(labels: string[], historyRain: (number|null)[], h1: (number|null)[], h2: (number|null)[], h3: (number|null)[]) {
    const panel = this.accPanel?.nativeElement; if (!panel) return;
    const isNum = (v:any)=> v!=null && !isNaN(Number(v));
    const usedIdx = historyRain.map((v, idx) => ({v, idx})).filter(x => isNum(x.v)).map(x => x.idx);
    const total = usedIdx.length;
    if (total === 0 || labels.length === 0) { panel.style.display = 'none'; return; }

    // Buckets: 0mm (exact zero), (0,5], (5,10], (10,∞)
    const bucketOf = (mm: any): number|null => {
      if (!isNum(mm)) return null;
      const v = Math.max(0, Number(mm));
      if (v === 0) return 0;           // 0mm
      if (v > 0 && v <= 5) return 1;   // (0,5]
      if (v > 5 && v <= 10) return 2;  // (5,10]
      return 3;                        // >10
    };
    const nameOf = (b:number|null) => b===0 ? '0mm' : b===1 ? '(0,5]' : b===2 ? '(5,10]' : b===3 ? '>10' : '--';

    const calcFor = (arr: (number|null)[]) => {
      let correct=0;
      usedIdx.forEach(i=>{ const ba=bucketOf(historyRain[i]); const bf=bucketOf(arr[i]); if(ba!==null && bf!==null && ba===bf) correct++; });
      return {correct,total};
    };
    const fmtPct = (n: number) => `${n.toFixed(1)}%`;

    // Detailed console logs for validation
    try {
      console.groupCollapsed('[准确率] (+1h/+2h/+3h)');
      console.log('总小时(实测):', total);
      usedIdx.forEach(i => {
        const label = labels[i];
        const a = historyRain[i];
        const bA = bucketOf(a);
        const f1 = h1[i]; const b1 = bucketOf(f1);
        const f2 = h2[i]; const b2 = bucketOf(f2);
        const f3 = h3[i]; const b3 = bucketOf(f3);
        const m1 = (bA!==null && b1!==null && bA===b1) ? 'true' : 'false';
        const m2 = (bA!==null && b2!==null && bA===b2) ? 'true' : 'false';
        const m3 = (bA!==null && b3!==null && bA===b3) ? 'true' : 'false';
        const fv = (v:any)=> (v==null||isNaN(Number(v)))? 'NULL' : Number(v).toFixed(2);
        console.log(`${label} | 实测 ${fv(a)} (${nameOf(bA)}) | +1h ${fv(f1)} (${nameOf(b1)}) ${m1} | +2h ${fv(f2)} (${nameOf(b2)}) ${m2} | +3h ${fv(f3)} (${nameOf(b3)}) ${m3}`);
      });
    } catch {}

    const r1 = calcFor(h1), r2 = calcFor(h2), r3 = calcFor(h3);
    try {
      console.log(`命中/总 (+1h): ${r1.correct}/${r1.total} => ${fmtPct((r1.correct/total)*100)}`);
      console.log(`命中/总 (+2h): ${r2.correct}/${r2.total} => ${fmtPct((r2.correct/total)*100)}`);
      console.log(`命中/总 (+3h): ${r3.correct}/${r3.total} => ${fmtPct((r3.correct/total)*100)}`);
      console.groupEnd?.();
    } catch {}

    this.accH1.nativeElement.textContent = total>0 ? fmtPct((r1.correct/total)*100) : '--';
    this.accH2.nativeElement.textContent = total>0 ? fmtPct((r2.correct/total)*100) : '--';
    this.accH3.nativeElement.textContent = total>0 ? fmtPct((r3.correct/total)*100) : '--';
    panel.style.display = 'block';
  }

  destroy() { if (this.chart) { this.chart.destroy(); this.chart = null; } }
}
