/**
 * Runway / Cash 누적 저장소.
 *
 * 4인 기업 운영의 가장 중요한 숫자 — 현금 잔고 / 월 소진율 / 남은 개월수 — 를
 * 한 명령 (`/runway`) 로 본다. 회계 시스템은 아니고, 대표가 머리에 가지고 있는
 * "지금 통장에 얼마, 한 달에 얼마 나감" 을 코드 옆에서 잡는 가벼운 트래커.
 *
 * 저장 형식: JSON Lines (.jsonl) — 한 줄 = 한 entry. 외부 회계 SaaS 연동 안 함.
 * 위치: `<workspace>/.astra/runway.jsonl`. 사람이 직접 편집 가능, grep / 백업 친화.
 *
 * 민감 정보(현금 잔고) 포함되므로 외부로 안 보냄 — 로컬 only.
 */

import { createEventStore } from '../_shared/eventSourcedStore';

const STORE_REL_PATH = '.astra/runway.jsonl';

export type RunwayEntryType = 'snapshot' | 'expense' | 'revenue' | 'burn';

export interface RunwayEntry {
    /** unique id — timestamp 기반. */
    id: string;
    /** ISO timestamp. */
    timestamp: string;
    /** 항목 종류 — snapshot(잔고) / expense(지출) / revenue(수입) / burn(월 소진율 수동 설정). */
    type: RunwayEntryType;
    /** 금액 — KRW 기본 단위, 소수점 허용. */
    amount: number;
    /** 통화 — 기본 'KRW'. 추후 'USD' 등 확장 가능. */
    currency?: string;
    /** 카테고리 — expense 의 경우 'salary' / 'rent' / 'saas' / 'misc' 등. */
    category?: string;
    /** 자유 메모. */
    memo?: string;
}

const _store = createEventStore<RunwayEntry>({
    relPath: STORE_REL_PATH,
    validate: (e): e is RunwayEntry => !!e
        && typeof (e as any).id === 'string'
        && typeof (e as any).amount === 'number'
        && typeof (e as any).type === 'string',
});

export const getRunwayFilePath = _store.getFilePath;
export const readRunway = _store.read;
export const appendRunway = _store.append;

/**
 * 현재 상태 계산 — 최신 snapshot, 최근 30일 net burn, 명시적 burn 설정 중 우선순위.
 *
 * - latestCash: 가장 최근 'snapshot' entry 의 amount (없으면 null).
 * - explicitBurn: 가장 최근 'burn' entry — 사용자가 수동 설정한 월 소진율.
 * - computedBurn: 최근 30일 expense - revenue, 30일 미만이면 일 평균 × 30 으로 보정.
 * - effectiveBurn: explicitBurn 우선, 없으면 computedBurn.
 * - runwayMonths: latestCash / effectiveBurn — burn 이 0 이하면 Infinity.
 */
export interface RunwayStatus {
    latestCash: number | null;
    latestCashAt: string | null;
    explicitBurn: number | null;
    computedBurn: number | null;
    effectiveBurn: number | null;
    runwayMonths: number | null;
    last30Expense: number;
    last30Revenue: number;
    last30Days: number;
    totalEntries: number;
}

export function computeRunwayStatus(now: Date = new Date()): RunwayStatus {
    const entries = readRunway();
    const nowMs = now.getTime();
    const thirtyDaysMs = 30 * 24 * 60 * 60 * 1000;

    let latestCash: number | null = null;
    let latestCashAt: string | null = null;
    let explicitBurn: number | null = null;
    let last30Expense = 0;
    let last30Revenue = 0;
    let oldestRecentMs = nowMs;
    let hasRecent = false;

    for (const e of entries) {
        const t = Date.parse(e.timestamp);
        if (e.type === 'snapshot') {
            if (!latestCashAt || (Date.parse(e.timestamp) >= Date.parse(latestCashAt))) {
                latestCash = e.amount;
                latestCashAt = e.timestamp;
            }
        } else if (e.type === 'burn') {
            if (!explicitBurn || t >= (entries.find(x => x.type === 'burn' && x.amount === explicitBurn)?.timestamp ? Date.parse(e.timestamp) : 0)) {
                explicitBurn = e.amount;
            }
        } else if (e.type === 'expense' && nowMs - t <= thirtyDaysMs) {
            last30Expense += e.amount;
            if (t < oldestRecentMs) oldestRecentMs = t;
            hasRecent = true;
        } else if (e.type === 'revenue' && nowMs - t <= thirtyDaysMs) {
            last30Revenue += e.amount;
            if (t < oldestRecentMs) oldestRecentMs = t;
            hasRecent = true;
        }
    }

    // 최신 burn 정확히 다시 — 위 로직이 꼬여서 단순화.
    explicitBurn = null;
    let burnAt = 0;
    for (const e of entries) {
        if (e.type !== 'burn') continue;
        const t = Date.parse(e.timestamp);
        if (t >= burnAt) { explicitBurn = e.amount; burnAt = t; }
    }

    const netBurn30 = last30Expense - last30Revenue;
    let computedBurn: number | null = null;
    let last30Days = 0;
    if (hasRecent) {
        const span = Math.max(1, Math.ceil((nowMs - oldestRecentMs) / (24 * 60 * 60 * 1000)));
        last30Days = Math.min(30, span);
        // 30일 미만이면 일 평균 × 30 으로 환산.
        if (last30Days < 30) computedBurn = (netBurn30 / last30Days) * 30;
        else computedBurn = netBurn30;
    }

    const effectiveBurn = explicitBurn ?? computedBurn;
    let runwayMonths: number | null = null;
    if (latestCash !== null && effectiveBurn !== null && effectiveBurn > 0) {
        runwayMonths = latestCash / effectiveBurn;
    } else if (latestCash !== null && effectiveBurn !== null && effectiveBurn <= 0) {
        runwayMonths = Infinity;
    }

    return {
        latestCash,
        latestCashAt,
        explicitBurn,
        computedBurn,
        effectiveBurn,
        runwayMonths,
        last30Expense,
        last30Revenue,
        last30Days,
        totalEntries: entries.length,
    };
}
