/**
 * 데일리 브리핑 워처 — 평일(월~금) KST 09:30 에 "오늘의 할 일"을 텔레그램으로 발송.
 *
 * 소스:
 *   1. Google Calendar — iCal 캐시 새로고침 후 오늘 일정 (readCalendarEventsCache)
 *   2. Google Tasks   — 오늘 마감 + 기한 지난 미완료 + 날짜 없는 [조건부] task (listTasks)
 *
 * 발송 조건 (모두 충족 시에만):
 *   - g1nation.dailyBriefing.enabled (기본 true)
 *   - 텔레그램 봇 토큰 등록 + allowedChatIds 설정 — 미설정이면 조용히 skip (로그만)
 *
 * 스케줄링은 stocksWatcher 와 동일한 단일 setTimeout 체인 패턴 (KST 고정).
 */
import * as vscode from 'vscode';
import { logError, logInfo } from '../../utils';
import { TelegramHttpClient } from '../../integrations/telegram/telegramClient';
import { TELEGRAM_TOKEN_SECRET_KEY } from '../../extension/telegramCommands';
import { refreshCalendarCache, readCalendarEventsCache, readCalendarConfig } from '../calendar/calendarCache';
import { listTasks } from '../calendar/tasksApi';

let _timer: NodeJS.Timeout | undefined;
let _disposed = false;
let _lastFiredYmd = '';   // 같은 날 중복 발송 방지 (타이머 드리프트 대비)

function nowInKst(): { hour: number; minute: number; ymd: string; weekday: number } {
    const now = new Date();
    const parts = new Intl.DateTimeFormat('en-US', {
        timeZone: 'Asia/Seoul',
        year: 'numeric', month: '2-digit', day: '2-digit',
        hour: '2-digit', minute: '2-digit', hour12: false,
    }).formatToParts(now);
    const get = (t: string) => parts.find(p => p.type === t)?.value || '00';
    const ymd = `${get('year')}-${get('month')}-${get('day')}`;
    // 'YYYY-MM-DD' → UTC midnight Date — getUTCDay 가 그 날짜의 요일 (0=일).
    const weekday = new Date(`${ymd}T00:00:00Z`).getUTCDay();
    return { hour: Number(get('hour')), minute: Number(get('minute')), ymd, weekday };
}

function briefingTime(): { hour: number; minute: number } {
    const raw = (vscode.workspace.getConfiguration('g1nation').get<string>('dailyBriefing.time', '09:30') || '09:30').trim();
    const m = raw.match(/^(\d{1,2}):(\d{2})$/);
    if (!m) return { hour: 9, minute: 30 };
    return { hour: Math.min(23, Number(m[1])), minute: Math.min(59, Number(m[2])) };
}

/** 다음 발송까지 ms — 평일 스킵 포함. */
function msUntilNextFire(): number {
    const { hour, minute, ymd, weekday } = nowInKst();
    const target = briefingTime();
    const nowMin = hour * 60 + minute;
    const targetMin = target.hour * 60 + target.minute;

    // 오늘이 평일이고 아직 시간 전이면 오늘.
    if (weekday >= 1 && weekday <= 5 && targetMin > nowMin && _lastFiredYmd !== ymd) {
        return (targetMin - nowMin) * 60_000;
    }
    // 다음 평일 찾기 (내일부터 최대 7일).
    let daysAhead = 1;
    let wd = weekday;
    for (; daysAhead <= 7; daysAhead++) {
        wd = (weekday + daysAhead) % 7;
        if (wd >= 1 && wd <= 5) break;
    }
    return ((24 * 60 - nowMin) + (daysAhead - 1) * 24 * 60 + targetMin) * 60_000;
}

function pickChatId(): number | null {
    const allowed = vscode.workspace.getConfiguration('g1nation').get<number[]>('telegram.allowedChatIds', []) || [];
    return allowed.length > 0 ? allowed[0] : null;
}

/** 오늘의 브리핑 텍스트 구성. 일정·할일이 모두 없으면 null (발송 skip). */
export async function buildBriefingText(context: vscode.ExtensionContext, ymd: string): Promise<string | null> {
    const lines: string[] = [`📋 *오늘의 할 일* — ${ymd}`];

    // (1) 캘린더 일정 — 캐시 새로고침은 best-effort (실패해도 기존 캐시 사용).
    try { await refreshCalendarCache(context); } catch { /* 기존 캐시로 진행 */ }
    const events = readCalendarEventsCache(context)
        .filter(e => (e.startIso || '').slice(0, 10) === ymd)
        .sort((a, b) => a.startIso.localeCompare(b.startIso));
    if (events.length) {
        lines.push('', `🗓 *일정* (${events.length})`);
        for (const e of events.slice(0, 15)) {
            const time = e.allDay ? '종일' : e.startIso.slice(11, 16);
            lines.push(`· ${time} — ${e.summary}${e.location ? ` @${e.location}` : ''}`);
        }
    }

    // (2) Google Tasks — 미완료 중: 오늘 마감 / 기한 경과 / 날짜 없는 조건부.
    const tr = await listTasks(context, { showCompleted: false, maxResults: 100 });
    if (tr.ok) {
        const open = tr.tasks.filter(t => t.status === 'needsAction');
        const dueToday = open.filter(t => t.due === ymd);
        const overdue = open.filter(t => t.due && t.due < ymd);
        const conditional = open.filter(t => !t.due && /^\[조건부\]/.test(t.title));
        if (dueToday.length) {
            lines.push('', `✅ *오늘 마감 할 일* (${dueToday.length})`);
            for (const t of dueToday.slice(0, 15)) lines.push(`· ${t.title}`);
        }
        if (overdue.length) {
            lines.push('', `⏰ *기한 지난 미완료* (${overdue.length})`);
            for (const t of overdue.slice(0, 10)) lines.push(`· ${t.due} — ${t.title}`);
        }
        if (conditional.length) {
            lines.push('', `🔗 *조건부 대기* (${conditional.length})`);
            for (const t of conditional.slice(0, 10)) lines.push(`· ${t.title}`);
        }
        // (3) /meet 등록 보류 — 텔레그램에서 바로 회신해 등록 완결 가능 (P5 HITL).
        let pendingCount = 0;
        try {
            const { loadPending } = await import('../datacollect/scheduling/meetRegistration');
            const pend = loadPending();
            if (pend && pend.items.length) {
                pendingCount = pend.items.length;
                lines.push('', `⏸ *회의 액션 보류* (${pend.items.length}) — 이 채팅에 \`confirm 1=ok 2=6/20 3=skip\` 으로 회신하면 등록됩니다 (목록: \`pending\`)`);
                for (const it of pend.items.slice(0, 8)) {
                    const why = it.kind === 'undecided' ? '진행미정' : it.kind === 'nodate' ? '기한미정' : `조건부:${it.condition}`;
                    lines.push(`· ${it.idx}. ${it.work} (${why})`);
                }
            }
        } catch { /* 보류 조회 실패는 브리핑 본 흐름에 영향 없음 */ }

        if (!dueToday.length && !overdue.length && !events.length && !conditional.length && !pendingCount) {
            return null; // 알릴 것이 전혀 없음 — 발송 skip
        }
    } else if (!events.length) {
        // Tasks 실패 + 일정도 없으면 보낼 내용 없음.
        logInfo('Daily briefing: Tasks 조회 실패 + 일정 없음 — skip.', { error: tr.error });
        return null;
    } else {
        lines.push('', `⚠️ Tasks 조회 실패: ${tr.error}`);
    }

    lines.push('', '— Astra 데일리 브리핑');
    return lines.join('\n');
}

async function fireOnce(context: vscode.ExtensionContext): Promise<void> {
    const { ymd } = nowInKst();
    if (_lastFiredYmd === ymd) return;
    _lastFiredYmd = ymd;

    const chatId = pickChatId();
    if (chatId === null) { logInfo('Daily briefing skip: telegram.allowedChatIds 미설정.'); return; }
    const token = (await context.secrets.get(TELEGRAM_TOKEN_SECRET_KEY)) || '';
    if (!token.trim()) { logInfo('Daily briefing skip: 텔레그램 봇 토큰 없음.'); return; }
    const calCfg = readCalendarConfig(context);
    if (!calCfg.icalUrl && !calCfg.refreshToken) { logInfo('Daily briefing skip: 캘린더 미연결.'); return; }

    try {
        const text = await buildBriefingText(context, ymd);
        if (!text) { logInfo('Daily briefing: 오늘 일정·할일 없음 — 발송 skip.'); return; }
        const client = new TelegramHttpClient({ getToken: () => token });
        await client.sendMessage({ chatId, text, parseMode: 'Markdown' });
        logInfo('Daily briefing 발송 완료.', { chatId, chars: text.length });
    } catch (e: any) {
        logError('Daily briefing 발송 실패.', { error: e?.message ?? String(e) });
    }
}

function scheduleNext(context: vscode.ExtensionContext): void {
    if (_disposed) return;
    const ms = msUntilNextFire();
    logInfo('Daily briefing 다음 발송 예약.', { inMinutes: Math.round(ms / 60_000) });
    _timer = setTimeout(async () => {
        const enabled = vscode.workspace.getConfiguration('g1nation').get<boolean>('dailyBriefing.enabled', true);
        if (enabled) {
            try { await fireOnce(context); } catch (e: any) {
                logError('Daily briefing fire 실패.', { error: e?.message ?? String(e) });
            }
        }
        scheduleNext(context);
    }, ms);
}

/** VS Code 시작 시 호출 — disposable 반환 (subscriptions 에 push). */
export function startDailyBriefingWatcher(context: vscode.ExtensionContext): vscode.Disposable {
    _disposed = false;
    scheduleNext(context);
    return new vscode.Disposable(() => {
        _disposed = true;
        if (_timer) { clearTimeout(_timer); _timer = undefined; }
    });
}
