import { logger as rootLogger } from '../config/logger'
import type { PluginContext, PluginInstance, PluginManifest } from './types'
import type { LoadedPlugin } from './plugin-loader'

export interface RegistryEntry {
  instance: PluginInstance
  manifest: PluginManifest
  active: boolean
  dir: string
}

/**
 * Registry central dos plugins.
 * Gerencia o lifecycle (activate/deactivate) e expõe metadados para a API admin.
 */
class PluginRegistry {
  private entries = new Map<string, RegistryEntry>()
  private ctx!: PluginContext

  /** Deve ser chamado uma vez no bootstrap com o contexto completo */
  setContext(ctx: PluginContext): void {
    this.ctx = ctx
  }

  /** Registra plugins carregados (sem ativar ainda) */
  register(plugins: LoadedPlugin[]): void {
    for (const { instance, manifest, dir } of plugins) {
      this.entries.set(manifest.name, { instance, manifest, active: false, dir })
    }
  }

  /** Ativa todos os plugins marcados como enabled no manifest.
   *  Plugins com dependências faltando são pulados com warning (não crasham o servidor). */
  async activateAll(): Promise<void> {
    for (const [name, entry] of this.entries) {
      if (!entry.manifest.enabled) continue
      try {
        await this.activate(name)
      } catch (err: any) {
        rootLogger.warn({ name, err: err.message }, '[PluginRegistry] Plugin pulado na inicialização')
      }
    }
  }

  async activate(name: string): Promise<void> {
    const entry = this.entries.get(name)
    if (!entry) throw new Error(`Plugin "${name}" não encontrado`)
    if (entry.active) return

    // Verifica dependências
    for (const dep of entry.manifest.dependencies) {
      const depEntry = this.entries.get(dep)
      if (!depEntry?.active) {
        throw new Error(`Dependência "${dep}" do plugin "${name}" não está ativa`)
      }
    }

    try {
      await entry.instance.activate(this.ctx)
      entry.active = true
      entry.manifest.enabled = true
      rootLogger.info({ name }, '[PluginRegistry] Plugin ativado')
    } catch (err) {
      rootLogger.error({ err, name }, '[PluginRegistry] Falha ao ativar plugin')
      throw err
    }
  }

  async deactivate(name: string): Promise<void> {
    const entry = this.entries.get(name)
    if (!entry) throw new Error(`Plugin "${name}" não encontrado`)
    if (!entry.active) return
    if (!entry.manifest.canDisable) throw new Error(`Plugin "${name}" não pode ser desativado`)

    // Verifica se algum plugin ativo depende deste
    for (const [depName, depEntry] of this.entries) {
      if (depEntry.active && depEntry.manifest.dependencies.includes(name)) {
        throw new Error(`Plugin "${depName}" depende de "${name}". Desative-o primeiro.`)
      }
    }

    try {
      await entry.instance.deactivate(this.ctx)
      entry.active = false
      entry.manifest.enabled = false
      rootLogger.info({ name }, '[PluginRegistry] Plugin desativado')
    } catch (err) {
      rootLogger.error({ err, name }, '[PluginRegistry] Falha ao desativar plugin')
      throw err
    }
  }

  /** Retorna lista de todos os plugins com status para a API admin */
  list(): Array<RegistryEntry & { name: string }> {
    return Array.from(this.entries.entries()).map(([name, entry]) => ({ name, ...entry }))
  }

  getEntry(name: string): RegistryEntry | undefined {
    return this.entries.get(name)
  }

  /**
   * Força reativação de um plugin sem passar pelo deactivate.
   * Útil quando canDisable=false mas a config foi atualizada e precisa ser relida.
   */
  async reactivate(name: string): Promise<void> {
    const entry = this.entries.get(name)
    if (!entry) throw new Error(`Plugin "${name}" não encontrado`)
    entry.active = false
    await this.activate(name)
    rootLogger.info({ name }, '[PluginRegistry] Plugin reativado com nova config')
  }

  /** Retorna menu items de todos os plugins ativos */
  getActiveMenuItems(): Array<{ pluginName: string; items: PluginManifest['frontend'] }> {
    return Array.from(this.entries.values())
      .filter((e) => e.active && e.manifest.frontend?.menuItems?.length)
      .map((e) => ({ pluginName: e.manifest.name, items: e.manifest.frontend }))
  }
}

export const pluginRegistry = new PluginRegistry()
