/**
 * Claude Code Plugin API
 *
 * Developer-friendly API for creating Claude Code plugins
 * Inspired by Conduit's component system
 */

import { HookSystem, HookEvent, HookContext } from './hook-system'
import { SecurityManager, Sandbox, Permission } from './security'

// ============================================================================
// TYPES AND INTERFACES
// ============================================================================

export interface PluginConfig {
  name: string
  version: string
  description: string
  author: string
  license?: string
  repository?: string
  permissions?: Permission[]
  enabled?: boolean
}

export interface CommandHandler {
  description: string
  parameters?: Record<string, any>
  handler: (args: any, context: PluginContext) => Promise<any>
}

export interface ToolExtension {
  name: string
  description: string
  parameters?: any
  handler: (args: any, context: PluginContext) => Promise<any>
}

export interface PluginContext {
  plugin: string
  session: {
    id?: string
    messageId?: string
  }
  config: Map<string, any>
  sandbox: Sandbox
}

export type HookHandler = (context: HookContext) => Promise<void | any>

// ============================================================================
// PLUGIN CLASS
// ============================================================================

export class Plugin {
  public readonly name: string
  public readonly version: string
  public readonly description: string
  public readonly author: string
  public readonly license?: string
  public readonly repository?: string
  private permissions: Permission[]
  private enabled: boolean
  private commands: Map<string, CommandHandler> = new Map()
  private tools: ToolExtension[] = []
  private hooks: Map<HookEvent, HookHandler[]> = new Map()
  private config: Map<string, any> = new Map()
  private security: SecurityManager
  private hookSystem: HookSystem

  constructor(config: PluginConfig, security: SecurityManager, hookSystem: HookSystem) {
    this.name = config.name
    this.version = config.version
    this.description = config.description
    this.author = config.author
    this.license = config.license
    this.repository = config.repository
    this.permissions = config.permissions || []
    this.enabled = config.enabled !== false
    this.security = security
    this.hookSystem = hookSystem

    // Create security context
    this.security.createContext(this.name, this.permissions)
  }

  // ============================================================================
  // LIFECYCLE HOOKS
  // ============================================================================

  async onLoad?(): Promise<void>
  async onUnload?(): Promise<void>
  async onEnable?(): Promise<void>
  async onDisable?(): Promise<void>

  // ============================================================================
  // COMMAND REGISTRATION
  // ============================================================================

  registerCommand(name: string, handler: CommandHandler): void {
    this.commands.set(name, handler)
  }

  getCommand(name: string): CommandHandler | undefined {
    return this.commands.get(name)
  }

  listCommands(): string[] {
    return Array.from(this.commands.keys())
  }

  async executeCommand(name: string, args: any, context: PluginContext): Promise<any> {
    const command = this.commands.get(name)

    if (!command) {
      throw new Error(`Command "${name}" not found in plugin "${this.name}"`)
    }

    return await command.handler(args, context)
  }

  // ============================================================================
  // TOOL EXTENSIONS
  // ============================================================================

  registerTool(tool: ToolExtension): void {
    this.tools.push(tool)
  }

  getTools(): ToolExtension[] {
    return [...this.tools]
  }

  // ============================================================================
  // HOOK REGISTRATION
  // ============================================================================

  on(event: HookEvent, handler: HookHandler): void {
    const existing = this.hooks.get(event) || []
    existing.push(handler)
    this.hooks.set(event, existing)
  }

  async executeHooks(event: HookEvent, context: HookContext): Promise<void> {
    const handlers = this.hooks.get(event) || []

    for (const handler of handlers) {
      await handler(context)
    }
  }

  // ============================================================================
  // CONFIGURATION
  // ============================================================================

  setConfig(key: string, value: any): void {
    this.config.set(key, value)
  }

  getConfig<T>(key: string): T | undefined {
    return this.config.get(key) as T
  }

  getAllConfig(): Map<string, any> {
    return new Map(this.config)
  }

  loadConfig(configObj: Record<string, any>): void {
    for (const [key, value] of Object.entries(configObj)) {
      this.config.set(key, value)
    }
  }

  // ============================================================================
  // STATE MANAGEMENT
  // ============================================================================

  enable(): void {
    this.enabled = true
  }

  disable(): void {
    this.enabled = false
  }

  isEnabled(): boolean {
    return this.enabled
  }

  // ============================================================================
  // SECURITY
  // ============================================================================

  hasPermission(permission: Permission): boolean {
    return this.permissions.includes(permission)
  }

  createSandbox(): Sandbox {
    const context = this.security.getContext(this.name)
    if (!context) {
      throw new Error(`Security context not found for plugin "${this.name}"`)
    }
    return new Sandbox(this.security, context)
  }

  // ============================================================================
  // METADATA
  // ============================================================================

  getMetadata(): PluginConfig {
    return {
      name: this.name,
      version: this.version,
      description: this.description,
      author: this.author,
      license: this.license,
      repository: this.repository,
      permissions: this.permissions,
      enabled: this.enabled,
    }
  }

  toJSON(): Record<string, any> {
    return {
      name: this.name,
      version: this.version,
      description: this.description,
      author: this.author,
      license: this.license,
      repository: this.repository,
      permissions: this.permissions,
      enabled: this.enabled,
      commands: this.listCommands(),
      tools: this.tools.length,
      hooks: Array.from(this.hooks.keys()),
    }
  }
}

// ============================================================================
// PLUGIN BUILDER - Fluent API for Creating Plugins
// ============================================================================

export class PluginBuilder {
  private config: Partial<PluginConfig> = {}
  private commandHandlers: Map<string, CommandHandler> = new Map()
  private toolExtensions: ToolExtension[] = []
  private hookHandlers: Map<HookEvent, HookHandler[]> = new Map()
  private configValues: Map<string, any> = new Map()

  name(name: string): this {
    this.config.name = name
    return this
  }

  version(version: string): this {
    this.config.version = version
    return this
  }

  description(description: string): this {
    this.config.description = description
    return this
  }

  author(author: string): this {
    this.config.author = author
    return this
  }

  license(license: string): this {
    this.config.license = license
    return this
  }

  repository(repository: string): this {
    this.config.repository = repository
    return this
  }

  permissions(...permissions: Permission[]): this {
    this.config.permissions = permissions
    return this
  }

  enabled(enabled: boolean): this {
    this.config.enabled = enabled
    return this
  }

  command(name: string, handler: CommandHandler): this {
    this.commandHandlers.set(name, handler)
    return this
  }

  tool(tool: ToolExtension): this {
    this.toolExtensions.push(tool)
    return this
  }

  hook(event: HookEvent, handler: HookHandler): this {
    const existing = this.hookHandlers.get(event) || []
    existing.push(handler)
    this.hookHandlers.set(event, existing)
    return this
  }

  config(key: string, value: any): this {
    this.configValues.set(key, value)
    return this
  }

  onLoad(handler: () => Promise<void>): this {
    return this.hook('PluginLoad', handler as HookHandler)
  }

  onUnload(handler: () => Promise<void>): this {
    return this.hook('PluginUnload', handler as HookHandler)
  }

  onSessionStart(handler: HookHandler): this {
    return this.hook('SessionStart', handler)
  }

  onSessionEnd(handler: HookHandler): this {
    return this.hook('SessionEnd', handler)
  }

  onPreToolUse(handler: HookHandler): this {
    return this.hook('PreToolUse', handler)
  }

  onPostToolUse(handler: HookHandler): this {
    return this.hook('PostToolUse', handler)
  }

  onPreFileEdit(handler: HookHandler): this {
    return this.hook('PreFileEdit', handler)
  }

  onPostFileEdit(handler: HookHandler): this {
    return this.hook('PostFileEdit', handler)
  }

  build(security: SecurityManager, hookSystem: HookSystem): Plugin {
    if (!this.config.name || !this.config.version || !this.config.author) {
      throw new Error('Plugin must have name, version, and author')
    }

    const plugin = new Plugin(
      this.config as PluginConfig,
      security,
      hookSystem
    )

    // Register commands
    for (const [name, handler] of this.commandHandlers) {
      plugin.registerCommand(name, handler)
    }

    // Register tools
    for (const tool of this.toolExtensions) {
      plugin.registerTool(tool)
    }

    // Register hooks
    for (const [event, handlers] of this.hookHandlers) {
      for (const handler of handlers) {
        plugin.on(event, handler)
      }
    }

    // Load config
    plugin.loadConfig(Object.fromEntries(this.configValues))

    return plugin
  }
}

// ============================================================================
// HELPER FUNCTIONS
// ============================================================================

export function createPlugin(
  config: PluginConfig,
  builder?: (plugin: PluginBuilder) => PluginBuilder
): PluginBuilder {
  const pb = new PluginBuilder()

  if (config.name) pb.name(config.name)
  if (config.version) pb.version(config.version)
  if (config.description) pb.description(config.description)
  if (config.author) pb.author(config.author)
  if (config.license) pb.license(config.license)
  if (config.repository) pb.repository(config.repository)
  if (config.permissions) pb.permissions(...config.permissions)
  if (config.enabled !== undefined) pb.enabled(config.enabled)

  return builder ? builder(pb) : pb
}

export function definePlugin(
  config: PluginConfig,
  definition: (pb: PluginBuilder) => void
): PluginBuilder {
  const builder = new PluginBuilder()

  // Set basic config
  if (config.name) builder.name(config.name)
  if (config.version) builder.version(config.version)
  if (config.description) builder.description(config.description)
  if (config.author) builder.author(config.author)
  if (config.license) builder.license(config.license)
  if (config.repository) builder.repository(config.repository)
  if (config.permissions) builder.permissions(...config.permissions)
  if (config.enabled !== undefined) builder.enabled(config.enabled)

  // Apply definition
  definition(builder)

  return builder
}

// ============================================================================
// EXPORTS
// ============================================================================

export default Plugin
