/**
 * Rotas exclusivas do ADMIN (dono do SaaS).
 * Prefixo: /api/admin/
 */
import path from 'path'
import fs from 'fs/promises'
import { Router, type Request, type Response } from 'express'
import { z } from 'zod'
import bcrypt from 'bcryptjs'
import multer from 'multer'
import si from 'systeminformation'
import { prisma } from '../../infra/database/prisma'
import { dragonfly } from '../../infra/cache/dragonfly'
import { pluginsRouter } from '../plugins/plugins.routes'

const SETTINGS_KEY = 'system:settings'

const DEFAULT_SETTINGS = {
  systemName: 'NewWhats',
  timezone: 'America/Sao_Paulo',
  logoUrl: null as string | null,
  faviconUrl: null as string | null,
  accentColor: '#3b82f6',
  sounds: { newMessage: true, notification: true, connectionStatus: true },
  smtp: { host: '', port: 587, user: '', password: '', from: '', secure: false },
  emailTemplates: { welcome: '', passwordReset: '', trialExpiring: '' },
  registration: { mode: 'open' as 'open' | 'closed' | 'invite', defaultPlanId: null as string | null },
  maintenance: { enabled: false, message: 'Sistema em manutenção. Voltamos em breve.' },
  webhook: { url: '', secret: '' },
}

async function getSettings() {
  const stored = await dragonfly.getJson<typeof DEFAULT_SETTINGS>(SETTINGS_KEY)
  return { ...DEFAULT_SETTINGS, ...stored }
}

const uploadAsset = multer({
  storage: multer.memoryStorage(),
  limits: { fileSize: 2 * 1024 * 1024 }, // 2 MB
  fileFilter: (_req, file, cb) => {
    if (file.mimetype.startsWith('image/')) cb(null, true)
    else cb(new Error('Apenas imagens são permitidas'))
  },
})

export const adminRouter = Router()

// ── Plugins ───────────────────────────────────────────────────────────────────
adminRouter.use('/plugins', pluginsRouter)

// ── Plans ─────────────────────────────────────────────────────────────────────
adminRouter.get('/plans', async (_req: Request, res: Response) => {
  try {
    const plans = await prisma.plan.findMany({
      select: { id: true, name: true }
    })
    res.json(plans)
  } catch {
    res.status(500).json({ error: 'Erro ao listar planos' })
  }
})

// ── Settings ──────────────────────────────────────────────────────────────────
adminRouter.get('/settings', async (_req: Request, res: Response) => {
  try {
    const settings = await getSettings()
    // Nunca expõe a senha SMTP ao frontend
    const { smtp, ...rest } = settings
    res.json({ ...rest, smtp: { ...smtp, password: smtp.password ? '••••••••' : '' } })
  } catch {
    res.status(500).json({ error: 'Erro ao buscar configurações' })
  }
})

adminRouter.patch('/settings', async (req: Request, res: Response) => {
  try {
    const current = await getSettings()
    const body = req.body as Partial<typeof DEFAULT_SETTINGS>

    // Se o frontend devolver o placeholder de senha, mantém a senha atual
    if (body.smtp?.password === '••••••••') {
      body.smtp = { ...body.smtp, password: current.smtp.password }
    }

    const updated = { ...current, ...body }
    await dragonfly.setJson(SETTINGS_KEY, updated, 0) // TTL 0 = sem expiração
    res.json({ ok: true })
  } catch {
    res.status(500).json({ error: 'Erro ao salvar configurações' })
  }
})

// Upload de logo
adminRouter.post('/settings/logo', uploadAsset.single('file'), async (req: Request, res: Response) => {
  try {
    if (!req.file) { res.status(400).json({ error: 'Arquivo não enviado' }); return }
    const ext = req.file.originalname.split('.').pop() ?? 'png'
    const dest = path.resolve('./media/system/logo.' + ext)
    await fs.writeFile(dest, req.file.buffer)
    const url = `/media/system/logo.${ext}`
    const settings = await getSettings()
    await dragonfly.setJson(SETTINGS_KEY, { ...settings, logoUrl: url }, 0)
    res.json({ url })
  } catch {
    res.status(500).json({ error: 'Erro ao salvar logo' })
  }
})

// Upload de favicon
adminRouter.post('/settings/favicon', uploadAsset.single('file'), async (req: Request, res: Response) => {
  try {
    if (!req.file) { res.status(400).json({ error: 'Arquivo não enviado' }); return }
    const ext = req.file.originalname.split('.').pop() ?? 'ico'
    const dest = path.resolve('./media/system/favicon.' + ext)
    await fs.writeFile(dest, req.file.buffer)
    const url = `/media/system/favicon.${ext}`
    const settings = await getSettings()
    await dragonfly.setJson(SETTINGS_KEY, { ...settings, faviconUrl: url }, 0)
    res.json({ url })
  } catch {
    res.status(500).json({ error: 'Erro ao salvar favicon' })
  }
})

// Endpoint público para o frontend ler as settings de branding (sem autenticação)
// Usado pelo _app.tsx para aplicar nome/logo/favicon sem precisar do token
adminRouter.get('/settings/public', async (_req: Request, res: Response) => {
  try {
    const { systemName, logoUrl, faviconUrl, accentColor, maintenance } = await getSettings()
    res.json({ systemName, logoUrl, faviconUrl, accentColor, maintenance })
  } catch {
    res.status(500).json({ error: 'Erro' })
  }
})

// ── Métricas do sistema ───────────────────────────────────────────────────────
adminRouter.get('/metrics', async (_req: Request, res: Response) => {
  try {
    const [cpuLoad, mem, disk] = await Promise.all([
      si.currentLoad(),
      si.mem(),
      si.fsSize(),
    ])

    const rootDisk = disk.find((d) => d.mount === '/') ?? disk[0]

    const [connectedInstances, totalUsers] = await Promise.all([
      prisma.instance.count({ where: { status: 'CONNECTED' } }),
      prisma.user.count(),
    ])

    res.json({
      cpu: { percent: Math.round(cpuLoad.currentLoad) },
      ram: {
        total: mem.total,
        used: mem.used,
        percent: Math.round((mem.used / mem.total) * 100),
      },
      disk: {
        total: rootDisk?.size ?? 0,
        used: rootDisk?.used ?? 0,
        percent: Math.round(rootDisk?.use ?? 0),
      },
      uptime: Math.floor(process.uptime()),
      connectedInstances,
      totalUsers,
    })
  } catch {
    res.status(500).json({ error: 'Erro ao coletar métricas' })
  }
})

// Listar todos os tenants com presença online
adminRouter.get('/users', async (_req: Request, res: Response) => {
  try {
    const [users, presenceKeys] = await Promise.all([
      prisma.user.findMany({
        select: {
          id: true,
          name: true,
          email: true,
          role: true,
          isActive: true,
          planId: true,
          trialEndsAt: true,
          createdAt: true,
          _count: { select: { instances: true } },
        },
        orderBy: { createdAt: 'desc' },
      }),
      dragonfly.raw().keys('presence:user:*'),
    ])

    const onlineIds = new Set(presenceKeys.map((k) => k.replace('presence:user:', '')))

    res.json(users.map((u) => ({ ...u, online: onlineIds.has(u.id) })))
  } catch (err) {
    res.status(500).json({ error: 'Erro ao listar usuários' })
  }
})

// Detalhes de um tenant
adminRouter.get('/users/:id', async (req: Request, res: Response) => {
  try {
    const id = req.params['id'] as string
    const user = await prisma.user.findUnique({
      where: { id },
      select: {
        id: true,
        name: true,
        email: true,
        role: true,
        isActive: true,
        planId: true,
        trialEndsAt: true,
        createdAt: true,
        instances: { select: { id: true, name: true, status: true } },
        apiKeys: { select: { id: true, name: true, isActive: true, createdAt: true } },
      },
    })
    if (!user) {
      res.status(404).json({ error: 'Usuário não encontrado' })
      return
    }
    res.json(user)
  } catch (err) {
    res.status(500).json({ error: 'Erro ao buscar usuário' })
  }
})

// Ativar / desativar tenant
adminRouter.patch('/users/:id/status', async (req: Request, res: Response) => {
  try {
    const id = req.params['id'] as string
    const { isActive } = z.object({ isActive: z.boolean() }).parse(req.body)

    const user = await prisma.user.update({
      where: { id },
      data: { isActive },
      select: { id: true, name: true, email: true, isActive: true },
    })
    res.json(user)
  } catch (err) {
    if (err instanceof z.ZodError) {
      res.status(400).json({ error: 'isActive deve ser boolean' })
      return
    }
    res.status(500).json({ error: 'Erro ao atualizar status' })
  }
})

// Alterar plano do tenant
adminRouter.patch('/users/:id/plan', async (req: Request, res: Response) => {
  try {
    const id = req.params['id'] as string
    const { planId } = z.object({ planId: z.string().uuid() }).parse(req.body)

    const user = await prisma.user.update({
      where: { id },
      data: { planId },
      select: { id: true, name: true, planId: true },
    })
    res.json(user)
  } catch (err) {
    if (err instanceof z.ZodError) {
      res.status(400).json({ error: 'planId inválido' })
      return
    }
    res.status(500).json({ error: 'Erro ao atualizar plano' })
  }
})

// Estender trial de um tenant
adminRouter.patch('/users/:id/trial', async (req: Request, res: Response) => {
  try {
    const id = req.params['id'] as string
    const { days } = z.object({ days: z.number().int().min(1).max(365) }).parse(req.body)

    const user = await prisma.user.update({
      where: { id },
      data: {
        trialEndsAt: new Date(Date.now() + days * 24 * 60 * 60 * 1000),
      },
      select: { id: true, name: true, trialEndsAt: true },
    })
    res.json(user)
  } catch (err) {
    if (err instanceof z.ZodError) {
      res.status(400).json({ error: 'days inválido' })
      return
    }
    res.status(500).json({ error: 'Erro ao estender trial' })
  }
})

// Resetar senha de um tenant (admin)
adminRouter.patch('/users/:id/reset-password', async (req: Request, res: Response) => {
  try {
    const id = req.params['id'] as string
    const { newPassword } = z
      .object({ newPassword: z.string().min(8).max(128) })
      .parse(req.body)

    const passwordHash = await bcrypt.hash(newPassword, 12)
    await prisma.user.update({ where: { id }, data: { passwordHash } })

    res.json({ message: 'Senha redefinida com sucesso' })
  } catch (err) {
    if (err instanceof z.ZodError) {
      res.status(400).json({ error: 'newPassword inválido' })
      return
    }
    res.status(500).json({ error: 'Erro ao redefinir senha' })
  }
})

// Obter engine ativa
adminRouter.get('/engine', async (_req: Request, res: Response) => {
  try {
    const settings = await getSettings()
    const storedEngine = (settings as any).baileysEngine ?? process.env.BAILEYS_ENGINE ?? 'infinite'
    res.json({ engine: storedEngine })
  } catch (err) {
    console.error('[Admin] Erro ao obter engine:', err)
    res.status(500).json({ error: 'Erro ao obter a engine ativa' })
  }
})

// Altera a engine e reinicia o backend (via PM2 ou Docker)
adminRouter.post('/engine', async (req: Request, res: Response) => {
  try {
    const { engine } = z.object({ engine: z.enum(['infinite', 'official']) }).parse(req.body)

    // 1. Persiste nas configurações globais
    const settings = await getSettings()
    await dragonfly.setJson(SETTINGS_KEY, { ...settings, baileysEngine: engine }, 0)

    // 2. Escreve a alteração no arquivo .env para que persista no próximo boot
    const envPath = path.resolve('./.env')
    try {
      let content = await fs.readFile(envPath, 'utf-8')
      if (content.includes('BAILEYS_ENGINE=')) {
        content = content.replace(/BAILEYS_ENGINE=\w+/g, `BAILEYS_ENGINE=${engine}`)
      } else {
        content += `\nBAILEYS_ENGINE=${engine}\n`
      }
      await fs.writeFile(envPath, content, 'utf-8')
    } catch (envErr) {
      console.error('[Admin] Erro ao gravar BAILEYS_ENGINE no .env:', envErr)
    }

    // 3. Responde ao frontend antes de desligar/reiniciar
    res.json({ ok: true, engine, message: 'Engine configurada. Reiniciando o servidor...' })

    // 4. Executa o restart após uma breve janela para dar tempo do client receber a resposta HTTP 200 OK
    setTimeout(() => {
      console.info(`[Admin] Alternando engine para "${engine}". Reiniciando processo...`)
      const { exec } = require('child_process')
      exec('pm2 restart newwhats-backend', (err: any) => {
        if (err) {
          console.warn('[Admin] PM2 não disponível ou falhou ao reiniciar. Saindo com process.exit(0) para restart automático via Docker...')
          process.exit(0)
        }
      })
    }, 1500)

  } catch (err) {
    console.error('[Admin] Erro ao alternar engine:', err)
    if (err instanceof z.ZodError) {
      res.status(400).json({ error: 'Engine inválida. Use "infinite" ou "official"' })
      return
    }
    res.status(500).json({ error: 'Erro ao salvar a engine' })
  }
})

// ── Rota para Disparo Manual do Deploy ──
adminRouter.post('/deploys/trigger', async (_req: Request, res: Response) => {
  try {
    const response = await fetch('http://10.99.0.4:9000/hooks/clube67-deploy-hook', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ trigger: 'admin_panel' })
    })
    if (!response.ok) throw new Error(`Status ${response.status}`)
    res.json({ ok: true, message: 'Deploy automático iniciado na VPS 4.' })
  } catch (err: any) {
    console.error('[Admin] Falha ao disparar webhook de deploy:', err.message)
    res.status(502).json({ error: 'Não foi possível se conectar ao servidor de build' })
  }
})

