import 'dotenv/config'
import http from 'http'
import { existsSync } from 'fs'
import express from 'express'
import cors from 'cors'
import helmet from 'helmet'
import path from 'path'

import { env } from './config/env'
import { logger } from './config/logger'
import { prisma } from './infra/database/prisma'
// prisma re-usado inline abaixo (plan/status route)
import { dragonfly } from './infra/cache/dragonfly'
import { buildSocketServer } from './infra/socket/socketServer'
import { hookBus } from './core/hook-bus'
import { pluginConfig } from './core/plugin-config'
import { db, ensurePluginTables } from './core/db'
import { loadPlugins } from './core/plugin-loader'
import { pluginRegistry } from './core/plugin-registry'
import { WhatsAppConnectionManager } from './modules/whatsapp/connection/WhatsAppConnectionManager'
import { buildWhatsAppRoutes } from './modules/whatsapp/whatsapp.routes'
import { buildChatRoutes } from './modules/chats/chat.routes'
import { buildSendRoutes, buildAvatarRoute } from './modules/whatsapp/whatsapp.send.routes'
import { buildMediaRoutes } from './modules/whatsapp/whatsapp.media.routes'
import { buildChatbotRoutes } from './modules/chatbot/chatbot.routes'
import { buildContactRoutes } from './modules/contacts/contact.routes'
import { buildSectorRoutes } from './modules/sectors/sector.routes'
import { buildApiKeyRoutes } from './modules/api-keys/apikey.routes'
import { authRouter } from './modules/auth/auth.routes'
import { adminRouter } from './modules/auth/admin.routes'
import { pairRouter } from './modules/pair/pair.routes'
import { buildTemplateRoutes } from './modules/templates/template.routes'
import { buildScheduledRoutes } from './modules/scheduled/scheduled.routes'
import { buildStickerRoutes } from './modules/stickers/sticker.routes'
import { storageProvider } from './core/StorageProvider'
import { SchedulerService } from './modules/scheduled/scheduler.service'
import { authMiddleware } from './shared/middlewares/auth.middleware'
import { adminMiddleware } from './shared/middlewares/admin.middleware'
import { errorMiddleware } from './shared/middlewares/error.middleware'

async function bootstrap() {
  // ── Express ──────────────────────────────────────────────────────────────
  const app = express()

  app.use(helmet({ crossOriginResourcePolicy: { policy: 'cross-origin' } }))
  app.use(cors({ origin: env.FRONTEND_URL, credentials: true }))
  app.use(express.json({ limit: '10mb' }))

  // Servir arquivos de mídia baixados (ambos os prefixos: acesso direto e via proxy nginx)
  const mediaStatic = express.static(path.resolve('./media'), { maxAge: '7d' })
  app.use('/media', mediaStatic)
  app.use('/api/media', mediaStatic)

  // ── HTTP Server + Socket.IO ───────────────────────────────────────────────
  const httpServer = http.createServer(app)
  const io = buildSocketServer(httpServer)

  // ── WhatsApp Connection Manager ───────────────────────────────────────────
  const whatsAppManager = new WhatsAppConnectionManager(io)
  // Expõe para o plugin ext-api (sem circular dep)
  ;(globalThis as any).__whatsAppManager = whatsAppManager

  // ── Rotas ─────────────────────────────────────────────────────────────────
  app.use('/api/auth', authRouter)
  app.use('/api/pair', pairRouter)

  // Configurações públicas de branding (sem autenticação — usadas pelo frontend antes do login)
  app.get('/api/admin/settings/public', async (_req, res) => {
    try {
      const raw = await dragonfly.getJson<Record<string, unknown>>('system:settings')
      const defaults = { systemName: 'NewWhats', logoUrl: null, faviconUrl: null, accentColor: '#3b82f6', maintenance: { enabled: false, message: '' } }
      const cfg = { ...defaults, ...(raw ?? {}) }
      res.json({ systemName: cfg.systemName, logoUrl: cfg.logoUrl, faviconUrl: cfg.faviconUrl, accentColor: cfg.accentColor, maintenance: cfg.maintenance })
    } catch { res.status(500).json({ error: 'Erro' }) }
  })

  app.use('/api/admin', authMiddleware, adminMiddleware, adminRouter)
  app.use('/api/chats', authMiddleware, buildChatRoutes(whatsAppManager))
  app.use('/api/instances', authMiddleware, buildWhatsAppRoutes(whatsAppManager))
  app.use('/api/instances/:instanceId', authMiddleware, buildSendRoutes(whatsAppManager))
  app.use('/api/instances/:instanceId', authMiddleware, buildMediaRoutes(whatsAppManager))
  app.use('/api/chatbot', authMiddleware, buildChatbotRoutes())
  app.use('/api/contacts', authMiddleware, buildContactRoutes())
  app.use('/api/sectors', authMiddleware, buildSectorRoutes())
  app.use('/api/api-keys', authMiddleware, buildApiKeyRoutes())
  app.use('/api/inbox', authMiddleware, buildAvatarRoute(whatsAppManager))

  // ── Templates e Agendamentos ──────────────────────────────────────────────
  const scheduler = new SchedulerService(whatsAppManager)
  app.use('/api/templates', authMiddleware, buildTemplateRoutes())
  app.use('/api/scheduled', authMiddleware, buildScheduledRoutes(scheduler))
  app.use('/api/stickers',  authMiddleware, buildStickerRoutes())

  // Proxy genérico para arquivos no Wasabi — sem authMiddleware porque <img>/<audio>/<video>
  // do browser não enviam JWT. Paths são UUID-based (obscuros) — mesmo nível de segurança
  // que a rota /media/ estática que também não requer autenticação.
  app.get('/api/storage/view/*', async (req, res) => {
    const filePath = (req.params as any)[0] as string
    if (!filePath) { res.status(400).end(); return }
    try {
      const buffer = await storageProvider.getBuffer(filePath)
      if (!buffer) { res.status(404).end(); return }
      const ext = filePath.split('.').pop()?.toLowerCase()
      const mime =
        ext === 'webp' ? 'image/webp' :
        ext === 'jpg' || ext === 'jpeg' ? 'image/jpeg' :
        ext === 'png'  ? 'image/png'  :
        ext === 'mp4'  ? 'video/mp4'  :
        ext === 'ogg'  ? 'audio/ogg'  :
        'application/octet-stream'
      res.setHeader('Content-Type', mime)
      res.setHeader('Cache-Control', 'public, max-age=31536000, immutable')
      res.send(buffer)
    } catch {
      res.status(500).end()
    }
  })

  // Banner do frontend — dias restantes do plano
  app.get('/api/plan/status', authMiddleware, async (req, res) => {
    const user = await prisma.user.findUnique({
      where: { id: req.tenantId! },
      select: { trialEndsAt: true, plan: { select: { name: true } } },
    })
    const daysRemaining = user?.trialEndsAt
      ? Math.max(0, Math.ceil((new Date(user.trialEndsAt).getTime() - Date.now()) / 86_400_000))
      : null
    res.json({ daysRemaining, planName: user?.plan?.name ?? 'Trial' })
  })

  // Presença: retorna se o próprio usuário está online (chave ainda viva no Dragonfly)
  // Útil para self-check; para listar agentes online em admin, use a mesma lógica com prefixo
  app.get('/api/presence/me', authMiddleware, async (req, res) => {
    const online = await dragonfly.exists(`presence:user:${req.tenantId!}`)
    res.json({ online })
  })

  // Health check (sem autenticação)
  app.get('/health', (_req, res) => {
    res.json({ status: 'ok', timestamp: new Date().toISOString() })
  })

  // Retorna menu items de plugins ativos — usado pelo frontend após login
  app.get('/api/plugins/menu', authMiddleware, (_req, res) => {
    const items = pluginRegistry.getActiveMenuItems()
    res.json(items)
  })

  app.use(errorMiddleware)

  // ── Infra: conectar Dragonfly e Prisma ───────────────────────────────────
  await dragonfly.connect()
  await prisma.$connect()
  logger.info('PostgreSQL conectado via Prisma')

  // ── Plugin System ─────────────────────────────────────────────────────────
  await ensurePluginTables(db)

  const loadedPlugins = await loadPlugins()
  const pluginNames = loadedPlugins.map((p) => p.manifest.name)
  await pluginConfig.loadAll(pluginNames)

  const pluginCtx = {
    app,
    db,
    prisma,
    logger,
    hooks: hookBus,
    config: pluginConfig,
    io,
    httpServer,
  }

  pluginRegistry.setContext(pluginCtx)
  pluginRegistry.register(loadedPlugins)
  await pluginRegistry.activateAll()
  logger.info({ count: pluginNames.length }, '🔌 Plugins carregados')

  // ── Auto-reconectar instâncias ativas após reinício ──────────────────────
  // Reseta CONNECTING/QR_PENDING para DISCONNECTED antes de reconectar:
  // impede que a API retorne um status "fantasma" de antes do crash.
  await prisma.instance.updateMany({
    where: { status: { in: ['CONNECTING', 'QR_PENDING'] } },
    data: { status: 'DISCONNECTED' },
  })

  // Broadcasts travados em RUNNING (processo morreu durante o loop de envio):
  // marca como FAILED para não aparecerem eternamente como "Enviando…" no frontend.
  const stuckCount = await prisma.broadcast.updateMany({
    where: { status: 'RUNNING' },
    data: { status: 'FAILED', finishedAt: new Date() },
  })
  if (stuckCount.count > 0) {
    logger.warn({ count: stuckCount.count }, '[Broadcast] Jobs RUNNING marcados como FAILED após reinício')
  }

  // Reconecta toda instância não-banida que tenha credenciais persistidas
  // no disco (creds.json). O filesystem é a fonte de verdade para "essa
  // instância já foi pareada" — o status no DB pode estar DISCONNECTED
  // temporariamente porque o watchdog matou a conexão ANTES do timer de
  // reconexão rodar (ex: restart do tsx watch durante o backoff de 3s).
  // Sem esta lógica, a instância fica órfã até alguém chamar connect()
  // pela API, causando perda de mensagens durante o hiato.
  const candidates = await prisma.instance.findMany({
    where: { status: { notIn: ['BANNED'] } },
  })

  for (const inst of candidates) {
    const credsPath = path.join(env.BAILEYS_SESSIONS_PATH, inst.id, 'creds.json')
    if (!existsSync(credsPath)) {
      // Nunca foi pareada — pular pra não disparar QR indesejado no boot
      continue
    }
    logger.info(
      { instanceId: inst.id, previousStatus: inst.status },
      'Reconectando instância após reinício (creds persistidas)',
    )
    whatsAppManager.connect(inst.id, inst.tenantId).catch((err) =>
      logger.error({ err, instanceId: inst.id }, 'Falha ao reconectar instância'),
    )
  }

  // ── Scheduler de mensagens ────────────────────────────────────────────────
  scheduler.start()

  // ── Iniciar servidor ──────────────────────────────────────────────────────
  const port = parseInt(env.PORT, 10)
  httpServer.listen(port, () => {
    logger.info(`🚀 NewWhats backend rodando em http://localhost:${port}`)
    logger.info(`   Socket.IO pronto — frontend: ${env.FRONTEND_URL}`)
  })

  // ── Graceful shutdown ─────────────────────────────────────────────────────
  const shutdown = async (signal: string) => {
    logger.info({ signal }, 'Encerrando servidor...')
    scheduler.stop()
    httpServer.close()
    await prisma.$disconnect()
    process.exit(0)
  }

  process.on('SIGTERM', () => shutdown('SIGTERM'))
  process.on('SIGINT', () => shutdown('SIGINT'))
}

bootstrap().catch((err) => {
  console.error('Falha fatal ao iniciar servidor:', err)
  process.exit(1)
})
