/**
 * Minimal ICS parser — no library deps. Connect_origin 의 Python 버전을
 * 그대로 옮겼고, 본 함수는 *pure* 라서 단위테스트가 쉽다.
 *
 * 처리 범위:
 *   - VEVENT 블록 추출
 *   - line continuation (다음 줄이 공백 시작) 펼치기
 *   - SUMMARY / DESCRIPTION / LOCATION / DTSTART / DTEND 필드
 *   - DTSTART;VALUE=DATE → all-day 이벤트 표시
 *   - YYYYMMDD / YYYYMMDDTHHMMSS / ...Z (UTC) 포맷 모두 처리
 *
 * 처리 안 함 (필요해지면 v2):
 *   - RRULE 반복 일정 (단일 instance 만 표시)
 *   - TZID 타임존 변환 (UTC 가 아니면 로컬로 가정)
 *   - VTIMEZONE 블록
 *   - 첨부 / 참석자 / 알림
 */

export interface IcsEvent {
    /** 시작 시각 (로컬 Date 기준). all-day 일정은 자정. */
    start: Date;
    /** 종료 시각. 없으면 undefined. */
    end?: Date;
    /** 제목 (없으면 '(제목 없음)'). */
    summary: string;
    location: string;
    description: string;
    /** DTSTART 가 VALUE=DATE 형식이었으면 true — 시각은 무시하고 날짜만 의미. */
    allDay: boolean;
}

/** 한 문자열의 ICS 본문을 받아 VEVENT 들을 배열로 반환. 잘못된 입력은 빈 배열. */
export function parseIcs(raw: string): IcsEvent[] {
    if (!raw || typeof raw !== 'string') return [];
    // Line continuation: ICS 는 75자 wrap 시 다음 줄이 공백/탭으로 시작 → 합쳐줘야 한다.
    const unfolded = raw.replace(/\r?\n[ \t]/g, '');
    const events: IcsEvent[] = [];
    let cur: Record<string, string> | null = null;
    let curDateOnly: Record<'start' | 'end', boolean> = { start: false, end: false };
    for (const rawLine of unfolded.split('\n')) {
        const line = rawLine.replace(/\r$/, '');
        if (line === 'BEGIN:VEVENT') {
            cur = {};
            curDateOnly = { start: false, end: false };
        } else if (line === 'END:VEVENT') {
            if (cur) {
                const ev = _toEvent(cur, curDateOnly);
                if (ev) events.push(ev);
            }
            cur = null;
        } else if (cur && line.includes(':')) {
            const colonIdx = line.indexOf(':');
            const keyPart = line.slice(0, colonIdx);
            const value = line.slice(colonIdx + 1);
            const base = keyPart.split(';', 1)[0];
            if (base === 'SUMMARY' || base === 'DESCRIPTION' || base === 'LOCATION'
                || base === 'DTSTART' || base === 'DTEND') {
                cur[base] = value;
                if ((base === 'DTSTART' || base === 'DTEND') && keyPart.includes(';VALUE=DATE')) {
                    curDateOnly[base === 'DTSTART' ? 'start' : 'end'] = true;
                }
            }
        }
    }
    return events;
}

/** 시작 시각 기준 오름차순 정렬 + 현재 시각 - 1시간 ~ 미래 cutoffDays 범위만 필터. */
export function selectUpcoming(events: IcsEvent[], daysAhead: number, now: Date = new Date()): IcsEvent[] {
    const past = new Date(now.getTime() - 60 * 60 * 1000); // 1 hour ago
    const cutoff = new Date(now.getTime() + daysAhead * 24 * 60 * 60 * 1000);
    return events
        .filter((e) => e.start >= past && e.start <= cutoff)
        .sort((a, b) => a.start.getTime() - b.start.getTime());
}

function _toEvent(raw: Record<string, string>, dateOnly: { start: boolean; end: boolean }): IcsEvent | null {
    const start = _parseDt(raw.DTSTART ?? '');
    if (!start) return null;
    const end = _parseDt(raw.DTEND ?? '');
    return {
        start,
        end: end ?? undefined,
        summary: _unescape(raw.SUMMARY ?? '(제목 없음)'),
        location: _unescape(raw.LOCATION ?? ''),
        description: _unescape(raw.DESCRIPTION ?? ''),
        allDay: dateOnly.start,
    };
}

function _parseDt(s: string): Date | null {
    if (!s) return null;
    const trimmed = s.trim();
    // Strip trailing Z (UTC marker — Date 파싱 시 자동 처리되지 않으므로 명시 변환).
    const utc = trimmed.endsWith('Z');
    const core = utc ? trimmed.slice(0, -1) : trimmed;
    // Two valid forms: YYYYMMDDTHHMMSS or YYYYMMDD
    const m = core.match(/^(\d{4})(\d{2})(\d{2})(?:T(\d{2})(\d{2})(\d{2}))?$/);
    if (!m) return null;
    const [, yy, mm, dd, HH, MM, SS] = m;
    const year = parseInt(yy, 10), month = parseInt(mm, 10) - 1, day = parseInt(dd, 10);
    const hour = HH ? parseInt(HH, 10) : 0;
    const min = MM ? parseInt(MM, 10) : 0;
    const sec = SS ? parseInt(SS, 10) : 0;
    if (utc) {
        return new Date(Date.UTC(year, month, day, hour, min, sec));
    }
    return new Date(year, month, day, hour, min, sec);
}

function _unescape(s: string): string {
    // ICS literal escapes: \, \; \n \\
    return s.replace(/\\,/g, ',').replace(/\\;/g, ';').replace(/\\n/g, ' ').replace(/\\\\/g, '\\');
}
