/**
 * Presenter — 옛 AgentWorkState + bubble queue + activity items 를 OfficeSnapshot 으로
 * 변환하는 *pure* 함수. mini / full view 둘 다 같은 OfficeSnapshot 을 받게 만드는 게 목표.
 *
 * 이번 세션의 범위: 인터페이스 + 스텁. 실제 wiring 은 다음 세션에서:
 *   - sidebarProvider 의 `_pixelOfficeBroadcast` 가 옛 message 대신 OfficeSnapshot 송신
 *   - mini view (media/sidebar.js) 와 full view (features/astraOffice/view/runtime.ts)
 *     둘 다 `officeSnapshot` message 를 받아 자기 식대로 렌더
 *
 * 이번 세션에 옛 message 와 OfficeSnapshot 을 *동시에* 보낼 수도 있게 — 호환 모드. 다음
 * 세션에서 옛 message 제거.
 */

import type { AgentWorkState, AgentBubble } from '../company/pixelOfficeState';
import type { CompanyState } from '../company/types';
import {
    type OfficeSnapshot,
    type OfficeAgentSnapshot,
    type OfficePhase,
    type OfficeActivityItem,
    type OfficeBubbleSeed,
    makeEmptyOfficeSnapshot,
} from './schema';

/** mini/full view 가 받는 message envelope. */
export interface OfficeSnapshotMessage {
    type: 'officeSnapshot';
    value: OfficeSnapshot;
}

/** 옛 AgentWorkState.status → OfficePhase 매핑. */
const STATUS_TO_PHASE: Record<string, OfficePhase> = {
    idle: 'idle',
    intake: 'intake',
    analyzing: 'planning',
    need_clarification: 'awaiting-approval',
    contract_ready: 'planning',
    planning: 'planning',
    executing: 'executing',
    reviewing: 'reviewing',
    waiting_approval: 'awaiting-approval',
    error: 'error',
    done: 'done',
};

/** agentId 가 alias 면 정식 id 로 정규화. dispatcher / agents.ts 와 같은 규칙. */
const AGENT_ALIASES: Record<string, string> = {
    writer: 'writer',
    editor: 'designer',
    secretary: 'support',
    business: 'inspector',
};

export function normalizeAgentId(rawAgentId: string | undefined): string | null {
    if (!rawAgentId) return null;
    const lower = rawAgentId.toLowerCase();
    return AGENT_ALIASES[lower] ?? lower;
}

/**
 * 옛 입력들을 합쳐 새 OfficeSnapshot 을 만든다. 입력 중 일부가 undefined 라도 안전.
 *
 * 이번 세션 stub: 옛 AgentWorkState 의 *단일 슬롯* 정보로 roster 1개 짜리 snapshot 만
 * 생성. 다음 세션에서 CompanyState 의 active roster 전체로 확장.
 */
export function presentOfficeSnapshot(input: {
    activeState?: AgentWorkState;
    recentBubbles?: AgentBubble[];
    recentActivity?: OfficeActivityItem[];
    company?: CompanyState;
    /**
     * 이 turn 의 active agent roster — `listActiveAgentsByCategory` 결과를 평탄화해서 전달.
     * 빈 배열/undefined 면 옛 동작 (active agent 1명만) 으로 fallback.
     */
    roster?: Array<{ agentId: string; agentName: string; roleCategory: OfficeAgentSnapshot['roleCategory'] }>;
}): OfficeSnapshot {
    const snap = makeEmptyOfficeSnapshot();
    const { activeState, recentBubbles, recentActivity, roster: rosterInput } = input;

    if (activeState) {
        const phase = STATUS_TO_PHASE[activeState.status] ?? 'idle';
        snap.phase = phase;
        snap.activeAgentId = normalizeAgentId(activeState.agentId);
        const activeId = snap.activeAgentId;
        // Roster:
        //   - rosterInput 이 주어지면 (#G) 회사 전체 active agent 사용. active agent 만
        //     activeState 의 상태로 표시하고 나머지는 idle.
        //   - 없으면 (옛 caller) 활성 agent 1명만 fallback.
        if (rosterInput && rosterInput.length > 0) {
            const now = Date.now();
            snap.roster = rosterInput.map((r) => {
                const isActive = activeId !== null && r.agentId === activeId;
                return {
                    agentId: r.agentId,
                    agentName: r.agentName,
                    roleCategory: r.roleCategory,
                    status: isActive ? activeState.status : 'idle',
                    currentStep: isActive ? activeState.currentStep : undefined,
                    lastLog: isActive ? (activeState.recentLogs ?? []).slice(-1)[0] : undefined,
                    lastActivityAt: isActive ? (activeState.updatedAt ?? now) : 0,
                };
            });
        } else {
            const role = _inferRoleCategory(activeState.agentId);
            const agent: OfficeAgentSnapshot = {
                agentId: snap.activeAgentId ?? activeState.agentId,
                agentName: activeState.agentName ?? activeState.agentId,
                roleCategory: role,
                status: activeState.status,
                currentStep: activeState.currentStep,
                lastLog: (activeState.recentLogs ?? []).slice(-1)[0],
                lastActivityAt: activeState.updatedAt ?? Date.now(),
            };
            snap.roster = [agent];
        }
        if (activeState.currentTask) {
            snap.task = {
                goal: activeState.currentTask,
                criteria: activeState.requirementContract?.criteria,
                openQuestions: activeState.requirementContract?.openQuestions,
                format: activeState.requirementContract?.format,
                context: activeState.requirementContract?.context,
            };
        }
        if (activeState.pipelineStages) {
            snap.pipeline = {
                stages: activeState.pipelineStages.map((s) => ({
                    label: s.label,
                    agentId: s.agent,
                    status: s.status,
                })),
                index: activeState.pipelineStages.findIndex((s) => s.status === 'active'),
            };
        }
        if (activeState.needUserInput?.length || activeState.awaitingApproval) {
            snap.awaiting = {
                kind: activeState.awaitingApproval ? 'approval' : 'clarification',
                questions: activeState.awaitingApproval
                    ? [activeState.awaitingApproval]
                    : (activeState.needUserInput ?? []),
            };
        }
    }

    if (recentActivity?.length) snap.activity = recentActivity.slice(-32);

    if (recentBubbles?.length) {
        snap.newBubbles = recentBubbles
            .map((b) => _toBubbleSeed(b))
            .filter((b): b is OfficeBubbleSeed => b !== null);
    }

    snap.updatedAt = Date.now();
    return snap;
}

// ── helpers ──

function _inferRoleCategory(rawAgentId: string | undefined): OfficeAgentSnapshot['roleCategory'] {
    if (!rawAgentId) return 'support';
    const id = rawAgentId.toLowerCase();
    if (id.includes('ceo')) return 'ceo';
    if (id.includes('plan')) return 'planner';
    if (id.includes('research')) return 'researcher';
    if (id.includes('design')) return 'designer';
    if (id.includes('writer')) return 'writer';
    if (id.includes('editor')) return 'designer';
    if (id.includes('developer') || id.includes('dev')) return 'developer';
    if (id.includes('qa')) return 'qa';
    if (id.includes('inspect') || id.includes('business')) return 'inspector';
    return 'support';
}

function _toBubbleSeed(b: AgentBubble): OfficeBubbleSeed | null {
    if (!b || !b.text) return null;
    return {
        agentId: normalizeAgentId(b.agentId) ?? b.agentId,
        text: b.text,
        type: (b.type as OfficeBubbleSeed['type']) ?? 'status',
    };
}
