/**
 * Hook 管理器
 *
 * 负责 hook 的注册、触发和管理
 */

import { spawn } from 'child_process';
import { minimatch } from 'minimatch';
import type {
  Hooks,
  HookType,
  HookConfig,
  HookEvent,
  HookEventListener,
  ShellCommandConfig,
  FileHookConfig,
  ToolExecuteBeforeInput,
  ToolExecuteBeforeOutput,
  ToolExecuteAfterInput,
  ToolExecuteAfterOutput,
  SessionStartInput,
  SessionEndInput,
  MessageBeforeInput,
  MessageBeforeOutput,
  MessageAfterInput,
  FileChangeInput,
  FileChangeOutput,
  Plugin,
  PluginInput,
} from './types.js';

/**
 * Hook 管理器
 */
export class HookManager {
  /** 已注册的 hooks */
  private hooks: Hooks[] = [];

  /** 配置型 hooks（从配置文件加载） */
  private configHooks: HookConfig | null = null;

  /** 事件监听器 */
  private eventListeners: HookEventListener[] = [];

  /** 当前工作目录 */
  private workdir: string;

  /** 会话 ID */
  private sessionId: string;

  constructor(workdir: string, sessionId?: string) {
    this.workdir = workdir;
    this.sessionId = sessionId || 'default';
  }

  /**
   * 注册插件
   */
  async registerPlugin(plugin: Plugin): Promise<void> {
    const input: PluginInput = {
      workdir: this.workdir,
      sessionId: this.sessionId,
    };

    try {
      const hooks = await plugin(input);
      this.hooks.push(hooks);
    } catch (error) {
      console.error('Failed to register plugin:', error);
    }
  }

  /**
   * 注册 hooks 对象
   */
  registerHooks(hooks: Hooks): void {
    this.hooks.push(hooks);
  }

  /**
   * 设置配置型 hooks
   */
  setConfigHooks(config: HookConfig): void {
    this.configHooks = config;
  }

  /**
   * 添加事件监听器
   */
  addEventListener(listener: HookEventListener): void {
    this.eventListeners.push(listener);
  }

  /**
   * 移除事件监听器
   */
  removeEventListener(listener: HookEventListener): void {
    const index = this.eventListeners.indexOf(listener);
    if (index !== -1) {
      this.eventListeners.splice(index, 1);
    }
  }

  /**
   * 发送事件
   */
  private emitEvent(type: HookType, data: unknown): void {
    const event: HookEvent = {
      type,
      timestamp: Date.now(),
      data,
    };

    for (const listener of this.eventListeners) {
      try {
        listener(event);
      } catch (error) {
        console.error('Event listener error:', error);
      }
    }
  }

  /**
   * 触发工具执行前 hook
   */
  async triggerToolExecuteBefore(
    input: ToolExecuteBeforeInput
  ): Promise<ToolExecuteBeforeOutput> {
    const output: ToolExecuteBeforeOutput = {
      args: { ...input.args },
    };

    for (const hook of this.hooks) {
      if (hook['tool.execute.before']) {
        try {
          await hook['tool.execute.before'](input, output);
        } catch (error) {
          console.error('Hook tool.execute.before error:', error);
        }
      }
    }

    this.emitEvent('tool.execute.before', { input, output });
    return output;
  }

  /**
   * 触发工具执行后 hook
   */
  async triggerToolExecuteAfter(
    input: ToolExecuteAfterInput,
    result: ToolExecuteAfterOutput['result']
  ): Promise<ToolExecuteAfterOutput> {
    const output: ToolExecuteAfterOutput = {
      result: { ...result },
    };

    for (const hook of this.hooks) {
      if (hook['tool.execute.after']) {
        try {
          await hook['tool.execute.after'](input, output);
        } catch (error) {
          console.error('Hook tool.execute.after error:', error);
        }
      }
    }

    this.emitEvent('tool.execute.after', { input, output });
    return output;
  }

  /**
   * 触发会话开始 hook
   */
  async triggerSessionStart(input: SessionStartInput): Promise<void> {
    for (const hook of this.hooks) {
      if (hook['session.start']) {
        try {
          await hook['session.start'](input);
        } catch (error) {
          console.error('Hook session.start error:', error);
        }
      }
    }

    this.emitEvent('session.start', input);
  }

  /**
   * 触发会话结束 hook
   */
  async triggerSessionEnd(input: SessionEndInput): Promise<void> {
    for (const hook of this.hooks) {
      if (hook['session.end']) {
        try {
          await hook['session.end'](input);
        } catch (error) {
          console.error('Hook session.end error:', error);
        }
      }
    }

    // 执行配置型 session_completed hooks
    if (this.configHooks?.session_completed) {
      await this.executeShellCommands(this.configHooks.session_completed);
    }

    this.emitEvent('session.end', input);
  }

  /**
   * 触发消息前 hook
   */
  async triggerMessageBefore(
    input: MessageBeforeInput
  ): Promise<MessageBeforeOutput> {
    const output: MessageBeforeOutput = {
      content: input.content,
    };

    for (const hook of this.hooks) {
      if (hook['message.before']) {
        try {
          await hook['message.before'](input, output);
        } catch (error) {
          console.error('Hook message.before error:', error);
        }
      }
    }

    this.emitEvent('message.before', { input, output });
    return output;
  }

  /**
   * 触发消息后 hook
   */
  async triggerMessageAfter(input: MessageAfterInput): Promise<void> {
    for (const hook of this.hooks) {
      if (hook['message.after']) {
        try {
          await hook['message.after'](input);
        } catch (error) {
          console.error('Hook message.after error:', error);
        }
      }
    }

    this.emitEvent('message.after', input);
  }

  /**
   * 触发文件编辑 hook
   */
  async triggerFileEdited(input: FileChangeInput): Promise<FileChangeOutput> {
    const output: FileChangeOutput = {};

    // 执行插件 hooks
    for (const hook of this.hooks) {
      if (hook['file.edited']) {
        try {
          await hook['file.edited'](input, output);
        } catch (error) {
          console.error('Hook file.edited error:', error);
        }
      }
    }

    // 执行配置型 hooks
    if (this.configHooks?.file_edited) {
      const results = await this.executeFileHooks(
        input.path,
        this.configHooks.file_edited
      );
      output.commandResults = results;
    }

    this.emitEvent('file.edited', { input, output });
    return output;
  }

  /**
   * 触发文件创建 hook
   */
  async triggerFileCreated(input: FileChangeInput): Promise<FileChangeOutput> {
    const output: FileChangeOutput = {};

    // 执行插件 hooks
    for (const hook of this.hooks) {
      if (hook['file.created']) {
        try {
          await hook['file.created'](input, output);
        } catch (error) {
          console.error('Hook file.created error:', error);
        }
      }
    }

    // 执行配置型 hooks
    if (this.configHooks?.file_created) {
      const results = await this.executeFileHooks(
        input.path,
        this.configHooks.file_created
      );
      output.commandResults = results;
    }

    this.emitEvent('file.created', { input, output });
    return output;
  }

  /**
   * 触发文件删除 hook
   */
  async triggerFileDeleted(input: FileChangeInput): Promise<FileChangeOutput> {
    const output: FileChangeOutput = {};

    // 执行插件 hooks
    for (const hook of this.hooks) {
      if (hook['file.deleted']) {
        try {
          await hook['file.deleted'](input, output);
        } catch (error) {
          console.error('Hook file.deleted error:', error);
        }
      }
    }

    // 执行配置型 hooks
    if (this.configHooks?.file_deleted) {
      const results = await this.executeFileHooks(
        input.path,
        this.configHooks.file_deleted
      );
      output.commandResults = results;
    }

    this.emitEvent('file.deleted', { input, output });
    return output;
  }

  /**
   * 执行文件 hooks
   * 根据文件路径匹配 glob 模式并执行对应命令
   */
  private async executeFileHooks(
    filePath: string,
    config: FileHookConfig
  ): Promise<FileChangeOutput['commandResults']> {
    const results: FileChangeOutput['commandResults'] = [];

    for (const [pattern, commands] of Object.entries(config)) {
      // 使用 minimatch 进行 glob 匹配
      if (minimatch(filePath, pattern, { matchBase: true })) {
        const commandResults = await this.executeShellCommands(commands, {
          FILE_PATH: filePath,
        });
        results.push(...commandResults);
      }
    }

    return results;
  }

  /**
   * 执行 shell 命令列表
   */
  private async executeShellCommands(
    commands: ShellCommandConfig[],
    extraEnv?: Record<string, string>
  ): Promise<Array<{ command: string[]; success: boolean; output?: string; error?: string }>> {
    const results: Array<{
      command: string[];
      success: boolean;
      output?: string;
      error?: string;
    }> = [];

    for (const cmdConfig of commands) {
      const result = await this.executeShellCommand(cmdConfig, extraEnv);
      results.push(result);
    }

    return results;
  }

  /**
   * 执行单个 shell 命令
   */
  private executeShellCommand(
    config: ShellCommandConfig,
    extraEnv?: Record<string, string>
  ): Promise<{ command: string[]; success: boolean; output?: string; error?: string }> {
    return new Promise((resolve) => {
      const [cmd, ...args] = config.command;
      const timeout = config.timeout || 30000;
      const cwd = config.cwd || this.workdir;

      const env = {
        ...process.env,
        ...config.environment,
        ...extraEnv,
      };

      let stdout = '';
      let stderr = '';

      const child = spawn(cmd, args, {
        cwd,
        env,
        shell: true,
      });

      const timer = setTimeout(() => {
        child.kill('SIGTERM');
        resolve({
          command: config.command,
          success: false,
          error: `Command timed out after ${timeout}ms`,
        });
      }, timeout);

      child.stdout?.on('data', (data) => {
        stdout += data.toString();
      });

      child.stderr?.on('data', (data) => {
        stderr += data.toString();
      });

      child.on('close', (code) => {
        clearTimeout(timer);
        resolve({
          command: config.command,
          success: code === 0,
          output: stdout.trim() || undefined,
          error: stderr.trim() || undefined,
        });
      });

      child.on('error', (error) => {
        clearTimeout(timer);
        resolve({
          command: config.command,
          success: false,
          error: error.message,
        });
      });
    });
  }

  /**
   * 获取所有已注册的 hooks 数量
   */
  getHookCount(): number {
    return this.hooks.length;
  }

  /**
   * 清空所有 hooks
   */
  clear(): void {
    this.hooks = [];
    this.configHooks = null;
    this.eventListeners = [];
  }
}

// 全局 Hook 管理器实例
let globalHookManager: HookManager | null = null;

/**
 * 获取全局 Hook 管理器
 */
export function getHookManager(): HookManager | null {
  return globalHookManager;
}

/**
 * 初始化全局 Hook 管理器
 */
export function initHookManager(workdir: string, sessionId?: string): HookManager {
  globalHookManager = new HookManager(workdir, sessionId);
  return globalHookManager;
}

/**
 * 重置全局 Hook 管理器
 */
export function resetHookManager(): void {
  if (globalHookManager) {
    globalHookManager.clear();
    globalHookManager = null;
  }
}
