/*
 * SCR-HOME 월 캘린더 레이아웃 — WISE(UIWS) CalendarView 패턴 참조(외부 라이브러리 미사용).
 * 기간(2일+) 전시를 주 단위로 분할해 가로 spanning bar 로 배치(레인 그리디 할당),
 * MAX_LANES 초과분은 일자별 "+N" 카운트로 축약한다. 단일일 전시도 span=1 bar 로 통일.
 */

export type CalStatus = 'ongoing' | 'upcoming' | 'ended';

export interface CalEvent {
  id: string;
  name: string;
  start: string; // "YYYY-MM-DD"
  end: string; // "YYYY-MM-DD"
  hallLabel: string | null;
  status: CalStatus;
}

export interface WeekBar {
  event: CalEvent;
  lane: number;
  colStart: number; // 0..6
  span: number; // 1..7
  continuesLeft: boolean; // 이전 주에서 이어짐
  continuesRight: boolean; // 다음 주로 이어짐
}

export interface DayCell {
  date: Date;
  day: number; // 1..31
  inMonth: boolean;
  isToday: boolean;
  dow: number; // 0(일)..6(토)
  key: string; // "YYYY-MM-DD"
}

export interface WeekRow {
  cells: DayCell[]; // 7
  bars: WeekBar[]; // lane < MAX_LANES 만
  hiddenByCol: number[]; // 7 — 각 열에서 숨겨진(레인 초과) 이벤트 수
  laneCount: number; // 표시 레인 수(0..MAX_LANES) — 행 높이 산정용
}

export const MAX_LANES = 3;

/** "YYYY-MM-DD" → 로컬 Date(자정). 타임존 밀림 방지(UTC 파싱 회피). */
function parseYmd(s: string): Date {
  const [y, m, d] = s.split('-').map(Number);
  return new Date(y, (m || 1) - 1, d || 1);
}
function ymd(d: Date): string {
  const mm = String(d.getMonth() + 1).padStart(2, '0');
  const dd = String(d.getDate()).padStart(2, '0');
  return `${d.getFullYear()}-${mm}-${dd}`;
}
function addDays(d: Date, n: number): Date {
  return new Date(d.getFullYear(), d.getMonth(), d.getDate() + n);
}
/** 자정 기준 일수 차(b - a). */
function dayDiff(a: Date, b: Date): number {
  const ms = new Date(b.getFullYear(), b.getMonth(), b.getDate()).getTime() -
    new Date(a.getFullYear(), a.getMonth(), a.getDate()).getTime();
  return Math.round(ms / 86400000);
}

/**
 * 지정 월의 6주 × 7열 그리드 + 주별 spanning bar 를 계산한다.
 * @param today 오늘(강조) — 미지정 시 강조 없음.
 */
export function buildMonthGrid(
  year: number,
  month: number, // 0-based
  events: CalEvent[],
  today?: Date,
): WeekRow[] {
  const firstOfMonth = new Date(year, month, 1);
  const gridStart = addDays(firstOfMonth, -firstOfMonth.getDay()); // 그 주 일요일로
  const todayKey = today ? ymd(today) : null;

  const weeks: WeekRow[] = [];
  for (let w = 0; w < 6; w++) {
    const weekStart = addDays(gridStart, w * 7);
    const weekEnd = addDays(weekStart, 6);

    const cells: DayCell[] = [];
    for (let c = 0; c < 7; c++) {
      const date = addDays(weekStart, c);
      cells.push({
        date,
        day: date.getDate(),
        inMonth: date.getMonth() === month,
        isToday: todayKey != null && ymd(date) === todayKey,
        dow: c,
        key: ymd(date),
      });
    }

    // 이 주와 겹치는 이벤트 → 주 내부 세그먼트로 클립
    type Seg = { event: CalEvent; colStart: number; colEnd: number; s: Date; e: Date };
    const segs: Seg[] = [];
    for (const ev of events) {
      const s = parseYmd(ev.start);
      const e = parseYmd(ev.end);
      if (e < weekStart || s > weekEnd) continue;
      const colStart = Math.max(0, dayDiff(weekStart, s));
      const colEnd = Math.min(6, dayDiff(weekStart, e));
      if (colEnd < colStart) continue;
      segs.push({ event: ev, colStart, colEnd, s, e });
    }
    // 시작 열 오름차순, 긴 세그먼트 우선(레인 안정)
    segs.sort((a, b) => a.colStart - b.colStart || b.colEnd - b.colStart - (a.colEnd - a.colStart));

    const laneEnds: number[] = []; // 레인별 마지막 점유 열
    const bars: WeekBar[] = [];
    const hiddenByCol = [0, 0, 0, 0, 0, 0, 0];

    for (const seg of segs) {
      // 첫 빈 레인 탐색
      let lane = laneEnds.findIndex((end) => end < seg.colStart);
      if (lane === -1) {
        lane = laneEnds.length;
        laneEnds.push(seg.colEnd);
      } else {
        laneEnds[lane] = seg.colEnd;
      }
      if (lane < MAX_LANES) {
        bars.push({
          event: seg.event,
          lane,
          colStart: seg.colStart,
          span: seg.colEnd - seg.colStart + 1,
          continuesLeft: seg.s < weekStart,
          continuesRight: seg.e > weekEnd,
        });
      } else {
        for (let c = seg.colStart; c <= seg.colEnd; c++) hiddenByCol[c] += 1;
      }
    }

    weeks.push({
      cells,
      bars,
      hiddenByCol,
      laneCount: Math.min(laneEnds.length, MAX_LANES),
    });
  }
  return weeks;
}

/** 특정 일자에 걸치는 전 이벤트(팝업용) — 정렬: 시작일→이름. */
export function eventsOnDay(events: CalEvent[], dayKey: string): CalEvent[] {
  const d = parseYmd(dayKey);
  return events
    .filter((ev) => {
      const s = parseYmd(ev.start);
      const e = parseYmd(ev.end);
      return s <= d && d <= e;
    })
    .sort((a, b) => (a.start < b.start ? -1 : a.start > b.start ? 1 : a.name < b.name ? -1 : 1));
}
