import { useMemo } from "react";
import { FitnessChartPoint, FitnessChartSeries, FitnessChartsResponse } from "../api/client";
import "./FitnessCharts.css";

const CHART_KEYS = ["weight_kg", "calories", "protein_g", "water_l", "steps", "body_fat_pct"] as const;

interface MetricChartProps {
  series: FitnessChartSeries;
  showTrend: boolean;
  granularity: "week" | "day";
}

function formatTick(point: FitnessChartPoint, granularity: "week" | "day") {
  if (granularity === "day") {
    const d = new Date(`${point.date}T12:00:00`);
    return d.toLocaleDateString("ru-RU", { day: "numeric", month: "short" });
  }
  const d = new Date(`${point.week_start}T12:00:00`);
  return d.toLocaleDateString("ru-RU", { day: "numeric", month: "short" });
}

function MetricChart({ series, showTrend, granularity }: MetricChartProps) {
  const layout = useMemo(() => {
    const width = 640;
    const height = 168;
    const pad = { top: 12, right: 12, bottom: 28, left: 44 };
    const plotW = width - pad.left - pad.right;
    const plotH = height - pad.top - pad.bottom;

    const active = series.points.filter((p) => p.has_data && p.value != null);
    if (active.length === 0) {
      return null;
    }

    const xMax = Math.max(1, series.points.length - 1);
    const values = active.map((p) => p.value as number);
    let yMin = Math.min(...values);
    let yMax = Math.max(...values);
    if (yMin === yMax) {
      yMin -= 1;
      yMax += 1;
    } else {
      const padY = (yMax - yMin) * 0.12;
      yMin -= padY;
      yMax += padY;
    }

    const xScale = (index: number) => pad.left + (index / xMax) * plotW;
    const yScale = (value: number) => pad.top + plotH - ((value - yMin) / (yMax - yMin)) * plotH;

    const dots = active.map((p) => ({
      x: xScale(p.index),
      y: yScale(p.value as number),
      label: formatTick(p, granularity),
      value: p.value as number,
    }));

    let trendPath = "";
    if (showTrend && series.trend?.line) {
      const trendPoints = series.trend.line
        .map((p) => `${xScale(p.index)},${yScale(p.value)}`)
        .join(" ");
      trendPath = trendPoints;
    }

    const yTicks = [yMin, (yMin + yMax) / 2, yMax];
    const labelIndexes = active.length <= 4
      ? active.map((p) => p.index)
      : [active[0].index, active[active.length - 1].index];

    return {
      width,
      height,
      pad,
      plotW,
      plotH,
      yMin,
      yMax,
      yScale,
      xScale,
      dots,
      trendPath,
      yTicks,
      labelIndexes,
    };
  }, [granularity, series, showTrend]);

  if (!layout) {
    return null;
  }

  const slopeLabel =
    granularity === "day" && series.trend && "slope_per_day" in series.trend
      ? `${(series.trend as { slope_per_day: number }).slope_per_day > 0 ? "+" : ""}${(series.trend as { slope_per_day: number }).slope_per_day}/день`
      : series.trend && "slope_per_week" in series.trend
        ? `${(series.trend as { slope_per_week: number }).slope_per_week > 0 ? "+" : ""}${(series.trend as { slope_per_week: number }).slope_per_week}/нед`
        : null;

  return (
    <article className="fitness-chart-card">
      <div className="fitness-chart-card-header">
        <h4>
          {series.label} <span className="fitness-chart-unit">({series.unit})</span>
        </h4>
        <span className="fitness-chart-meta">
          {series.data_points} {granularity === "day" ? "дн." : "нед."}
          {showTrend && slopeLabel ? ` · тренд ${slopeLabel}` : ""}
        </span>
      </div>
      <svg
        className="fitness-chart-svg"
        viewBox={`0 0 ${layout.width} ${layout.height}`}
        role="img"
        aria-label={`График ${series.label}`}
      >
        {layout.yTicks.map((tick) => (
          <g key={tick}>
            <line
              x1={layout.pad.left}
              x2={layout.pad.left + layout.plotW}
              y1={layout.yScale(tick)}
              y2={layout.yScale(tick)}
              className="fitness-chart-grid"
            />
            <text x={layout.pad.left - 6} y={layout.yScale(tick) + 4} className="fitness-chart-axis">
              {tick.toFixed(tick >= 100 ? 0 : 1)}
            </text>
          </g>
        ))}

        {showTrend && layout.trendPath ? (
          <polyline points={layout.trendPath} className="fitness-chart-trend" />
        ) : null}

        {layout.dots.map((dot) => (
          <g key={`${dot.label}-${dot.value}`}>
            <circle cx={dot.x} cy={dot.y} r={4.5} className="fitness-chart-dot" />
            <title>
              {dot.label}: {dot.value.toFixed(1)} {series.unit}
            </title>
          </g>
        ))}

        {layout.labelIndexes.map((idx) => {
          const point = series.points[idx];
          if (!point?.has_data) return null;
          return (
            <text
              key={idx}
              x={layout.xScale(idx)}
              y={layout.height - 8}
              textAnchor="middle"
              className="fitness-chart-axis"
            >
              {formatTick(point, granularity)}
            </text>
          );
        })}
      </svg>
    </article>
  );
}

interface FitnessChartsProps {
  data: FitnessChartsResponse | null;
  showTrend: boolean;
  loading?: boolean;
}

export default function FitnessCharts({ data, showTrend, loading }: FitnessChartsProps) {
  if (loading) {
    return <p className="fitness-chart-status">Загрузка графиков…</p>;
  }
  if (!data) {
    return null;
  }

  const granularity = data.granularity;
  const source =
    granularity === "day" && data.daily_series ? data.daily_series : data.series;

  const charts = CHART_KEYS.map((key) => source[key]).filter(
    (series) => series && series.data_points > 0,
  );

  return (
    <section className="fitness-section fitness-charts-section">
      <div className="fitness-charts-head">
        <div>
          <h3>Динамика за год</h3>
          <p className="fitness-charts-subtitle">
            {granularity === "day"
              ? `Мало данных (${data.days_with_data} дн.) — показаны дневные точки`
              : `Недельные точки · заполнено ${data.weeks_with_data} из ${data.weeks} нед.`}
          </p>
        </div>
      </div>

      {charts.length === 0 ? (
        <p className="fitness-empty">
          Пока нет данных для графиков. Логируй еду, воду, шаги или вес — точки появятся автоматически.
        </p>
      ) : (
        <div className="fitness-charts-grid">
          {charts.map((series) => (
            <MetricChart
              key={series.key}
              series={series}
              showTrend={showTrend}
              granularity={granularity}
            />
          ))}
        </div>
      )}
    </section>
  );
}
