import { useMemo } from 'react';
import { useQuery } from '@tanstack/react-query';
import { useNavigate } from 'react-router-dom';

import { fetchDailyCounts } from '../api/client';

const CELL_SIZE = 12;
const GAP = 3;
const RADIUS = CELL_SIZE / 2;
const ROWS = 7; // days per week
const LEFT_LABEL_WIDTH = 28;
const TOP_LABEL_HEIGHT = 16;

const DAY_LABELS = ['', 'mon', '', 'wed', '', 'fri', ''];
const MONTH_LABELS = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'];

const COLORS = [
  'rgba(255,255,255,0.05)',  // 0: empty
  '#0e4429',                  // 1: low
  '#006d32',                  // 2: medium-low
  '#26a641',                  // 3: medium
  '#39d353',                  // 4: high
];

export function ContributionGraph() {
  const to = new Date();
  const from = new Date(to);
  from.setFullYear(from.getFullYear() - 1);

  const fromStr = fmt(from);
  const toStr = fmt(to);

  const dailyQ = useQuery({
    queryKey: ['daily-counts', fromStr, toStr],
    queryFn: () => fetchDailyCounts(fromStr, toStr),
    staleTime: 5 * 60_000,
  });

  const navigate = useNavigate();

  const { weeks, monthMarkers, thresholds, totalCount } = useMemo(() => {
    const counts = dailyQ.data ?? [];
    const countMap = new Map(counts.map((d) => [d.date, d.count]));

    // Start from the Sunday before `from`
    const start = new Date(from);
    start.setDate(start.getDate() - start.getDay());

    const weeks: { date: string; count: number; col: number; row: number }[][] = [];
    const monthMarkers: { col: number; label: string }[] = [];
    let col = 0;
    let prevMonth = -1;
    const cursor = new Date(start);

    while (cursor <= to) {
      const week: typeof weeks[0] = [];
      for (let row = 0; row < ROWS; row++) {
        const dateStr = fmt(cursor);
        const count = countMap.get(dateStr) ?? 0;
        week.push({ date: dateStr, count, col, row });

        // Track month transitions (on the first day of each week)
        if (row === 0) {
          const m = cursor.getMonth();
          if (m !== prevMonth) {
            monthMarkers.push({ col, label: MONTH_LABELS[m] });
            prevMonth = m;
          }
        }
        cursor.setDate(cursor.getDate() + 1);
      }
      weeks.push(week);
      col++;
    }

    // Compute quantile thresholds from non-zero counts
    const nonZero = counts.map((d) => d.count).filter((c) => c > 0).sort((a, b) => a - b);
    const thresholds = computeThresholds(nonZero);
    const totalCount = counts.reduce((sum, d) => sum + d.count, 0);

    return { weeks, monthMarkers, thresholds, totalCount };
  }, [dailyQ.data]);

  const cols = weeks.length;
  const svgWidth = LEFT_LABEL_WIDTH + cols * (CELL_SIZE + GAP);
  const svgHeight = TOP_LABEL_HEIGHT + ROWS * (CELL_SIZE + GAP);

  if (dailyQ.isLoading) return <p style={{ fontSize: '0.8rem' }}>loading contribution graph...</p>;
  if (dailyQ.isError) return null;

  return (
    <div className="contribution-graph mb-4">
      <p style={{ fontSize: '0.8rem', opacity: 0.6 }}>
        {totalCount} contributions in the last year
      </p>
      <div style={{ overflowX: 'auto' }}>
        <svg width={svgWidth} height={svgHeight} className="d-block">
          {/* Day-of-week labels */}
          {DAY_LABELS.map((label, i) =>
            label ? (
              <text
                key={i}
                x={LEFT_LABEL_WIDTH - 6}
                y={TOP_LABEL_HEIGHT + i * (CELL_SIZE + GAP) + CELL_SIZE / 2}
                textAnchor="end"
                dominantBaseline="central"
                className="graph-label"
              >
                {label}
              </text>
            ) : null,
          )}
          {/* Month labels */}
          {monthMarkers.map(({ col, label }, i) => (
            <text
              key={i}
              x={LEFT_LABEL_WIDTH + col * (CELL_SIZE + GAP) + RADIUS}
              y={10}
              textAnchor="middle"
              className="graph-label"
            >
              {label}
            </text>
          ))}
          {/* Circles */}
          {weeks.flatMap((week) =>
            week.map(({ date, count, col, row }) => (
              <circle
                key={date}
                cx={LEFT_LABEL_WIDTH + col * (CELL_SIZE + GAP) + RADIUS}
                cy={TOP_LABEL_HEIGHT + row * (CELL_SIZE + GAP) + RADIUS}
                r={RADIUS - 1}
                fill={colorFor(count, thresholds)}
                className="graph-cell"
                onClick={() => navigate(`/activity/${date}`)}
              >
                <title>{`${date}: ${count} ${count === 1 ? 'contribution' : 'contributions'}`}</title>
              </circle>
            )),
          )}
        </svg>
      </div>
    </div>
  );
}

function fmt(d: Date): string {
  return d.toISOString().slice(0, 10);
}

function colorFor(count: number, thresholds: number[]): string {
  if (count === 0) return COLORS[0];
  if (count <= thresholds[0]) return COLORS[1];
  if (count <= thresholds[1]) return COLORS[2];
  if (count <= thresholds[2]) return COLORS[3];
  return COLORS[4];
}

function computeThresholds(sorted: number[]): number[] {
  if (sorted.length === 0) return [1, 2, 3];
  const p = (pct: number) => sorted[Math.min(Math.floor(pct * sorted.length), sorted.length - 1)];
  return [p(0.25), p(0.5), p(0.75)];
}
