/**
 * Parallel Agent Manager
 * 
 * Session-based async agent execution with:
 * - Concurrency control per agent type
 * - Batched notifications
 * - Automatic cleanup
 * 
 * This is the main facade that composes the specialized components.
 */

import type { PluginInput } from "@opencode-ai/plugin";
import {
    TASK_STATUS,
    PART_TYPES,
    MESSAGE_ROLES,
    AGENT_NAMES,
    type LaunchInput,
    type ResumeInput,
    type ParallelTask,
} from "../../shared/index.js";
import { ConcurrencyController } from "./concurrency.js";
import { TaskStore } from "./task-store.js";
import { log } from "./logger.js";
import { formatDuration } from "./format.js";

// Import components
import { TaskLauncher } from "./manager/task-launcher.js";
import { TaskResumer } from "./manager/task-resumer.js";
import { TaskPoller } from "./manager/task-poller.js";
import { TaskCleaner } from "./manager/task-cleaner.js";
import { EventHandler } from "./manager/event-handler.js";
import { SessionPool } from "./session-pool.js";
import { getTaskToastManager } from "../notification/task-toast-manager.js";
import { progressNotifier } from "../progress/progress-notifier.js";
import { MemoryLevel, MemoryManager } from "../memory/memory-manager.js";
import { CORE_PHILOSOPHY } from "../../agents/prompts/shared/philosophy.js";
import { AgentRegistry } from "./agent-registry.js";
import { TodoManager } from "../todo/todo-manager.js";
import type { ConcurrencyConfig } from "./concurrency.js";

// Re-export
export type { ParallelTask };
export { formatDuration };

type OpencodeClient = PluginInput["client"];
const UNIT_REVIEW_DESCRIPTION_LIMIT = 240;

export class ParallelAgentManager {
    private static _instance: ParallelAgentManager;

    private store = new TaskStore();
    private client: OpencodeClient;
    private directory: string;
    private concurrency: ConcurrencyController;
    private sessionPool: SessionPool;

    // Composed components
    private launcher: TaskLauncher;
    private resumer: TaskResumer;
    private poller: TaskPoller;
    private cleaner: TaskCleaner;
    private eventHandler: EventHandler;

    private constructor(client: OpencodeClient, directory: string, concurrencyConfig?: ConcurrencyConfig) {
        this.client = client;
        this.directory = directory;
        this.concurrency = new ConcurrencyController(concurrencyConfig);

        // Initialize Memory System
        const memory = MemoryManager.getInstance();
        memory.add(MemoryLevel.SYSTEM, CORE_PHILOSOPHY, 1.0);
        memory.add(MemoryLevel.PROJECT, `Working directory: ${directory}`, 0.9);

        // Initialize Agent Registry
        AgentRegistry.getInstance().setDirectory(directory);

        // Initialize Todo Manager
        TodoManager.getInstance().setDirectory(directory);

        // Initialize SessionPool
        this.sessionPool = SessionPool.getInstance(client, directory);

        // Enable work-stealing for all agent types (Phase 3-B)
        this.concurrency.enableWorkStealing(AGENT_NAMES.PLANNER, 2);  // 2 workers for planning
        this.concurrency.enableWorkStealing(AGENT_NAMES.WORKER, 8);   // 8 workers for parallel execution
        this.concurrency.enableWorkStealing(AGENT_NAMES.REVIEWER, 4); // 4 workers for reviews
        this.concurrency.enableWorkStealing(AGENT_NAMES.COMMANDER, 1); // 1 worker for command

        // Initialize cleaner first (needed by others)
        this.cleaner = new TaskCleaner(client, this.store, this.concurrency, this.sessionPool);

        // Initialize poller
        this.poller = new TaskPoller(
            client,
            this.store,
            this.concurrency,
            (parentSessionID) => this.cleaner.notifyParentIfAllComplete(parentSessionID),
            (taskId) => this.cleaner.scheduleCleanup(taskId),
            () => this.cleaner.pruneExpiredTasks(),
            (task) => this.handleTaskComplete(task)
        );

        // Initialize launcher
        this.launcher = new TaskLauncher(
            client,
            this.store,
            this.concurrency,
            this.sessionPool,
            (taskId, error) => this.handleTaskError(taskId, error),
            () => this.poller.start()
        );

        // Initialize resumer
        this.resumer = new TaskResumer(
            client,
            this.store,
            (sessionID) => this.findBySession(sessionID),
            () => this.poller.start(),
            (parentSessionID) => this.cleaner.notifyParentIfAllComplete(parentSessionID)
        );

        // Initialize event handler
        this.eventHandler = new EventHandler(
            client,
            this.store,
            this.concurrency,
            (sessionID) => this.findBySession(sessionID),
            (parentSessionID) => this.cleaner.notifyParentIfAllComplete(parentSessionID),
            (taskId) => this.cleaner.scheduleCleanup(taskId),
            (sessionID) => this.poller.validateSessionHasOutput(sessionID),
            (sessionID) => this.sessionPool.invalidate(sessionID),
            (task) => this.handleTaskComplete(task)
        );

        // Initialize ProgressNotifier
        progressNotifier.setManager(this);

        // Start TUI Monitor
        // TerminalMonitor has been deprecated in favor of native TUI integration (TaskToastManager)
        // TerminalMonitor.getInstance().start();
    }

    static getInstance(
        client?: OpencodeClient,
        directory?: string,
        concurrencyConfig?: ConcurrencyConfig,
    ): ParallelAgentManager {
        if (!ParallelAgentManager._instance) {
            if (!client || !directory) {
                throw new Error("ParallelAgentManager requires client and directory on first call");
            }
            ParallelAgentManager._instance = new ParallelAgentManager(client, directory, concurrencyConfig);
        }
        return ParallelAgentManager._instance;
    }

    // ========================================================================
    // Public API
    // ========================================================================

    async launch(inputs: LaunchInput | LaunchInput[]): Promise<ParallelTask | ParallelTask[]> {
        this.cleaner.pruneExpiredTasks();
        const result = await this.launcher.launch(inputs);
        progressNotifier.update();
        return result;
    }

    async resume(input: ResumeInput): Promise<ParallelTask> {
        return this.resumer.resume(input);
    }

    getTask(id: string): ParallelTask | undefined {
        return this.store.get(id);
    }

    getRunningTasks(): ParallelTask[] {
        return this.store.getRunning();
    }

    getAllTasks(): ParallelTask[] {
        return this.store.getAll();
    }

    getTasksByParent(parentSessionID: string): ParallelTask[] {
        return this.store.getByParent(parentSessionID);
    }

    async cancelTask(taskId: string): Promise<boolean> {
        const task = this.store.get(taskId);
        if (!task || task.status !== TASK_STATUS.RUNNING) return false;

        task.status = TASK_STATUS.ERROR;
        task.error = "Cancelled by user";
        task.completedAt = new Date();

        if (task.concurrencyKey) this.concurrency.release(task.concurrencyKey);
        this.store.untrackPending(task.parentSessionID, taskId);

        await this.sessionPool.invalidate(task.sessionID);

        this.cleaner.scheduleCleanup(taskId);



        progressNotifier.update();
        log(`Cancelled ${taskId}`);
        return true;
    }

    async getResult(taskId: string): Promise<string | null> {
        const task = this.store.get(taskId);
        if (!task) return null;
        if (task.result) return task.result;
        if (task.status === TASK_STATUS.ERROR) return `Error: ${task.error}`;
        if (task.status === TASK_STATUS.RUNNING) return null;

        try {
            const result = await this.client.session.messages({ path: { id: task.sessionID } });
            if (result.error) return `Error: ${result.error}`;

            const messages = (result.data ?? []) as Array<{ info?: { role?: string }; parts?: Array<{ type?: string; text?: string }> }>;
            const lastMsg = messages.filter(m => m.info?.role === MESSAGE_ROLES.ASSISTANT).reverse()[0];
            if (!lastMsg) return "(No response)";

            const text = lastMsg.parts?.filter(p => p.type === PART_TYPES.TEXT || p.type === PART_TYPES.REASONING).map(p => p.text ?? "").filter(Boolean).join("\n") ?? "";
            task.result = text;
            return text;
        } catch (error) {
            return `Error: ${error instanceof Error ? error.message : String(error)}`;
        }
    }

    setConcurrencyLimit(agentType: string, limit: number): void {
        this.concurrency.setLimit(agentType, limit);
    }

    configureConcurrency(config: ConcurrencyConfig): void {
        this.concurrency.configure(config);
    }

    getPendingCount(parentSessionID: string): number {
        return this.store.getPendingCount(parentSessionID);
    }

    getConcurrency(): ConcurrencyController {
        return this.concurrency;
    }

    cleanup(): void {
        this.poller.stop();
        this.store.clear();
        MemoryManager.getInstance().clearTaskMemory();
        // TerminalMonitor.getInstance().stop();
        import("../session/store.js").then(store => store.clearAll()).catch(() => { });
    }

    /**
     * Shutdown - alias for cleanup, releases all resources
     */
    async shutdown(): Promise<void> {
        this.cleanup();
        // Release session pool
        await this.sessionPool.shutdown().catch(() => {});
    }

    formatDuration = formatDuration;

    // ========================================================================
    // Event Handling
    // ========================================================================

    handleEvent(event: { type: string; properties?: { sessionID?: string; info?: { id?: string } } }): void {
        this.eventHandler.handle(event);
    }

    // ========================================================================
    // Private Helpers
    // ========================================================================

    private findBySession(sessionID: string): ParallelTask | undefined {
        return this.store.getAll().find(t => t.sessionID === sessionID);
    }

    private handleTaskError(taskId: string, error: unknown): void {
        const task = this.store.get(taskId);
        if (!task) return;

        task.status = TASK_STATUS.ERROR;
        task.error = error instanceof Error ? error.message : String(error);
        task.completedAt = new Date();

        if (task.concurrencyKey) {
            this.concurrency.release(task.concurrencyKey);
            this.concurrency.reportResult(task.concurrencyKey, false);
        }
        this.store.untrackPending(task.parentSessionID, taskId);
        this.cleaner.notifyParentIfAllComplete(task.parentSessionID);
        this.cleaner.scheduleCleanup(taskId);

        progressNotifier.update();


    }

    private async handleTaskComplete(task: ParallelTask): Promise<void> {
        // MSVP: Multi-Stage Verification Pipeline (Unit Review)
        // If a WORKER completes, immediately trigger a parallel REVIEWER
        if (task.agent === AGENT_NAMES.WORKER && task.mode !== "race") {
            log(`[MSVP] Triggering Unit Review for task ${task.id}`);

            try {
                await this.launch({
                    agent: AGENT_NAMES.REVIEWER,
                    description: `Unit Review: ${task.description}`,
                    prompt: buildUnitReviewPrompt(task),
                    parentSessionID: task.parentSessionID,
                    depth: task.depth,
                    groupID: task.groupID || task.id, // Group reviews with their origins
                });
            } catch (error) {
                log(`[MSVP] Failed to trigger review for ${task.id}:`, error);
            }
        }
        progressNotifier.update();
    }


}

export const parallelAgentManager = {
    getInstance: ParallelAgentManager.getInstance.bind(ParallelAgentManager),
    cleanup: () => {
        try {
            ParallelAgentManager.getInstance().cleanup();
        } catch {
            // Not initialized, ignore
        }
    },
};

export function buildUnitReviewPrompt(task: Pick<ParallelTask, "id" | "description">): string {
    return [
        "[UNIT REVIEW]",
        `task=${task.id}`,
        `desc=${compactWireValue(task.description, UNIT_REVIEW_DESCRIPTION_LIMIT)}`,
        "check=tests,quality,integration",
        "return=findings_only",
    ].join("\n");
}

function compactWireValue(value: string, limit: number): string {
    const compact = value.replace(/\s+/g, " ").trim();
    return compact.length > limit ? `${compact.slice(0, limit - 3)}...` : compact;
}
