import { Router, type Request, type Response } from 'express'
import fs from 'fs/promises'
import path from 'path'
import { InstanceRepository } from './repositories/InstanceRepository'
import { dragonfly } from '../../infra/cache/dragonfly'
import { prisma } from '../../infra/database/prisma'
import { logger } from '../../config/logger'
import type { WhatsAppConnectionManager } from './connection/WhatsAppConnectionManager'
import { storageProvider } from '../../core/StorageProvider'

const ENGINE_CHANGE_COOLDOWN_MS = 5 * 60 * 1000  // 5 minutos entre trocas

export function buildWhatsAppRoutes(manager: WhatsAppConnectionManager): Router {
  const router = Router()
  const repo = new InstanceRepository()

  // Listar instâncias do tenant
  router.get('/', async (req: Request, res: Response) => {
    const tenantId = req.tenantId!
    const instances = await repo.findAllByTenant(tenantId)
    res.json(instances)
  })

  // Criar instância
  router.post('/', async (req: Request, res: Response) => {
    const tenantId = req.tenantId!
    const { name } = req.body as { name: string }
    if (!name) {
      res.status(400).json({ error: 'name é obrigatório' })
      return
    }
    const instance = await repo.create(tenantId, name)
    res.status(201).json(instance)
  })

  // Conectar instância (emite QR via Socket.IO)
  router.post('/:id/connect', async (req: Request, res: Response) => {
    const tenantId = req.tenantId!
    const id = req.params['id'] as string

    const instance = await repo.findById(id)
    if (!instance || instance.tenantId !== tenantId) {
      res.status(404).json({ error: 'Instância não encontrada' })
      return
    }

    // Não aguarda — a conexão ocorre em background e o QR chega via Socket.IO
    manager.connect(id, tenantId).catch((err: unknown) => {
      console.error('[Connect] Falha ao iniciar conexão:', err)
    })

    res.json({ message: 'Conectando... aguarde o QR Code via WebSocket' })
  })

  // Desconectar instância
  router.post('/:id/disconnect', async (req: Request, res: Response) => {
    const tenantId = req.tenantId!
    const id = req.params['id'] as string

    const instance = await repo.findById(id)
    if (!instance || instance.tenantId !== tenantId) {
      res.status(404).json({ error: 'Instância não encontrada' })
      return
    }

    await manager.disconnect(id)
    res.json({ message: 'Desconectado' })
  })

  // QR Code atual (fallback REST — normalmente via Socket.IO)
  router.get('/:id/qr', async (req: Request, res: Response) => {
    const tenantId = req.tenantId!
    const id = req.params['id'] as string

    const instance = await repo.findById(id)
    if (!instance || instance.tenantId !== tenantId) {
      res.status(404).json({ error: 'Instância não encontrada' })
      return
    }

    const qrBase64 = await dragonfly.get(`instance:${id}:qr`)
    if (!qrBase64) {
      res.status(404).json({ error: 'QR Code não disponível. Inicie a conexão primeiro.' })
      return
    }

    res.json({ qrBase64 })
  })

  // Deletar instância
  router.delete('/:id', async (req: Request, res: Response) => {
    const tenantId = req.tenantId!
    const id = req.params['id'] as string

    const instance = await repo.findById(id)
    if (!instance || instance.tenantId !== tenantId) {
      res.status(404).json({ error: 'Instância não encontrada' })
      return
    }

    // 1. Desconectar e deslogar a sessão do WhatsApp se estiver ativa
    await manager.disconnect(id).catch(() => {})

    // 2. Garantir a exclusão física da pasta de credenciais da sessão no servidor
    await manager.deleteSessionFiles(id).catch(() => {})

    // 3. Garantir a exclusão física da pasta de mídia local ./media/{instanceId} no servidor
    const localMediaDir = path.resolve('./media', id)
    await fs.rm(localMediaDir, { recursive: true, force: true }).catch((err: any) => {
      console.error(`[deleteInstance] Erro ao deletar pasta local de mídia da instância ${id}:`, err)
    })

    // 4. Apagar do banco de dados
    await repo.delete(id)

    // 5. Deleta mídias associadas à instância no storage remoto (Wasabi/S3) de forma assíncrona
    storageProvider.deletePrefix(`whatsapp/${tenantId}/${id}/`).catch((err: any) => {
      console.error(`[StorageProvider] Erro ao deletar mídias da instância ${id}:`, err)
    })

    res.json({ message: 'Instância removida com sucesso de todos os storages e servidores' })
  })

  // Estatísticas de mensagens da instância (contagens do banco)
  router.get('/:id/stats', async (req: Request, res: Response) => {
    const tenantId = req.tenantId!
    const id = req.params['id'] as string

    const instance = await repo.findById(id)
    if (!instance || instance.tenantId !== tenantId) {
      res.status(404).json({ error: 'Instância não encontrada' })
      return
    }

    const [received, sent, media, chats] = await Promise.all([
      prisma.message.count({ where: { instanceId: id, fromMe: false } }),
      prisma.message.count({ where: { instanceId: id, fromMe: true } }),
      prisma.message.count({
        where: {
          instanceId: id,
          type: { in: ['IMAGE', 'VIDEO', 'AUDIO', 'DOCUMENT', 'STICKER'] },
        },
      }),
      prisma.chat.count({ where: { instanceId: id } }),
    ])

    res.json({ received, sent, media, chats })
  })

  // Trocar engine de uma instância (com cooldown + auditoria)
  router.patch('/:id/engine', async (req: Request, res: Response) => {
    const tenantId = req.tenantId!
    const id = req.params['id'] as string
    const { engine } = req.body as { engine?: string }

    if (engine !== 'infinite' && engine !== 'official') {
      res.status(400).json({ error: 'engine deve ser "infinite" ou "official"' })
      return
    }

    const instance = await repo.findById(id)
    if (!instance || instance.tenantId !== tenantId) {
      res.status(404).json({ error: 'Instância não encontrada' })
      return
    }

    // Cooldown: bloqueia nova troca por 5 min após a última
    const cooldownKey = `instance:${id}:engine_change_lock`
    const lockedAt = await dragonfly.get(cooldownKey) as string | null
    if (lockedAt) {
      const remaining = Math.ceil((ENGINE_CHANGE_COOLDOWN_MS - (Date.now() - Number(lockedAt))) / 1000)
      res.status(429).json({ error: `Aguarde ${remaining}s antes de trocar a engine novamente` })
      return
    }

    const fromEngine = instance.engine ?? 'infinite'
    if (fromEngine === engine) {
      res.status(200).json({ message: 'Engine já está definida para este valor', engine })
      return
    }

    // Persiste a troca
    await prisma.instance.update({ where: { id }, data: { engine } })

    // Audit log estruturado
    logger.info(
      { tenantId, instanceId: id, fromEngine, toEngine: engine },
      '[EngineChange] Troca de engine auditada',
    )

    // Aplica cooldown (5 min via TTL no Redis)
    await dragonfly.psetex(cooldownKey, ENGINE_CHANGE_COOLDOWN_MS, String(Date.now()))

    // Reconecta a instância com a nova engine
    const isConnected = manager.getSocket(id) != null
    if (isConnected) {
      await manager.disconnect(id).catch(() => {})
      manager.connect(id, tenantId).catch(() => {})
    }

    res.json({ message: 'Engine atualizada', engine, reconnecting: isConnected })
  })

  return router
}
