/**
 * Agent Event Emitter
 *
 * Central hub for agent event emission and handling.
 */

import {
  EventType,
  type AgentEvent,
  type EventHandler,
  type ToolStartData,
  type ToolResultData,
  type ResponseData,
  type ErrorData,
  type ProgressData,
  type SessionStartData,
  type SessionEndData,
  type ClarificationData,
  type ContextFrameData,
  createEvent,
} from './types.js';

/**
 * Agent Event Emitter
 *
 * Manages event emission and handler subscriptions.
 * Multiple handlers can subscribe to receive all events.
 */
export class AgentEventEmitter {
  private handlers: Set<EventHandler> = new Set();
  private events: AgentEvent[] = [];
  private agentName?: string;

  constructor(agentName?: string) {
    this.agentName = agentName;
  }

  /**
   * Add an event handler
   */
  addHandler(handler: EventHandler): void {
    this.handlers.add(handler);
  }

  /**
   * Remove an event handler
   */
  removeHandler(handler: EventHandler): void {
    this.handlers.delete(handler);
  }

  /**
   * Emit an event to all handlers
   */
  emit(type: EventType, data?: Record<string, unknown>): AgentEvent {
    const event = createEvent(type, data ?? {}, this.agentName);
    this.events.push(event);

    for (const handler of this.handlers) {
      try {
        handler.handle(event);
      } catch (error) {
        console.error('Event handler error:', error);
      }
    }

    return event;
  }

  // ============================================================
  // Convenience Methods
  // ============================================================

  /**
   * Emit tool start event
   */
  emitToolStart(name: string, args?: Record<string, unknown>, toolId?: string): AgentEvent {
    const data: ToolStartData = { name, args, toolId };
    return this.emit(EventType.TOOL_START, data as Record<string, unknown>);
  }

  /**
   * Emit tool result event
   */
  emitToolResult(
    name: string,
    success: boolean,
    summary?: string,
    options?: { toolId?: string; duration?: number; error?: string }
  ): AgentEvent {
    const data: ToolResultData = {
      name,
      success,
      summary,
      toolId: options?.toolId,
      duration: options?.duration,
      error: options?.error,
    };
    return this.emit(EventType.TOOL_RESULT, data as Record<string, unknown>);
  }

  /**
   * Emit thinking event
   */
  emitThinking(message: string): AgentEvent {
    return this.emit(EventType.THINKING, { message });
  }

  /**
   * Emit progress event
   */
  emitProgress(message: string, percent?: number): AgentEvent {
    const data: ProgressData = { message, percent };
    return this.emit(EventType.PROGRESS, data as Record<string, unknown>);
  }

  /**
   * Emit response event
   */
  emitResponse(content: string, isFinal: boolean = true): AgentEvent {
    const data: ResponseData = { content, isFinal };
    return this.emit(EventType.RESPONSE, data as Record<string, unknown>);
  }

  /**
   * Emit partial response event
   */
  emitPartialResponse(content: string): AgentEvent {
    return this.emit(EventType.PARTIAL_RESPONSE, { content });
  }

  /**
   * Emit clarification request
   */
  emitClarification(question: string, context?: string, options?: string[]): AgentEvent {
    const data: ClarificationData = { question, context, options };
    return this.emit(EventType.CLARIFICATION, data as Record<string, unknown>);
  }

  /**
   * Emit error event
   */
  emitError(message: string, details?: string, code?: string): AgentEvent {
    const data: ErrorData = { message, details, code };
    return this.emit(EventType.ERROR, data as Record<string, unknown>);
  }

  /**
   * Emit warning event
   */
  emitWarning(message: string, details?: string): AgentEvent {
    return this.emit(EventType.WARNING, { message, details });
  }

  /**
   * Emit session start event
   */
  emitSessionStart(sessionId: string, goal?: string): AgentEvent {
    const data: SessionStartData = { sessionId, goal };
    return this.emit(EventType.SESSION_START, data as Record<string, unknown>);
  }

  /**
   * Emit session end event
   */
  emitSessionEnd(sessionId: string, success: boolean, duration?: number): AgentEvent {
    const data: SessionEndData = { sessionId, success, duration };
    return this.emit(EventType.SESSION_END, data as Record<string, unknown>);
  }

  /**
   * Emit sub-agent start event
   */
  emitSubagentStart(name: string, goal: string): AgentEvent {
    return this.emit(EventType.SUBAGENT_START, { name, goal });
  }

  /**
   * Emit sub-agent end event
   */
  emitSubagentEnd(name: string, success: boolean, result?: string): AgentEvent {
    return this.emit(EventType.SUBAGENT_END, { name, success, result });
  }

  /**
   * Emit context frame event
   */
  emitContextFrame(adding?: Record<string, unknown>, reading?: Record<string, unknown>): AgentEvent {
    const data: ContextFrameData = { adding, reading };
    return this.emit(EventType.CONTEXT_FRAME, data as Record<string, unknown>);
  }

  /**
   * Emit plan submitted event
   */
  emitPlanSubmitted(plan: string): AgentEvent {
    return this.emit(EventType.PLAN_SUBMITTED, { plan });
  }

  // ============================================================
  // Event History
  // ============================================================

  /**
   * Get all emitted events
   */
  getEvents(): AgentEvent[] {
    return [...this.events];
  }

  /**
   * Clear event history
   */
  clearEvents(): void {
    this.events = [];
  }

  /**
   * Get agent name
   */
  getAgentName(): string | undefined {
    return this.agentName;
  }

  /**
   * Set agent name
   */
  setAgentName(name: string): void {
    this.agentName = name;
  }
}

/**
 * Create an event emitter for an agent
 */
export function createEventEmitter(agentName?: string): AgentEventEmitter {
  return new AgentEventEmitter(agentName);
}
