import { loadLatestObservationInboxReports, queryObservationInbox, } from './inbox.js';
import { loadObservationReviewState } from './review-state.js';
import { resolveObservationReviewSession, } from './resolved-review.js';
import { buildObservationSkillChains } from './skill-chain.js';
import { loadSkillDerivedStandards, resolveSkillStandards, } from './soft-standards/index.js';
import { durationMsBetween } from '../shared/time.js';
export function buildObservationInboxViewModel(observationsDir, options = {}) {
    const activeSkill = options.skill?.trim() || undefined;
    const allItems = filterItemsBySkill(queryObservationInbox(observationsDir), activeSkill);
    const items = allItems.slice(0, 100);
    const reports = loadLatestObservationInboxReports(observationsDir).map((report) => filterReportBySkill(report, activeSkill));
    const experienceReports = reports.flatMap((report) => report.experience ? [report.experience] : []);
    const skillInvocationCounts = reports.reduce((acc, report) => {
        for (const [skill, count] of Object.entries(report.meta.skillInvocationCounts ?? {})) {
            acc[skill] = (acc[skill] ?? 0) + count;
        }
        return acc;
    }, {});
    const skillSessionCounts = reports.reduce((acc, report) => {
        for (const [skill, count] of Object.entries(report.meta.skillSessionCounts ?? {})) {
            acc[skill] = (acc[skill] ?? 0) + count;
        }
        return acc;
    }, {});
    const skillInvocationLastSeen = reports.reduce((acc, report) => {
        for (const [skill, timestamp] of Object.entries(report.meta.skillInvocationLastSeen ?? {})) {
            if (!acc[skill] || timestamp > acc[skill])
                acc[skill] = timestamp;
        }
        return acc;
    }, {});
    const skillToolCallCounts = reports.reduce((acc, report) => {
        for (const [skill, counts] of Object.entries(report.meta.skillToolCallCounts ?? {})) {
            const target = acc[skill] ?? {};
            for (const [tool, count] of Object.entries(counts)) {
                target[tool] = (target[tool] ?? 0) + count;
            }
            acc[skill] = target;
        }
        return acc;
    }, {});
    const totalSkillInvocations = Object.values(skillInvocationCounts).reduce((sum, count) => sum + count, 0);
    const countSkillsBySeverity = (...severities) => new Set(allItems.filter((item) => severities.includes(item.severity)).map((item) => item.skillName)).size;
    const severitySkillCounts = {
        high: countSkillsBySeverity('high'),
        medium: countSkillsBySeverity('medium'),
        low: countSkillsBySeverity('low'),
        noise: countSkillsBySeverity('noise'),
    };
    const skillCount = new Set([...Object.keys(skillInvocationCounts), ...allItems.map((item) => item.skillName)]).size;
    const skillNames = Array.from(new Set([
        ...Object.keys(skillInvocationCounts),
        ...allItems.map((item) => item.skillName),
        ...experienceReports.flatMap((report) => report.skills.map((skill) => skill.skillName)),
    ]));
    const skillChains = buildObservationSkillChains(skillNames, process.cwd(), experienceReports);
    const skillDerivedStandards = loadSkillDerivedStandards(observationsDir);
    const skillResolvedStandards = Object.fromEntries(skillNames.map((skillName) => [skillName, resolveSkillStandards(skillName, {
            observationsDir,
            skillChain: skillChains[skillName],
            derivedStandards: skillDerivedStandards,
        })]));
    const latestSeen = allItems.reduce((latest, item) => item.lastSeen > latest ? item.lastSeen : latest, '');
    const reportCount = reports.length;
    const latestSeenLabel = latestSeen ? latestSeen.slice(0, 19).replace('T', ' ') : '—';
    const reviewState = loadObservationReviewState(observationsDir);
    const resolvedReviewSessions = {};
    for (const experience of experienceReports) {
        for (const session of experience.sessions) {
            const enhancedReview = skillDerivedStandards[session.skillName]?.enhancedReview;
            resolvedReviewSessions[session.id] = resolveObservationReviewSession({
                session,
                enhancedReview,
                reviewState,
            });
        }
    }
    return {
        activeSkill,
        allItems,
        items,
        reports,
        experienceReports,
        skillInvocationCounts,
        skillSessionCounts,
        skillInvocationLastSeen,
        skillToolCallCounts,
        skillChains,
        skillDerivedStandards,
        skillResolvedStandards,
        totalSkillInvocations,
        severitySkillCounts,
        skillCount,
        reportCount,
        latestSeenLabel,
        reviewState,
        resolvedReviewSessions,
    };
}
function filterItemsBySkill(items, skillName) {
    return skillName ? items.filter((item) => item.skillName === skillName) : items;
}
function pickRecordValue(value, key) {
    if (!value || value[key] == null)
        return undefined;
    return { [key]: value[key] };
}
function filterReportBySkill(report, skillName) {
    if (!skillName)
        return report;
    const items = report.items.filter((item) => item.skillName === skillName);
    const relatedSessionIds = new Set(items.flatMap((item) => [item.sessionId, ...item.recentSessionIds]));
    const experience = report.experience ? {
        ...report.experience,
        meta: {
            ...report.experience.meta,
            sessionCount: report.experience.sessions.filter((session) => session.skillName === skillName).length,
            skillCount: report.experience.skills.some((skill) => skill.skillName === skillName) ? 1 : 0,
            invocationCount: report.experience.invocations.filter((invocation) => invocation.skillName === skillName).length,
            goalSliceCount: report.experience.goalSlices.filter((goal) => goal.skillName === skillName).length,
        },
        goalSlices: report.experience.goalSlices.filter((goal) => goal.skillName === skillName),
        invocations: report.experience.invocations.filter((invocation) => invocation.skillName === skillName),
        sessions: report.experience.sessions.filter((session) => session.skillName === skillName),
        skills: report.experience.skills.filter((skill) => skill.skillName === skillName),
    } : undefined;
    for (const session of experience?.sessions ?? []) {
        relatedSessionIds.add(session.sessionId);
    }
    const sessionTimeRanges = (report.meta.sessionTimeRanges ?? []).filter((range) => relatedSessionIds.has(range.sessionId) || (range.sessionGroupId ? relatedSessionIds.has(range.sessionGroupId) : false));
    return {
        ...report,
        meta: {
            ...report.meta,
            itemCount: items.length,
            sessionCount: sessionTimeRanges.length,
            sessionTimeRanges,
            sessionTimeRange: buildReportSessionTimeRange(sessionTimeRanges),
            skillInvocationCounts: pickRecordValue(report.meta.skillInvocationCounts, skillName),
            skillSessionCounts: pickRecordValue(report.meta.skillSessionCounts, skillName),
            skillInvocationLastSeen: pickRecordValue(report.meta.skillInvocationLastSeen, skillName),
            skillToolCallCounts: pickRecordValue(report.meta.skillToolCallCounts, skillName),
        },
        items,
        experience,
    };
}
function buildReportSessionTimeRange(ranges = []) {
    const starts = ranges.map((range) => range.startTimestamp).filter((value) => Boolean(value));
    const ends = ranges.map((range) => range.endTimestamp).filter((value) => Boolean(value));
    if (starts.length === 0 || ends.length === 0)
        return { from: '', to: '' };
    const from = starts.reduce((min, value) => value < min ? value : min, starts[0]);
    const to = ends.reduce((max, value) => value > max ? value : max, ends[0]);
    return { from, to, durationMs: durationMsBetween(from, to) };
}
// ============================================================================
// observability → renderer 投影 facade。
//
// 把 observation-inbox-renderer.ts 对 observability 内部多个 module 的细粒度
// import 收敛到 view-model 一处:renderer 只 import 这里 + 已经 facade 化的
// feedback-projection.js。observability 内部如何重组(rename、拆模块)对
// renderer 透明。
//
// 设计原则:
//   - 仅 re-export,不做语义合并 / 不引入新类型 / 不写运行时逻辑
//   - 不改任何字节级输出(html-renderer snapshot 必须字节一致)
//   - 适合 pre-project 的 function(每个 item / annotation 算一次)后续可上移
//     到 build view-model 时算好挂字段;text-signal 这类「每次 input 不同」
//     的保留 facade 形态
//
// 范围:仅 observation-inbox-renderer.ts 用到的子集。CLI / server 等其它消费者
// 继续直接 import observability 内部 —— 它们不是「视图」,不在 facade 收敛范围。
// ============================================================================
export { severityReasonFor } from './inbox.js';
export { observationMetricAnnotationTargetId } from './review-state.js';
export { resolveObservationReviewSession } from './resolved-review.js';
export { getSkillChainAdvisory, resolveAdvisoryCommand } from './skill-chain-advisories.js';
export { ASSISTANT_DELIVERABLE_ARTIFACT_RE, hasAssistantDeliverableArtifactText, hasAssistantDeliverySignalText, hasUserHardRuleText, HARD_RULE_TEXT_RE, isAssistantProgressUpdateText, isScheduledTaskPromptText, isSyntheticUserMessageText, isUserInteractionMetricText, } from './text-signals.js';
//# sourceMappingURL=inbox-view-model.js.map