import { describe, it, expect } from 'vitest';
import type { CalendarEvent } from '../api';
import { fmtRange, fmtTimeBadge, layoutWeekBars, monthGridDays, splitWeeks } from './calendar';

function ev(p: Partial<CalendarEvent> & { id: number; date: string }): CalendarEvent {
  return {
    spaceId: 'S', ownerId: null, endDate: null, time: null, endTime: null,
    title: `e${p.id}`, description: null, createdBy: 'user', sourceTaskId: null,
    createdAt: '', ...p,
  } as CalendarEvent;
}

describe('fmtTimeBadge', () => {
  it('all-day when no start time', () => {
    expect(fmtTimeBadge({ time: null, endTime: null })).toBe('終日');
    // endTime without start time still reads as all-day (start drives display)
    expect(fmtTimeBadge({ time: null, endTime: '10:00' })).toBe('終日');
  });
  it('start only', () => {
    expect(fmtTimeBadge({ time: '09:00', endTime: null })).toBe('09:00');
  });
  it('start–end range', () => {
    expect(fmtTimeBadge({ time: '09:00', endTime: '10:30' })).toBe('09:00–10:30');
  });
});

describe('fmtRange', () => {
  it('single day', () => {
    expect(fmtRange({ date: '2026-06-20', endDate: null })).toBe('6/20');
    expect(fmtRange({ date: '2026-06-20', endDate: '2026-06-20' })).toBe('6/20');
  });
  it('multi day', () => {
    expect(fmtRange({ date: '2026-06-20', endDate: '2026-06-23' })).toBe('6/20–6/23');
  });
});

describe('monthGridDays / splitWeeks', () => {
  it('returns 42 days split into 6 weeks of 7, starting on a Sunday', () => {
    const days = monthGridDays('2026-06');
    expect(days).toHaveLength(42);
    const weeks = splitWeeks(days);
    expect(weeks).toHaveLength(6);
    expect(weeks.every(w => w.length === 7)).toBe(true);
    // 2026-06-01 is a Monday → grid starts on Sunday 2026-05-31
    expect(days[0]).toBe('2026-05-31');
  });
});

describe('layoutWeekBars', () => {
  const week = ['2026-06-21', '2026-06-22', '2026-06-23', '2026-06-24', '2026-06-25', '2026-06-26', '2026-06-27'];

  it('places a multi-day span at the right columns', () => {
    const bars = layoutWeekBars(week, [ev({ id: 1, date: '2026-06-22', endDate: '2026-06-24' })]);
    expect(bars).toHaveLength(1);
    expect(bars[0]).toMatchObject({ colStart: 2, colSpan: 3, lane: 0, continuesLeft: false, continuesRight: false });
  });

  it('clips a span entering from the previous week (continuesLeft)', () => {
    const bars = layoutWeekBars(week, [ev({ id: 1, date: '2026-06-19', endDate: '2026-06-22' })]);
    expect(bars[0]).toMatchObject({ colStart: 1, colSpan: 2, continuesLeft: true, continuesRight: false });
  });

  it('clips a span leaving into the next week (continuesRight)', () => {
    const bars = layoutWeekBars(week, [ev({ id: 1, date: '2026-06-26', endDate: '2026-06-30' })]);
    expect(bars[0]).toMatchObject({ colStart: 6, colSpan: 2, continuesLeft: false, continuesRight: true });
  });

  it('stacks overlapping events on separate lanes', () => {
    const bars = layoutWeekBars(week, [
      ev({ id: 1, date: '2026-06-22', endDate: '2026-06-25' }),
      ev({ id: 2, date: '2026-06-23', endDate: '2026-06-24' }),
    ]);
    const lanes = bars.map(b => b.lane).sort();
    expect(lanes).toEqual([0, 1]);
  });

  it('ignores events outside the week', () => {
    expect(layoutWeekBars(week, [ev({ id: 1, date: '2026-07-01' })])).toHaveLength(0);
  });
});
