/**
 * Type definitions for the 1인 기업 (One-Person Company) mode.
 *
 * The mode turns the user into a virtual CEO that dispatches work to a roster
 * of specialist agents. Each turn produces a session directory containing the
 * CEO's brief, every specialist's output, and the final synthesis report. The
 * dispatcher runs agents *sequentially* — only one LLM is loaded at any
 * moment — so the user can run multiple distinct agents on a single
 * model-constrained machine without RAM thrash.
 */

/**
 * 직군(role category). Drives the pipeline editor's "직군 → 담당 에이전트"
 * cascading dropdown and the CEO planner's "no 개발 before 기획" guard rail.
 *
 * Why a fixed enum instead of free-text categories: a small LLM (gemma e2b
 * etc.) can't reliably *cluster* free-text specialties at plan time, but it
 * can absolutely reason about "planner first, developer second" when the
 * planner prompt enumerates these slugs. The UI also needs a stable set so
 * the dropdown is short and the icons are pickable.
 *
 *   ceo        — orchestrator (only the built-in CEO)
 *   planner    — 기획 (요구 정리·기획서·방향성 정의)
 *   researcher — 시장·트렌드·사실 확인 리서치
 *   designer   — UI/UX·비주얼·사운드·콘텐츠 디자인
 *   developer  — 코드 작성·API·자동화·배포 스크립트
 *   qa         — 테스트·버그 발굴·회귀 확인
 *   inspector  — 기획 의도 대비 산출물 감리 / 최종 승인
 *   support    — 비즈니스·운영·비서 (직접 산출물 X, 보조)
 */
export type AgentRoleCategory =
    | 'ceo'
    | 'planner'
    | 'researcher'
    | 'designer'
    | 'developer'
    | 'qa'
    | 'inspector'
    | 'support';

export const ROLE_CATEGORY_LABELS: Record<AgentRoleCategory, string> = {
    ceo: 'CEO',
    planner: '기획',
    researcher: '리서치',
    designer: '디자인',
    developer: '개발',
    qa: 'QA',
    inspector: '감리',
    support: '지원',
};

export const ROLE_CATEGORY_ORDER: AgentRoleCategory[] = [
    'ceo', 'planner', 'researcher', 'designer', 'developer', 'qa', 'inspector', 'support',
];

/** Static description of a company agent. Loaded from `agents.ts`. */
export interface CompanyAgentDef {
    /** Stable identifier used in JSON plans, file names, config keys. */
    id: string;
    /** Display name (may be a Korean nickname like "레오" or "코다리"). */
    name: string;
    /** Role title shown in the manage panel and used in system prompts. */
    role: string;
    /** Single emoji used in chat headers and chip badges. */
    emoji: string;
    /** Brand colour for the agent card UI. CSS hex. */
    color: string;
    /** Comma-list of areas this agent owns. Drives the CEO's planner. */
    specialty: string;
    /** One-line punchy tagline shown under the agent name. */
    tagline: string;
    /** Optional voice / personality directive injected into the system prompt. */
    persona?: string;
    /**
     * 직군 — drives pipeline editor cascading dropdowns and CEO planner
     * sequencing rules. Built-ins ship with a fixed category; users may
     * override via state.roleCategoryOverrides (custom agents pick at
     * create time and can re-edit later). Falls back to 'support' when
     * a legacy state entry is missing this field.
     */
    roleCategory: AgentRoleCategory;
    /**
     * When true, this agent can't be toggled off in the UI. CEO uses this so
     * it's always available as the orchestrator.
     */
    alwaysOn?: boolean;
}

/**
 * User edits to an agent's *prompt* surface. Each field is optional: empty
 * or missing means "use the static default from `agents.ts`". Storing this
 * in state (rather than rewriting `agents.ts`) means the user can re-tune
 * any agent's voice / specialty without touching code, and a `Reset` button
 * can wipe the override to recover the shipped default.
 */
export interface AgentPromptOverride {
    /** Replaces the persona block. Multi-line markdown allowed. */
    persona?: string;
    /** Replaces the specialty list shown to the CEO planner + specialist. */
    specialty?: string;
    /** Replaces the short tagline shown in the manage UI. */
    tagline?: string;
}

/**
 * User edits to an agent's *identity* surface (name / role title / emoji /
 * color). Unlike prompt overrides these don't affect the LLM's behaviour
 * directly — they change how the agent is *referred to* across the UI,
 * planner enumeration, and CEO report. Built-ins ship with fixed defaults;
 * the user can rename "레오" to "Lily" via this override without forking
 * the shipped roster. CEO planner's `_canonicalAgentId` already matches
 * against the effective (override-applied) name, so renaming an agent
 * still lets the LLM dispatch to it correctly.
 */
export interface AgentDisplayOverride {
    /** Replaces the display name (e.g. "레오" → "Lily"). */
    name?: string;
    /** Replaces the role title (e.g. "Head of YouTube" → "콘텐츠 디렉터"). */
    role?: string;
    /** Replaces the single emoji icon. */
    emoji?: string;
    /** Replaces the brand color (CSS hex). */
    color?: string;
}

/**
 * Persisted runtime state for the company mode. Stored in VS Code's
 * `globalState` plus a small JSON file under `.astra/company/_shared/`.
 */
export interface CompanyState {
    /** When false, the chip is shown but prompts route through normal chat. */
    enabled: boolean;
    /** User-facing name surfaced in CEO prompts and the chip badge. */
    companyName: string;
    /** Agents the user has toggled on. CEO is implicitly included. */
    activeAgentIds: string[];
    /**
     * Optional per-agent model override. Empty string / missing key means
     * "use the global default model". When the user assigns *different*
     * models to two agents, the LM Studio lifecycle manager unloads one and
     * loads the other between dispatches — RAM holds exactly one model at a
     * time, by design.
     */
    modelOverrides: Record<string, string>;
    /**
     * Optional per-agent prompt edits (persona / specialty / tagline).
     * Empty record = every agent uses its shipped default. The dispatcher
     * resolves the effective values via `resolveAgentPrompt` at build time
     * so changes apply on the very next turn — no restart required.
     */
    promptOverrides: Record<string, AgentPromptOverride>;
    /**
     * Optional per-agent Knowledge Mix override — same semantics as the
     * global `g1nation.knowledgeMix.secondBrainWeight` setting (0–100, where
     * higher means "lean on Second Brain evidence harder"). Missing key →
     * fall back to the global. Stored as integer.
     *
     * Why per-agent matters here: a Developer dispatch usually wants high
     * model knowledge (general coding patterns) and minimal brain noise,
     * while a Researcher / Writer benefits from heavy brain retrieval
     * because their job is to cite recorded knowledge.
     */
    knowledgeMixOverrides: Record<string, number>;
    /**
     * User-added agents. Merged with the built-in `COMPANY_AGENTS` roster at
     * read time — the built-ins are immutable code; everything in here is
     * user-defined and editable. Keys are agent ids (lowercase, kebab-case),
     * which must NOT collide with built-in ids.
     */
    customAgents?: Record<string, CompanyAgentDef>;
    /**
     * Per-agent 직군 override. Lets the user re-classify a built-in agent
     * without forking the code (e.g. promote Writer from `planner` to
     * `inspector` if they want that workflow). Missing key → use the
     * agent def's own `roleCategory`.
     */
    roleCategoryOverrides?: Record<string, AgentRoleCategory>;
    /**
     * Per-agent identity overrides (name / role / emoji / color). Same
     * semantics as `promptOverrides` but for the display surface — the
     * effective values are merged into the def at read time via
     * `resolveAgent`.
     */
    displayOverrides?: Record<string, AgentDisplayOverride>;
    /**
     * User-defined work pipelines. When `activePipelineId` is set to a key
     * here, the dispatcher runs the pipeline's stages in order instead of
     * letting the CEO planner emit ad-hoc tasks. Empty / unset → legacy
     * behaviour (CEO planner → linear dispatch).
     */
    pipelines?: Record<string, PipelineDef>;
    /**
     * Which pipeline drives this turn. `null` / undefined → legacy CEO
     * planner path. Must reference a key in `pipelines` or it is ignored.
     */
    activePipelineId?: string | null;
}

/**
 * One step of a custom pipeline. Each stage runs a specific agent with a
 * specific instruction template (supports `{{userPrompt}}` and stage-output
 * variables like `{{stage.plan}}`). The stage can optionally loop back to
 * a previous stage when its output matches a regex — the canonical use
 * case is "QA finds a bug → go back to dev".
 */
export interface PipelineStage {
    /** Stable id within the pipeline. Used for `loopBackTo` references. */
    id: string;
    /** Human label shown in the chat phase header and the editor. */
    label: string;
    /** Which agent runs this stage. Must resolve via `resolveAgent`. */
    agentId: string;
    /**
     * 직군 hint stored at save time. Lets the editor re-open with the
     * correct 직군 dropdown without having to re-derive it from agentId
     * (handy when the user later changes the agent's 직군 override). The
     * dispatcher itself doesn't read this — it goes straight from agentId
     * to `resolveAgent`.
     */
    roleCategory?: string;
    /**
     * Stage-level model override. Empty / missing → fall back to the
     * agent's own model override → global default. Use case: light planning
     * stages on a fast model (e.g. gemma e2b), heavy implementation stages
     * on a strong model (e.g. qwen 14b). LM Studio's lifecycle manager
     * swaps the model when the next stage's effective model differs.
     */
    modelOverride?: string;
    /**
     * Manual approval gate. When true, the dispatcher pauses after this
     * stage completes and waits for the user to click 승인 / 수정요청 /
     * 중단 in the chat. 수정요청은 같은 stage를 사용자 코멘트와 함께
     * 다시 실행. The pause is bounded by the existing turn-abort signal
     * — if the user kills the turn instead of clicking, the wait resolves
     * to "aborted" cleanly.
     */
    requiresApproval?: boolean;
    /**
     * Instruction template. Tokens substituted before dispatch:
     *   - `{{userPrompt}}`        — what the user typed
     *   - `{{brief}}`             — CEO brief from the kickoff stage
     *   - `{{stage.<id>}}`        — full output of a previous stage
     * Empty → fall back to `{{userPrompt}}`.
     */
    instructionTemplate: string;
    /**
     * Regex (string form, case-insensitive) applied to this stage's output.
     * On match the dispatcher jumps back to `loopBackTo`. Iterations counted
     * — see `maxIterations`.
     */
    loopBackPattern?: string;
    /** Stage id to jump to on `loopBackPattern` match. Must precede this stage. */
    loopBackTo?: string;
    /** Safety cap on how many times this stage can loop back. Default 3. */
    maxIterations?: number;
}

export interface PipelineDef {
    id: string;
    name: string;
    stages: PipelineStage[];
}

/** Output of the CEO planner LLM call after JSON parsing. */
export interface CompanyTaskPlan {
    /** 2-3 sentence Korean summary of what the company is going to do. */
    brief: string;
    /** Ordered list of agent dispatches. Order is execution order. */
    tasks: Array<{
        /** Agent id (must exist in `AGENTS` and be active). */
        agent: string;
        /** Concrete, actionable instruction for the specialist. */
        task: string;
    }>;
}

/** One agent's contribution to a turn. */
export interface AgentTurnOutput {
    agentId: string;
    task: string;
    /** Raw LLM output, before action-tag execution. */
    response: string;
    /** Wall-clock milliseconds spent on this dispatch (LLM + tools). */
    durationMs: number;
    /** Populated when the dispatch failed; `response` then holds the error. */
    error?: string;
    /**
     * Lines returned by the action-tag executor — e.g. `"✅ Created: testtimer/timer.py"`.
     * Kept separate from `response` so downstream surfaces (Telegram mirror,
     * sidebar chip) can extract concrete file paths instead of regex-scraping
     * the prose. Empty / absent when the agent emitted no action tags.
     */
    actionReport?: string[];
}

/** The whole result of a company turn — persisted under sessions/<timestamp>/. */
export interface SessionResult {
    /** ISO timestamp used as the session directory name. */
    timestamp: string;
    /** Absolute filesystem path of the session directory. */
    sessionDir: string;
    /** What the user typed. */
    userPrompt: string;
    /** The CEO's plan that drove this turn. */
    plan: CompanyTaskPlan;
    /** Per-agent outputs, in execution order. */
    agentOutputs: AgentTurnOutput[];
    /** CEO's final synthesis. Empty when the synthesis call failed. */
    report: string;
    /** Walls-clock milliseconds from prompt arrival to report emission. */
    totalDurationMs: number;
}

/** Where on disk the company state lives, relative to the workspace root. */
export const COMPANY_DIR_REL = '.astra/company';
export const COMPANY_SHARED_REL = `${COMPANY_DIR_REL}/_shared`;
export const COMPANY_SESSIONS_REL = `${COMPANY_DIR_REL}/sessions`;
export const COMPANY_AGENTS_REL = `${COMPANY_DIR_REL}/_agents`;

/** State-key namespaces used in VS Code's globalState. */
export const COMPANY_STATE_KEY = 'g1nation.company.state';
