'use strict'

/**
 * Proxy para a External API do motor NewWhats.
 *
 * Encaminha chamadas do satélite (Alemão) para o motor:
 *   REST:  GET|POST|... /api/nw/v1/*  →  {motorUrl}/api/ext/v1/*
 *   WS:    UPGRADE /api/nw/v1/stream  →  {motorUrl}/api/ext/v1/stream
 *
 * A integration_key é lida da tabela plugin_configs e injectada automaticamente
 * como header x-nw-key em todas as chamadas — nunca fica exposta ao browser.
 */

const http  = require('http')
const https = require('https')
const net   = require('net')
const url   = require('url')
const { queryOne } = require('../../database/postgres')

// ─── Helpers ──────────────────────────────────────────────────────────────────

async function getPluginConfig() {
  const [keyRow, urlRow] = await Promise.all([
    queryOne("SELECT value FROM plugin_configs WHERE plugin_id = 'newwhats' AND key = 'integration_key'"),
    queryOne("SELECT value FROM plugin_configs WHERE plugin_id = 'newwhats' AND key = 'newwhats_url'"),
  ])
  return {
    integrationKey: keyRow?.value  || null,
    motorUrl:       urlRow?.value?.replace(/\/$/, '') || null,
  }
}

function notConfigured(res) {
  res.status(503).json({ error: 'Plugin NewWhats não configurado. Configure a integration_key e newwhats_url no admin.' })
}

// ─── REST proxy ───────────────────────────────────────────────────────────────

/**
 * Express middleware que faz proxy de /api/nw/v1/* → motor /api/ext/v1/*
 * Preserva method, path, query string, body e headers (menos host).
 */
function createRestProxy() {
  return async function extApiProxy(req, res) {
    const { integrationKey, motorUrl } = await getPluginConfig()
    if (!integrationKey || !motorUrl) return notConfigured(res)

    const target  = url.parse(motorUrl)
    const isHttps = target.protocol === 'https:'
    const lib     = isHttps ? https : http

    // Remove /api/nw/v1 do prefix e substitui por /api/ext/v1
    const suffix  = req.path.replace(/^\/api\/nw\/v1/, '')
    const qs      = req.originalUrl.split('?')[1]
    const fwdPath = `/api/ext/v1${suffix}${qs ? '?' + qs : ''}`

    const options = {
      hostname: target.hostname,
      port:     target.port || (isHttps ? 443 : 80),
      path:     fwdPath,
      method:   req.method,
      headers: {
        ...req.headers,
        host:        target.hostname,
        'x-nw-key':  integrationKey,
        // Remove headers que causam problemas no proxy
        connection:  'close',
      },
    }

    const proxyReq = lib.request(options, (proxyRes) => {
      const status = proxyRes.statusCode || 502

      // Se o motor retornar um redirect (3xx), ele pode incluir um header
      // Location apontando para sua própria porta (ex: :8008/api/auth/login).
      // O browser seguiria esse redirect diretamente para o motor — o que
      // causa ERR_CONNECTION_REFUSED porque a porta não está exposta.
      // Interceptamos aqui e devolvemos 401 para o browser tratar.
      if (status >= 300 && status < 400) {
        proxyRes.resume() // descarta o body
        return res.status(401).json({ error: 'Motor NewWhats requer autenticação. Verifique a integration_key no painel de Plugins.' })
      }

      res.status(status)
      Object.entries(proxyRes.headers).forEach(([k, v]) => {
        // Nunca repassa Location ou Set-Cookie do motor — evita vazamento de URL interna
        if (k !== 'transfer-encoding' && k !== 'location' && k !== 'set-cookie') {
          res.setHeader(k, v)
        }
      })
      proxyRes.pipe(res)
    })

    proxyReq.on('error', (err) => {
      if (!res.headersSent) {
        res.status(502).json({ error: `Motor indisponível: ${err.message}` })
      }
    })

    // Forward body para POST/PUT/PATCH
    if (['POST', 'PUT', 'PATCH'].includes(req.method)) {
      const body = JSON.stringify(req.body)
      proxyReq.setHeader('content-length', Buffer.byteLength(body))
      proxyReq.setHeader('content-type', 'application/json')
      proxyReq.write(body)
    }

    proxyReq.end()
  }
}

// ─── WS proxy (TCP tunnel) ────────────────────────────────────────────────────

/**
 * Adiciona handler de upgrade ao httpServer para fazer tunnel do WS.
 *
 * Fluxo:
 *   Browser → UPGRADE /api/nw/v1/stream
 *   Satélite → abre TCP para motor + injeta x-nw-key no upgrade
 *   Tunnel bidirecional socket ↔ socket (zero overhead no runtime)
 */
function attachWsProxy(httpServer) {
  httpServer.on('upgrade', async (req, clientSocket, head) => {
    if (!req.url.startsWith('/api/nw/v1/stream')) return

    const { integrationKey, motorUrl } = await getPluginConfig()
    if (!integrationKey || !motorUrl) {
      clientSocket.write('HTTP/1.1 503 Service Unavailable\r\n\r\n')
      clientSocket.destroy()
      return
    }

    const target  = url.parse(motorUrl)
    const port    = parseInt(String(target.port || 80), 10)
    const host    = target.hostname

    const proxySocket = net.connect(port, host, () => {
      // Reescreve o upgrade request com o path correto + x-nw-key
      const headers = Object.entries(req.headers)
        .filter(([k]) => k !== 'host' && k !== 'x-nw-key')
        .map(([k, v]) => `${k}: ${v}`)
        .join('\r\n')

      proxySocket.write(
        `${req.method} /api/ext/v1/stream HTTP/1.1\r\n` +
        `Host: ${host}\r\n` +
        `x-nw-key: ${integrationKey}\r\n` +
        `${headers}\r\n` +
        `\r\n`
      )

      if (head && head.length) proxySocket.write(head)

      proxySocket.pipe(clientSocket)
      clientSocket.pipe(proxySocket)
    })

    proxySocket.on('error', (err) => {
      console.error('[nw-proxy] WS tunnel error:', err.message)
      clientSocket.destroy()
    })

    clientSocket.on('error', () => proxySocket.destroy())
    clientSocket.on('close', () => proxySocket.destroy())
    proxySocket.on('close', () => clientSocket.destroy())
  })
}

module.exports = { createRestProxy, attachWsProxy }
