import { useRef, useEffect, useState } from 'react';
import { eachDayOfInterval, eachWeekOfInterval, eachMonthOfInterval } from 'date-fns';

function isoWeek(d: Date): number {
  const utc = new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate()));
  utc.setUTCDate(utc.getUTCDate() + 4 - (utc.getUTCDay() || 7));
  const yearStart = new Date(Date.UTC(utc.getUTCFullYear(), 0, 1));
  return Math.ceil((((utc.getTime() - yearStart.getTime()) / 86400000) + 1) / 7);
}

interface TimelineAxisHeaderProps {
  startDate: Date;
  endDate: Date;
  /** When provided, skip ResizeObserver and render at this exact pixel width. */
  fixedWidth?: number;
  paddingX?: number;
  /** Force tick granularity; otherwise auto-selects based on pixelsPerDay. */
  granularity?: 'day' | 'week' | 'month';
}

export const HEADER_HEIGHT = 46;
const MONTH_ROW_H = 20;
const TICK_ROW_H = 26;

function dateToX(ts: number, start: Date, end: Date, width: number, paddingX: number): number {
  const totalMs = end.getTime() - start.getTime();
  if (totalMs <= 0) return paddingX;
  return paddingX + ((ts - start.getTime()) / totalMs) * (width - paddingX * 2);
}

export function TimelineAxisHeader({
  startDate, endDate, fixedWidth, paddingX = 10, granularity,
}: TimelineAxisHeaderProps) {
  const containerRef = useRef<HTMLDivElement>(null);
  const [autoWidth, setAutoWidth] = useState(600);

  useEffect(() => {
    if (fixedWidth != null) return;
    const el = containerRef.current;
    if (!el) return;
    const observer = new ResizeObserver((entries) => {
      const entry = entries[0];
      if (entry) setAutoWidth(entry.contentRect.width);
    });
    observer.observe(el);
    return () => observer.disconnect();
  }, [fixedWidth]);

  const width = fixedWidth ?? autoWidth;

  const spanMs = endDate.getTime() - startDate.getTime();
  const spanDays = spanMs / (24 * 60 * 60 * 1000);

  // Granularity: use explicit prop when provided; otherwise auto-detect from pixels per day
  const pixelsPerDay = (width - paddingX * 2) / Math.max(spanDays, 1);
  const showDays = granularity ? granularity === 'day' : pixelsPerDay >= 26;
  const showWeeks = granularity ? granularity === 'week' : (!showDays && pixelsPerDay >= 7);

  const months = eachMonthOfInterval({ start: startDate, end: endDate });

  const secondaryTicks: Date[] = showDays
    ? eachDayOfInterval({ start: startDate, end: endDate })
    : showWeeks
      ? eachWeekOfInterval({ start: startDate, end: endDate }, { weekStartsOn: 1 })
      : eachMonthOfInterval({ start: startDate, end: endDate });

  const DAY_ABBR = ['S', 'M', 'T', 'W', 'T', 'F', 'S'];

  const leftEdge = paddingX;
  const rightEdge = width - paddingX;

  return (
    <div ref={containerRef} className="w-full select-none" style={fixedWidth != null ? { width: fixedWidth } : undefined}>
      <svg width={width} height={HEADER_HEIGHT} className="overflow-visible">

        {/* Top row: year spans (month view) or month spans (day/week view) */}
        {granularity === 'month' ? (() => {
          const years: { year: number; startMs: number; endMs: number }[] = [];
          for (const m of months) {
            const y = m.getFullYear();
            const last = years[years.length - 1];
            if (!last || last.year !== y) {
              if (last) last.endMs = m.getTime();
              years.push({ year: y, startMs: m.getTime(), endMs: endDate.getTime() });
            }
          }
          return years.map((yr) => {
            const x1 = dateToX(Math.max(yr.startMs, startDate.getTime()), startDate, endDate, width, paddingX);
            const x2 = dateToX(Math.min(yr.endMs, endDate.getTime()), startDate, endDate, width, paddingX);
            const labelX = (x1 + x2) / 2;
            if (x2 - x1 < 24) return null;
            return (
              <text
                key={yr.year}
                x={labelX} y={MONTH_ROW_H - 4}
                textAnchor="middle"
                className="fill-foreground/70"
                fontSize={11}
                fontFamily="Geist, sans-serif"
                fontWeight={600}
                letterSpacing="-0.3"
              >
                {yr.year}
              </text>
            );
          });
        })() : months.map((month, i) => {
          const monthStart = Math.max(month.getTime(), startDate.getTime());
          const nextMonth = months[i + 1];
          const monthEnd = nextMonth
            ? Math.min(nextMonth.getTime(), endDate.getTime())
            : endDate.getTime();
          const x1 = dateToX(monthStart, startDate, endDate, width, paddingX);
          const x2 = dateToX(monthEnd, startDate, endDate, width, paddingX);
          const labelX = (x1 + x2) / 2;
          const monthPixelWidth = x2 - x1;

          let label: string;
          if (monthPixelWidth >= 60) {
            label = month.toLocaleString(undefined, { month: 'short', year: 'numeric' });
          } else if (monthPixelWidth >= 28) {
            label = month.toLocaleString(undefined, { month: 'short' });
          } else if (month.getMonth() === 0) {
            label = String(month.getFullYear());
          } else {
            return null;
          }

          return (
            <g key={month.toISOString()}>
              <text
                x={labelX} y={MONTH_ROW_H - 4}
                textAnchor="middle"
                className="fill-foreground/70"
                fontSize={11}
                fontFamily="Geist, sans-serif"
                fontWeight={600}
                letterSpacing="-0.3"
              >
                {label}
              </text>
            </g>
          );
        })}

        {/* Secondary ticks */}
        {secondaryTicks.map((tick, tickIdx) => {
          const x = dateToX(tick.getTime(), startDate, endDate, width, paddingX);
          if (x < leftEdge || x > rightEdge) return null;

          // On month granularity, skip ticks that would overlap (< 28px apart)
          if (!showDays && !showWeeks) {
            const nextTick = secondaryTicks[tickIdx + 1];
            if (nextTick) {
              const nextX = dateToX(nextTick.getTime(), startDate, endDate, width, paddingX);
              if (nextX - x < 28) return null;
            }
          }

          const label = showDays
            ? String(tick.getDate())
            : showWeeks
              ? `W${isoWeek(tick)}`
              : tick.toLocaleString(undefined, { month: 'short' });

          // Day-of-week abbreviation above the date number
          const dayAbbr = showDays ? DAY_ABBR[tick.getDay()] : null;

          // Center label inside the column the tick starts
          const nextTick = secondaryTicks[tickIdx + 1];
          let nextX: number;
          if (nextTick) {
            nextX = dateToX(nextTick.getTime(), startDate, endDate, width, paddingX);
          } else if (showDays) {
            nextX = x + pixelsPerDay;
          } else if (showWeeks) {
            nextX = x + pixelsPerDay * 7;
          } else {
            const monthEnd = new Date(tick.getFullYear(), tick.getMonth() + 1, 1).getTime();
            nextX = dateToX(Math.min(monthEnd, endDate.getTime()), startDate, endDate, width, paddingX);
          }
          const labelX = (x + nextX) / 2;

          return (
            <g key={tick.toISOString()}>
              {dayAbbr && (
                <text
                  x={labelX} y={MONTH_ROW_H + 11}
                  textAnchor="middle"
                  className="fill-muted-foreground"
                  fontSize={8}
                  fontFamily="Geist, sans-serif"
                  fontWeight={500}
                  opacity={0.6}
                >
                  {dayAbbr}
                </text>
              )}
              <text
                x={labelX} y={MONTH_ROW_H + TICK_ROW_H - 5}
                textAnchor="middle"
                className="fill-muted-foreground"
                fontSize={10}
                fontFamily="Geist, sans-serif"
                fontWeight={400}
              >
                {label}
              </text>
            </g>
          );
        })}

      </svg>
    </div>
  );
}
