/**
 * QR Rápido MCP Server — HTTP/SSE transport (para n8n cloud e clientes remotos)
 *
 * Variáveis de ambiente:
 *   QR_BASE_URL   — URL da API (default: https://qrrapido.site)
 *   PORT          — porta HTTP (default: 3000)
 *   ALLOWED_KEYS  — lista de API keys válidas separadas por vírgula (opcional; vazio = delega à API)
 *
 * Cada request deve enviar o header:   X-API-Key: qr_xxx
 * O server valida a key chamando a API antes de aceitar a sessão MCP.
 *
 * Endpoint n8n: https://mcp.qrrapido.site/mcp  (ou porta 3000 internamente)
 */

import express from "express";
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { ListToolsRequestSchema, CallToolRequestSchema } from "@modelcontextprotocol/sdk/types.js";
import { fetch } from "undici";
import { writeFileSync } from "fs";
import { tmpdir } from "os";
import { join } from "path";

const BASE_URL = process.env.QR_BASE_URL || "https://qrrapido.site";
const PORT     = parseInt(process.env.PORT || "3000", 10);

// ── PIX EMV builder ───────────────────────────────────────────────────────────

function pixField(id, value) {
  return `${id}${String(value.length).padStart(2, "0")}${value}`;
}

function removeAccents(text) {
  return text.toUpperCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^A-Z0-9 ]/g, " ");
}

function crc16(data) {
  let crc = 0xFFFF;
  for (const ch of data) {
    crc ^= ch.charCodeAt(0) << 8;
    for (let i = 0; i < 8; i++)
      crc = (crc & 0x8000) ? ((crc << 1) ^ 0x1021) & 0xFFFF : (crc << 1) & 0xFFFF;
  }
  return crc.toString(16).toUpperCase().padStart(4, "0");
}

function buildPixPayload({ pixKey, amount, merchantName, merchantCity, txId = "***" }) {
  const merchantInfo = pixField("00", "br.gov.bcb.pix") + pixField("01", pixKey);
  const name = removeAccents(merchantName).substring(0, 25);
  const city = removeAccents(merchantCity).substring(0, 15);
  let payload = pixField("00", "01")
    + pixField("26", merchantInfo)
    + pixField("52", "0000")
    + pixField("53", "986");
  if (amount && amount > 0) payload += pixField("54", amount.toFixed(2));
  payload += pixField("58", "BR")
    + pixField("59", name)
    + pixField("60", city)
    + pixField("62", pixField("05", txId.substring(0, 25)))
    + "6304";
  return payload + crc16(payload);
}

// ── API helpers ───────────────────────────────────────────────────────────────

async function callApi(apiKey, body) {
  const res = await fetch(`${BASE_URL}/api/v1/QRManager/generate`, {
    method: "POST",
    headers: { "X-API-Key": apiKey, "Content-Type": "application/json" },
    body: JSON.stringify(body)
  });
  return res.json();
}

async function validateKey(apiKey) {
  // Basic format check — real auth happens on every tool call via the API
  if (!apiKey || !apiKey.startsWith("qr_") || apiKey.length < 20) return false;
  return true;
}

function errorContent(msg) {
  return { content: [{ type: "text", text: `❌ ${msg}` }], isError: true };
}

// ── MCP server factory (uma instância por sessão/request) ─────────────────────

function createMcpServer(apiKey) {
  const server = new Server(
    { name: "qrrapido", version: "0.2.0" },
    { capabilities: { tools: {} } }
  );

  server.setRequestHandler(ListToolsRequestSchema, async () => ({
    tools: [
      {
        name: "generate_qr",
        description: "Gera QR code para URL, Wi-Fi, vCard, WhatsApp, Email, SMS ou texto. Para PIX use generate_pix_qr.",
        inputSchema: {
          type: "object",
          properties: {
            type: { type: "string", enum: ["url", "wifi", "vcard", "whatsapp", "email", "sms", "texto"] },
            content: { type: "string" },
            format: { type: "string", enum: ["png", "webp", "svg"], default: "png" },
            size: { type: "number", default: 400 },
            primaryColor: { type: "string", default: "#000000" },
            backgroundColor: { type: "string", default: "#FFFFFF" }
          },
          required: ["type", "content"]
        }
      },
      {
        name: "generate_pix_qr",
        description: "Gera QR code PIX (BRCode/EMV) com payload completo para recebimento.",
        inputSchema: {
          type: "object",
          properties: {
            pixKey: { type: "string", description: "Chave PIX: CPF, email, telefone ou aleatória" },
            amount: { type: "number", description: "Valor em reais (omita para valor aberto)" },
            merchantName: { type: "string", description: "Nome do recebedor (max 25 chars)" },
            merchantCity: { type: "string", description: "Cidade (max 15 chars)" },
            txId: { type: "string", default: "***" },
            size: { type: "number", default: 400 },
            primaryColor: { type: "string", default: "#000000" },
            backgroundColor: { type: "string", default: "#FFFFFF" }
          },
          required: ["pixKey", "merchantName", "merchantCity"]
        }
      }
    ]
  }));

  server.setRequestHandler(CallToolRequestSchema, async (req) => {
    const { name } = req.params;
    const args = req.params.arguments ?? {};

    if (name === "generate_pix_qr") {
      const { pixKey, amount, merchantName, merchantCity, txId = "***",
              size = 400, primaryColor = "#000000", backgroundColor = "#FFFFFF" } = args;
      if (!pixKey || !merchantName || !merchantCity)
        return errorContent("pixKey, merchantName e merchantCity são obrigatórios.");
      let payload;
      try { payload = buildPixPayload({ pixKey, amount, merchantName, merchantCity, txId }); }
      catch (e) { return errorContent(`Erro payload PIX: ${e.message}`); }
      let data;
      try { data = await callApi(apiKey, { type: "texto", content: payload, size, primaryColor, backgroundColor }); }
      catch (e) { return errorContent(`Falha na requisição: ${e.message}`); }
      if (!data.success) return errorContent(data.message || "Erro desconhecido");
      return {
        content: [
          { type: "image", data: data.qrCodeBase64, mimeType: data.mimeType || "image/png" },
          { type: "text", text: `✅ PIX gerado\n🔑 ${pixKey}\n💰 ${amount ? `R$ ${amount.toFixed(2)}` : "aberto"}\n👤 ${merchantName} — ${merchantCity}\n⏱ ${data.generationTimeMs}ms` }
        ]
      };
    }

    if (name === "generate_qr") {
      const { type, content, format = "png", size = 400,
              primaryColor = "#000000", backgroundColor = "#FFFFFF" } = args;
      if (!type || !content) return errorContent("'type' e 'content' obrigatórios.");
      let data;
      try { data = await callApi(apiKey, { type, content, outputFormat: format, size, primaryColor, backgroundColor }); }
      catch (e) { return errorContent(`Falha: ${e.message}`); }
      if (!data.success) return errorContent(data.message || "Erro desconhecido");
      return {
        content: [
          { type: "image", data: data.qrCodeBase64, mimeType: data.mimeType || "image/png" },
          { type: "text", text: `✅ QR gerado (${type})\n⏱ ${data.generationTimeMs}ms\n💾 cache: ${data.fromCache ? "hit" : "miss"}` }
        ]
      };
    }

    return errorContent(`Tool desconhecida: ${name}`);
  });

  return server;
}

// ── Express app ───────────────────────────────────────────────────────────────

const app = express();
app.use(express.json());

// Health check (sem auth)
app.get("/health", (_, res) => res.json({ status: "ok", service: "qrrapido-mcp", baseUrl: BASE_URL }));

// MCP endpoint — cada POST cria sessão stateless
app.all("/mcp", async (req, res) => {
  const apiKey = req.headers["x-api-key"] || req.query.key;

  if (!apiKey) {
    res.status(401).json({ error: "X-API-Key header required" });
    return;
  }

  const valid = await validateKey(apiKey);
  if (!valid) {
    res.status(401).json({ error: "Invalid or revoked API key" });
    return;
  }

  const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
  const server = createMcpServer(apiKey);

  res.on("close", () => { transport.close(); server.close(); });

  try {
    await server.connect(transport);
    await transport.handleRequest(req, res, req.body);
  } catch (err) {
    if (!res.headersSent)
      res.status(500).json({ error: "Internal server error", detail: err.message });
  }
});

app.listen(PORT, () => {
  console.log(`[qrrapido-mcp-http] Servidor HTTP na porta ${PORT}`);
  console.log(`[qrrapido-mcp-http] Base URL: ${BASE_URL}`);
  console.log(`[qrrapido-mcp-http] Endpoint n8n: http://localhost:${PORT}/mcp`);
});
