/**
 * Disk persistence for company-mode session artefacts.
 *
 * Each company turn produces a timestamped directory:
 *
 *     <workspaceRoot>/.astra/company/sessions/2026-05-13T21-29/
 *     ├─ _brief.md            ← CEO's task decomposition
 *     ├─ <agentId>.md         ← Each specialist's raw output
 *     ├─ _report.md           ← CEO's final synthesis
 *     └─ _session.json        ← Structured copy of the SessionResult
 *
 * Long-lived per-agent memory + shared decisions live one level up under
 * `_agents/<id>/memory.md` and `_shared/decisions.md`. The store is
 * intentionally dumb — markdown files, append-only — so the user can read,
 * grep, or git-commit them by hand without any tooling.
 *
 * Path resolution: we always prefer the **workspace root**. When the user
 * opens Astra without a workspace (very rare), we fall back to the
 * extension's globalStorage path so the feature still works rather than
 * silently swallowing writes.
 */
import * as fs from 'fs';
import * as path from 'path';
import * as vscode from 'vscode';
import { logError, logInfo } from '../../utils';
import {
    AgentTurnOutput,
    COMPANY_AGENTS_REL,
    COMPANY_SESSIONS_REL,
    COMPANY_SHARED_REL,
    CompanyTaskPlan,
    SessionResult,
} from './types';

/**
 * Resolve the base directory for company data. Falls back to globalStorage
 * when no workspace is open so the mode still works in a fresh window.
 */
export function resolveCompanyBase(context: vscode.ExtensionContext): string {
    const ws = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
    if (ws) return path.join(ws, '.astra', 'company');
    return path.join(context.globalStorageUri.fsPath, 'company');
}

function _ensureDir(p: string): void {
    try {
        fs.mkdirSync(p, { recursive: true });
    } catch (e: any) {
        logError('company.sessionStore: mkdir failed.', { path: p, error: e?.message ?? String(e) });
    }
}

/**
 * Build a stable, filesystem-safe timestamp like `2026-05-13T21-29-04`.
 * Colons and milliseconds are stripped so the value is portable across
 * macOS / Linux / Windows.
 */
export function newSessionTimestamp(now: Date = new Date()): string {
    const iso = now.toISOString(); // 2026-05-13T21:29:04.123Z
    return iso.slice(0, 19).replace(/:/g, '-');
}

/**
 * Create a new session directory and return its absolute path. The directory
 * is empty — callers populate it via `writeBrief`, `writeAgentOutput`, etc.
 */
export function createSessionDir(
    context: vscode.ExtensionContext,
    timestamp: string,
): string {
    const base = resolveCompanyBase(context);
    const dir = path.join(base, 'sessions', timestamp);
    _ensureDir(dir);
    return dir;
}

/** Write the CEO planner's brief (`_brief.md`). */
export function writeBrief(
    sessionDir: string,
    userPrompt: string,
    plan: CompanyTaskPlan,
): void {
    const lines = [
        `# Brief — ${path.basename(sessionDir)}`,
        '',
        '## User Prompt',
        userPrompt.trim() || '_(empty)_',
        '',
        '## Summary',
        plan.brief.trim() || '_(no brief)_',
        '',
        '## Dispatched Tasks',
    ];
    if (plan.tasks.length === 0) {
        lines.push('_(no tasks — CEO decided no dispatch was necessary)_');
    } else {
        for (const [i, t] of plan.tasks.entries()) {
            lines.push(`${i + 1}. **${t.agent}** — ${t.task}`);
        }
    }
    fs.writeFileSync(path.join(sessionDir, '_brief.md'), lines.join('\n'), 'utf8');
}

/** Write a single specialist's output to `<agentId>.md`. */
export function writeAgentOutput(sessionDir: string, output: AgentTurnOutput): void {
    const lines = [
        `# ${output.agentId} — ${path.basename(sessionDir)}`,
        '',
        `**Task:** ${output.task}`,
        `**Duration:** ${(output.durationMs / 1000).toFixed(1)}s`,
        output.error ? `**Error:** ${output.error}` : '',
        '',
        '---',
        '',
        output.response,
        '',
    ].filter((l) => l !== '');
    fs.writeFileSync(path.join(sessionDir, `${output.agentId}.md`), lines.join('\n'), 'utf8');
}

/** Write the CEO's final synthesis to `_report.md`. */
export function writeReport(sessionDir: string, report: string): void {
    const header = `# Report — ${path.basename(sessionDir)}\n\n`;
    fs.writeFileSync(path.join(sessionDir, '_report.md'), header + report.trim() + '\n', 'utf8');
}

/**
 * Write a machine-readable copy of the whole turn for tooling (debugging,
 * replays, future analytics). Keeps the markdown files the source of truth
 * for the user — the JSON is just a convenience for code that reads it back.
 */
export function writeSessionJson(sessionDir: string, result: SessionResult): void {
    const cloned: SessionResult = {
        ...result,
        // Drop the absolute sessionDir from the JSON so the file is portable
        // across machines — it's already implicit (its own directory).
        sessionDir: path.basename(sessionDir),
    };
    fs.writeFileSync(path.join(sessionDir, '_session.json'), JSON.stringify(cloned, null, 2), 'utf8');
}

// ── Long-lived per-agent memory + shared decisions ─────────────────────────

function _agentMemoryPath(context: vscode.ExtensionContext, agentId: string): string {
    return path.join(resolveCompanyBase(context), '_agents', agentId, 'memory.md');
}

/**
 * Append a short note to `<agent>/memory.md`. Memory accumulates over time;
 * the dispatcher reads it back as part of the specialist's system prompt so
 * agents "remember" past work. Best-effort — failures are logged but never
 * abort the turn.
 */
export function appendAgentMemory(
    context: vscode.ExtensionContext,
    agentId: string,
    note: string,
): void {
    if (!note.trim()) return;
    const memPath = _agentMemoryPath(context, agentId);
    try {
        _ensureDir(path.dirname(memPath));
        const stamp = new Date().toISOString();
        const block = `\n\n## ${stamp}\n${note.trim()}\n`;
        fs.appendFileSync(memPath, block, 'utf8');
    } catch (e: any) {
        logError('company.sessionStore: agent memory append failed.', {
            agentId, error: e?.message ?? String(e),
        });
    }
}

/** Read `<agent>/memory.md` (or empty string if missing). */
export function readAgentMemory(context: vscode.ExtensionContext, agentId: string): string {
    const memPath = _agentMemoryPath(context, agentId);
    if (!fs.existsSync(memPath)) return '';
    try { return fs.readFileSync(memPath, 'utf8'); } catch { return ''; }
}

function _sharedPath(context: vscode.ExtensionContext, fileName: string): string {
    return path.join(resolveCompanyBase(context), '_shared', fileName);
}

/** Append a decision/learning to `_shared/decisions.md`. */
export function appendDecision(context: vscode.ExtensionContext, decision: string): void {
    if (!decision.trim()) return;
    const p = _sharedPath(context, 'decisions.md');
    try {
        _ensureDir(path.dirname(p));
        const stamp = new Date().toISOString();
        fs.appendFileSync(p, `- ${stamp} — ${decision.trim()}\n`, 'utf8');
    } catch (e: any) {
        logError('company.sessionStore: decisions append failed.', { error: e?.message ?? String(e) });
    }
}

/** Read `_shared/decisions.md` (or empty string). Trimmed to the last N chars. */
export function readDecisions(context: vscode.ExtensionContext, maxChars: number = 2000): string {
    const p = _sharedPath(context, 'decisions.md');
    if (!fs.existsSync(p)) return '';
    try {
        const raw = fs.readFileSync(p, 'utf8');
        return raw.length > maxChars ? '…' + raw.slice(-maxChars) : raw;
    } catch { return ''; }
}

/** List existing session directories, newest first. */
export function listSessions(context: vscode.ExtensionContext): string[] {
    const dir = path.join(resolveCompanyBase(context), 'sessions');
    if (!fs.existsSync(dir)) return [];
    try {
        return fs.readdirSync(dir)
            .filter((name) => fs.statSync(path.join(dir, name)).isDirectory())
            .sort()
            .reverse();
    } catch (e: any) {
        logError('company.sessionStore: list failed.', { error: e?.message ?? String(e) });
        return [];
    }
}

/** Convenience used by the chip after a turn finishes. */
export function logSessionCreated(sessionDir: string, agentCount: number): void {
    logInfo('company.sessionStore: session created.', {
        dir: path.basename(sessionDir),
        agents: agentCount,
    });
}

// Re-export path constants for callers that need to namespace under the same dirs.
export { COMPANY_AGENTS_REL, COMPANY_SESSIONS_REL, COMPANY_SHARED_REL };
