import { describe, it, expect } from 'vitest';
import { parseDueDate, parseAllCandidates } from '@main/services/dueDateParser.js';

// 2026-04-26 is a Sunday (KST). Use that as "today" for fixtures.
const TODAY = new Date('2026-04-26T00:00:00.000Z'); // KST midnight = UTC 15:00 prior day, but for parser logic we treat input Date as KST-aligned. The parser treats the passed Date as the "today reference" without further timezone math.

describe('parseDueDate (Korean rule parser)', () => {
  it('returns null for empty / no-token text', () => {
    expect(parseDueDate('아무 일정도 없는 메모', TODAY).iso).toBeNull();
  });

  it('parses YYYY-MM-DD literal', () => {
    expect(parseDueDate('2026-05-15 마감', TODAY).iso).toBe('2026-05-15');
  });

  it('rejects invalid YYYY-MM-DD (silent coercion)', () => {
    // 2026-02-30 is invalid; parser should NOT match it as the literal rule
    const r = parseDueDate('2026-02-30 마감', TODAY);
    // Falls through to other rules — but no other token matches "2026-02-30 마감"
    expect(r.iso).toBeNull();
  });

  it('parses N월 N일 in current year if future', () => {
    expect(parseDueDate('5월 1일 휴가 신청', TODAY).iso).toBe('2026-05-01');
  });

  it('parses N월 N일 in next year if past', () => {
    expect(parseDueDate('3월 1일 발표', TODAY).iso).toBe('2027-03-01');
  });

  it('parses MM/DD', () => {
    expect(parseDueDate('5/3 데모', TODAY).iso).toBe('2026-05-03');
  });

  it('parses N일 뒤', () => {
    expect(parseDueDate('3일 뒤 데모', TODAY).iso).toBe('2026-04-29');
  });

  it('parses N일 후', () => {
    expect(parseDueDate('5일 후 미팅', TODAY).iso).toBe('2026-05-01');
  });

  it('parses N주 뒤', () => {
    expect(parseDueDate('2주 뒤 회의', TODAY).iso).toBe('2026-05-10');
  });

  it('parses N개월 뒤', () => {
    expect(parseDueDate('1개월 뒤 점검', TODAY).iso).toBe('2026-05-26');
  });

  it('parses 모레', () => {
    expect(parseDueDate('모레 발표', TODAY).iso).toBe('2026-04-28');
  });

  it('parses 내일', () => {
    expect(parseDueDate('내일 회의 준비', TODAY).iso).toBe('2026-04-27');
  });

  it('parses 글피', () => {
    expect(parseDueDate('글피까지 마무리', TODAY).iso).toBe('2026-04-29');
  });

  it('parses 오늘', () => {
    expect(parseDueDate('오늘 PR 리뷰', TODAY).iso).toBe('2026-04-26');
  });

  it('parses 다음 주 월요일', () => {
    expect(parseDueDate('다음 주 월요일까지 슬라이드', TODAY).iso).toBe('2026-05-04');
  });

  it('parses 다음 주 금요일', () => {
    expect(parseDueDate('다음 주 금요일에 점검', TODAY).iso).toBe('2026-05-08');
  });

  it('parses 이번 주 일요일 = today (Sunday)', () => {
    expect(parseDueDate('이번 주 일요일 약속', TODAY).iso).toBe('2026-04-26');
  });

  it('parses 이번 주 수요일', () => {
    // This week's Mon = 04-20, so this week's Wed = 04-22. But that's past — still resolve to 04-22 (parser does not skip past dates within current week — caller can decide to ignore).
    expect(parseDueDate('이번 주 수요일 회의', TODAY).iso).toBe('2026-04-22');
  });

  it('parses 다음 달 1일', () => {
    expect(parseDueDate('다음 달 1일', TODAY).iso).toBe('2026-05-01');
  });

  it('parses 다음 달 (alone) → first of next month', () => {
    expect(parseDueDate('다음 달 마감', TODAY).iso).toBe('2026-05-01');
  });

  it('returns medium confidence with iso null for 월말', () => {
    const r = parseDueDate('월말 마감', TODAY);
    expect(r.iso).toBeNull();
    expect(r.confidence).toBe('medium');
    expect(r.matchedToken).toBe('월말');
  });

  it('returns medium confidence with iso null for 주말', () => {
    const r = parseDueDate('주말까지 정리', TODAY);
    expect(r.iso).toBeNull();
    expect(r.confidence).toBe('medium');
  });

  it('returns medium confidence for 퇴근 전', () => {
    const r = parseDueDate('퇴근 전 답장', TODAY);
    expect(r.iso).toBeNull();
    expect(r.confidence).toBe('medium');
  });

  it('returns null for time-only (오후 3시)', () => {
    // Time tokens get medium confidence (defer to AI) since they imply same-day but not a date directly.
    const r = parseDueDate('오후 3시 미팅', TODAY);
    expect(r.iso).toBeNull();
    expect(r.confidence).toBe('medium');
  });

  it('first match wins when multiple tokens present', () => {
    expect(parseDueDate('내일도 모레도 바쁨', TODAY).iso).toBe('2026-04-27');
  });

  it('matches 다음 주 alone → next Monday', () => {
    // If not followed by 요일, "다음 주" alone defaults to next Monday
    expect(parseDueDate('다음 주 발표', TODAY).iso).toBe('2026-05-04');
  });
});

describe('parseAllCandidates', () => {
  it('returns empty array when no token', () => {
    expect(parseAllCandidates('아무 일정 없음', TODAY)).toEqual([]);
  });

  it('returns 2 high-confidence candidates for "내일 모레"', () => {
    const r = parseAllCandidates('내일 모레', TODAY);
    expect(r.length).toBe(2);
    const isos = r.map((c) => c.iso);
    expect(isos).toContain('2026-04-27');
    expect(isos).toContain('2026-04-28');
    expect(r.every((c) => c.confidence === 'high')).toBe(true);
  });

  it('returns candidates in text-position order', () => {
    const r = parseAllCandidates('내일 모레', TODAY);
    expect(r[0]!.matchedToken).toBe('내일');
    expect(r[1]!.matchedToken).toBe('모레');
  });

  it('returns 1 candidate for single token "내일"', () => {
    const r = parseAllCandidates('내일 회의', TODAY);
    expect(r.length).toBe(1);
    expect(r[0]!.iso).toBe('2026-04-27');
  });

  it('returns 1 medium-confidence candidate for "월말 마감"', () => {
    const r = parseAllCandidates('월말 마감', TODAY);
    expect(r.length).toBe(1);
    expect(r[0]!.iso).toBeNull();
    expect(r[0]!.confidence).toBe('medium');
    expect(r[0]!.matchedToken).toBe('월말');
  });

  it('returns mix of high + medium candidates', () => {
    const r = parseAllCandidates('내일 월말 회의', TODAY);
    expect(r.length).toBe(2);
    expect(r[0]!.confidence).toBe('high');
    expect(r[1]!.confidence).toBe('medium');
  });

  it('returns 2 candidates for "5월 1일 이후 다음 주 월요일까지"', () => {
    const r = parseAllCandidates('5월 1일 이후 다음 주 월요일까지', TODAY);
    expect(r.length).toBe(2);
    expect(r[0]!.iso).toBe('2026-05-01');
    expect(r[1]!.iso).toBe('2026-05-04');
  });
});
