'use strict'

const fs   = require('fs')
const path = require('path')
const { execute, queryOne, query } = require('../database/postgres')
const PluginConfigStore = require('./config')

// ─── Tabelas já criadas no schema.sql ───────────────────────────────────────
// plugins e plugin_configs existem — sem necessidade de CREATE aqui.

// ─── PluginManager ───────────────────────────────────────────────────────────

class PluginManager {
  constructor(app, dbAccess, httpServer = null) {
    this.app        = app
    this.db         = dbAccess        // { query, queryOne, execute } do postgres.js
    this.httpServer = httpServer      // http.Server nativo — para proxy WS
    this.plugins    = new Map()       // id → { manifest, instance, active }
  }

  // Carrega todos os plugins encontrados em plugins/*/
  async loadAll() {
    const dir     = __dirname
    const entries = fs.readdirSync(dir, { withFileTypes: true })

    for (const entry of entries) {
      if (!entry.isDirectory()) continue
      const pluginDir    = path.join(dir, entry.name)
      const manifestPath = path.join(pluginDir, 'manifest.json')
      if (!fs.existsSync(manifestPath)) continue

      try {
        const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'))
        await this._register(manifest, pluginDir)
      } catch (err) {
        console.error(`[plugins] Erro ao carregar "${entry.name}":`, err.message)
      }
    }
  }

  // Registra um plugin: garante linha na tabela e ativa se estava ativo
  async _register(manifest, pluginDir) {
    const { id, name, version, active_by_default = false } = manifest

    // Garante registro na tabela (upsert)
    await execute(
      `INSERT INTO plugins (id, active) VALUES ($1, $2)
       ON CONFLICT (id) DO NOTHING`,
      [id, active_by_default ? 1 : 0]
    )

    const record   = await queryOne('SELECT active FROM plugins WHERE id = $1', [id])
    const instance = require(path.join(pluginDir, 'index.js'))

    this.plugins.set(id, { manifest, instance, active: false })

    if (record && record.active) {
      await this.activate(id)
    }

    const status = (record && record.active) ? '✓ ativo' : '○ inativo'
    console.log(`[plugins] ${name} v${version} — ${status}`)
  }

  // Ativa um plugin
  async activate(id) {
    const plugin = this.plugins.get(id)
    if (!plugin) throw new Error(`Plugin "${id}" não encontrado`)
    if (plugin.active) return

    const ctx = this._buildContext(id)
    await plugin.instance.activate(ctx)
    plugin.active = true
    await execute('UPDATE plugins SET active = 1 WHERE id = $1', [id])
  }

  // Desativa um plugin
  async deactivate(id) {
    const plugin = this.plugins.get(id)
    if (!plugin) throw new Error(`Plugin "${id}" não encontrado`)
    if (!plugin.active) return

    const ctx = this._buildContext(id)
    if (typeof plugin.instance.deactivate === 'function') {
      await plugin.instance.deactivate(ctx)
    }
    plugin.active = false
    await execute('UPDATE plugins SET active = 0 WHERE id = $1', [id])
  }

  // Contexto injetado nos plugins durante activate/deactivate
  _buildContext(id) {
    return {
      app:        this.app,
      db:         this.db,
      httpServer: this.httpServer,    // http.Server nativo (pode ser null)
      config:     new PluginConfigStore(id),
      logger: {
        info:  (msg) => console.log(`[${id}] ${msg}`),
        warn:  (msg) => console.warn(`[${id}] WARN: ${msg}`),
        error: (msg) => console.error(`[${id}] ERR: ${msg}`),
      },
    }
  }

  // Lista todos os plugins
  async list() {
    const records = await query('SELECT id, active, installed_at FROM plugins')
    return records.map((r) => {
      const p = this.plugins.get(r.id)
      return {
        id:            r.id,
        active:        !!r.active,
        installed_at:  r.installed_at,
        name:          p?.manifest?.name         ?? r.id,
        version:       p?.manifest?.version      ?? '?',
        description:   p?.manifest?.description  ?? '',
        config_schema: p?.manifest?.config_schema ?? [],
        features:      p?.manifest?.features      ?? [],
        ui_path:       p?.manifest?.ui_path       ?? null,
      }
    })
  }

  // Retorna configuração pública de um plugin
  async getConfig(id) {
    const store = new PluginConfigStore(id)
    return store.getAll()
  }

  // Salva múltiplos pares key/value de uma vez
  async setConfig(id, values) {
    const store = new PluginConfigStore(id)
    for (const [k, v] of Object.entries(values)) {
      await store.set(k, v)
    }
  }
}

module.exports = PluginManager
