import type { App } from 'vue'
import type { Router } from 'vue-router'
import type { ModulePlugin, ModuleConfig, ModuleRegistration } from './types'
import { eventBus } from './event-bus'
import { container } from './di-container'

/**
 * Plugin Manager
 * Handles module registration, dependency resolution, and lifecycle management
 */
export class PluginManager {
  private modules = new Map<string, ModuleRegistration>()
  private app: App | null = null
  private router: Router | null = null
  private installOrder: string[] = []

  /**
   * Initialize the plugin manager
   */
  init(app: App, router: Router): void {
    this.app = app
    this.router = router
    
    // Register core services
    container.provide('app', app)
    container.provide('router', router)
    container.provide('eventBus', eventBus)
    
    console.log('🔧 Plugin Manager initialized')
  }

  /**
   * Register a module plugin
   */
  async register(plugin: ModulePlugin, config: ModuleConfig): Promise<void> {
    if (this.modules.has(plugin.name)) {
      throw new Error(`Module ${plugin.name} is already registered`)
    }

    // Validate dependencies
    const missingDeps = this.validateDependencies(plugin)
    if (missingDeps.length > 0) {
      throw new Error(`Module ${plugin.name} has missing dependencies: ${missingDeps.join(', ')}`)
    }

    // Register the module
    const registration: ModuleRegistration = {
      plugin,
      config,
      installed: false
    }

    this.modules.set(plugin.name, registration)
    console.log(`📦 Registered module: ${plugin.name} v${plugin.version}`)

    // Routes are now pre-registered during router creation
    // This registration step is kept for potential dynamic route additions in the future
    if (plugin.routes && this.router) {
      console.log(`🛤️  ${plugin.name} routes already pre-registered (${plugin.routes.length} routes)`)
    }

    // Auto-install if enabled and not lazy
    if (config.enabled && !config.lazy) {
      await this.install(plugin.name)
    }

    eventBus.emit('module:registered', { name: plugin.name, config }, 'plugin-manager')
  }

  /**
   * Install a module
   */
  async install(moduleName: string): Promise<void> {
    const registration = this.modules.get(moduleName)
    if (!registration) {
      throw new Error(`Module ${moduleName} is not registered`)
    }

    if (registration.installed) {
      console.warn(`Module ${moduleName} is already installed`)
      return
    }

    const { plugin, config } = registration

    // Install dependencies first
    if (plugin.dependencies) {
      for (const dep of plugin.dependencies) {
        const depRegistration = this.modules.get(dep)
        if (!depRegistration) {
          throw new Error(`Dependency ${dep} is not registered`)
        }
        if (!depRegistration.installed) {
          await this.install(dep)
        }
      }
    }

    try {
      // Install the module
      if (!this.app) {
        throw new Error('Plugin manager not initialized')
      }

      await plugin.install(this.app, { config: config.config })

      // Routes are already registered during the register() phase

      // Register services in DI container
      if (plugin.services) {
        for (const [name, service] of Object.entries(plugin.services)) {
          container.provide(Symbol(name), service)
        }
      }

      // Mark as installed
      registration.installed = true
      registration.installTime = Date.now()
      this.installOrder.push(moduleName)

      console.log(`✅ Installed module: ${moduleName}`)
      eventBus.emit('module:installed', { name: moduleName, plugin, config }, 'plugin-manager')

    } catch (error) {
      console.error(`❌ Failed to install module ${moduleName}:`, error)
      eventBus.emit('module:install-failed', { name: moduleName, error }, 'plugin-manager')
      throw error
    }
  }

  /**
   * Uninstall a module
   */
  async uninstall(moduleName: string): Promise<void> {
    const registration = this.modules.get(moduleName)
    if (!registration) {
      throw new Error(`Module ${moduleName} is not registered`)
    }

    if (!registration.installed) {
      console.warn(`Module ${moduleName} is not installed`)
      return
    }

    // Check for dependents
    const dependents = this.findDependents(moduleName)
    if (dependents.length > 0) {
      throw new Error(`Cannot uninstall ${moduleName}: required by ${dependents.join(', ')}`)
    }

    try {
      // Call module's uninstall hook
      if (registration.plugin.uninstall) {
        await registration.plugin.uninstall()
      }

      // Remove routes
      if (registration.plugin.routes && this.router) {
        // Note: Vue Router doesn't have removeRoute, so we'd need to track and rebuild
        // For now, we'll just log this limitation
        console.warn(`Routes from ${moduleName} cannot be removed (Vue Router limitation)`)
      }

      // Remove services from DI container
      if (registration.plugin.services) {
        for (const name of Object.keys(registration.plugin.services)) {
          container.remove(Symbol(name))
        }
      }

      // Mark as uninstalled
      registration.installed = false
      registration.installTime = undefined
      
      const orderIndex = this.installOrder.indexOf(moduleName)
      if (orderIndex !== -1) {
        this.installOrder.splice(orderIndex, 1)
      }

      console.log(`🗑️ Uninstalled module: ${moduleName}`)
      eventBus.emit('module:uninstalled', { name: moduleName }, 'plugin-manager')

    } catch (error) {
      console.error(`❌ Failed to uninstall module ${moduleName}:`, error)
      eventBus.emit('module:uninstall-failed', { name: moduleName, error }, 'plugin-manager')
      throw error
    }
  }

  /**
   * Get module registration info
   */
  getModule(name: string): ModuleRegistration | undefined {
    return this.modules.get(name)
  }

  /**
   * Get all registered modules
   */
  getModules(): Map<string, ModuleRegistration> {
    return new Map(this.modules)
  }

  /**
   * Get installed modules in installation order
   */
  getInstalledModules(): string[] {
    return [...this.installOrder]
  }

  /**
   * Check if a module is installed
   */
  isInstalled(name: string): boolean {
    return this.modules.get(name)?.installed || false
  }

  /**
   * Get module status
   */
  getStatus(): {
    registered: number
    installed: number
    failed: number
    modules: Array<{ name: string; version: string; installed: boolean; dependencies?: string[] }>
  } {
    const modules = Array.from(this.modules.values())
    
    return {
      registered: modules.length,
      installed: modules.filter(m => m.installed).length,
      failed: modules.filter(m => !m.installed && m.config.enabled).length,
      modules: modules.map(({ plugin, installed }) => ({
        name: plugin.name,
        version: plugin.version,
        installed,
        dependencies: plugin.dependencies
      }))
    }
  }

  /**
   * Validate module dependencies
   */
  private validateDependencies(plugin: ModulePlugin): string[] {
    if (!plugin.dependencies) {
      return []
    }

    return plugin.dependencies.filter(dep => !this.modules.has(dep))
  }

  /**
   * Find modules that depend on the given module
   */
  private findDependents(moduleName: string): string[] {
    const dependents: string[] = []

    for (const [name, registration] of this.modules) {
      if (registration.installed && 
          registration.plugin.dependencies?.includes(moduleName)) {
        dependents.push(name)
      }
    }

    return dependents
  }

  /**
   * Install all enabled modules
   */
  async installAll(): Promise<void> {
    const enabledModules = Array.from(this.modules.entries())
      .filter(([_, reg]) => reg.config.enabled && !reg.config.lazy)
      .map(([name, _]) => name)

    // Sort by dependencies to ensure correct installation order
    const sortedModules = this.topologicalSort(enabledModules)

    for (const moduleName of sortedModules) {
      if (!this.isInstalled(moduleName)) {
        await this.install(moduleName)
      }
    }
  }

  /**
   * Topological sort for dependency resolution
   */
  private topologicalSort(moduleNames: string[]): string[] {
    const visited = new Set<string>()
    const visiting = new Set<string>()
    const result: string[] = []

    const visit = (name: string) => {
      if (visiting.has(name)) {
        throw new Error(`Circular dependency detected involving module: ${name}`)
      }
      if (visited.has(name)) {
        return
      }

      visiting.add(name)

      const registration = this.modules.get(name)
      if (registration?.plugin.dependencies) {
        for (const dep of registration.plugin.dependencies) {
          if (moduleNames.includes(dep)) {
            visit(dep)
          }
        }
      }

      visiting.delete(name)
      visited.add(name)
      result.push(name)
    }

    for (const name of moduleNames) {
      if (!visited.has(name)) {
        visit(name)
      }
    }

    return result
  }
}

// Global plugin manager instance
export const pluginManager = new PluginManager()