const express = require('express');
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const crypto = require('crypto');

const app = express();

const PORT = Number(process.env.PORT || 3001);
const INTERNAL_TOKEN = process.env.INTERNAL_WEBHOOK_TOKEN || '';
const TEXT_WEBHOOK = process.env.N8N_TEXT_SUBMIT_WEBHOOK || '';
const SCENARIO_WEBHOOK = process.env.N8N_SCENARIO_WEBHOOK || '';
const ALLOWED_SCENARIOS = new Set(
  (process.env.ALLOWED_SCENARIOS || 'demo-1,demo-2')
    .split(',')
    .map(s => s.trim())
    .filter(Boolean)
);

app.use(helmet({ contentSecurityPolicy: false }));
app.use(express.json({ limit: '2mb' }));
app.use(rateLimit({
  windowMs: 60 * 1000,
  max: 60,
  standardHeaders: true,
  legacyHeaders: false
}));

function ok(res, data = {}, message = 'OK') {
  return res.json({ ok: true, data, message });
}

function fail(res, status, error, message, details = null) {
  return res.status(status).json({ ok: false, error, message, details });
}

async function postToN8N(url, payload) {
  if (!url) {
    throw new Error('Webhook URL is empty');
  }

  const response = await fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Internal-Token': INTERNAL_TOKEN
    },
    body: JSON.stringify(payload)
  });

  const raw = await response.text();

  let parsed;
  try {
    parsed = raw ? JSON.parse(raw) : {};
  } catch {
    parsed = { raw };
  }

  if (!response.ok) {
    const err = new Error(parsed.message || raw || `n8n returned ${response.status}`);
    err.status = response.status;
    err.payload = parsed;
    throw err;
  }

  return parsed;
}

app.get('/api/health', (req, res) => {
  return ok(res, {
    service: 'uno-bff',
    nowTs: Date.now()
  }, 'BFF is healthy');
});

app.post('/api/forms/text-submit', async (req, res) => {
  const text = typeof req.body?.text === 'string' ? req.body.text.trim() : '';

  if (!text) {
    return fail(res, 400, 'VALIDATION_ERROR', 'Поле text обязательно');
  }

  if (text.length > 500) {
    return fail(res, 400, 'VALIDATION_ERROR', 'Поле text должно быть не длиннее 500 символов');
  }

  const payload = {
    requestId: req.body?.requestId || crypto.randomUUID(),
    source: 'uno-click-site',
    text,
    ts: Date.now(),
    clientIp: req.headers['x-forwarded-for'] || req.socket.remoteAddress || '',
    userAgent: req.headers['user-agent'] || ''
  };

  try {
    const result = await postToN8N(TEXT_WEBHOOK, payload);
    return ok(res, result, 'Текст успешно отправлен');
  } catch (error) {
    console.error('text-submit error:', error);
    return fail(
      res,
      502,
      'N8N_ERROR',
      'Не удалось обработать запрос в n8n',
      error.payload || error.message
    );
  }
});

app.post('/api/actions/run-scenario', async (req, res) => {
  const scenarioId = typeof req.body?.scenarioId === 'string' ? req.body.scenarioId.trim() : '';
  const payload = (req.body?.payload && typeof req.body.payload === 'object' && !Array.isArray(req.body.payload))
    ? req.body.payload
    : {};

  if (!scenarioId) {
    return fail(res, 400, 'VALIDATION_ERROR', 'Поле scenarioId обязательно');
  }

  if (!ALLOWED_SCENARIOS.has(scenarioId)) {
    return fail(res, 400, 'VALIDATION_ERROR', `Сценарий ${scenarioId} не разрешен`);
  }

  const body = {
    requestId: req.body?.requestId || crypto.randomUUID(),
    source: 'uno-click-site',
    scenarioId,
    payload,
    ts: Date.now(),
    clientIp: req.headers['x-forwarded-for'] || req.socket.remoteAddress || '',
    userAgent: req.headers['user-agent'] || ''
  };

  try {
    const result = await postToN8N(SCENARIO_WEBHOOK, body);
    return ok(res, result, 'Сценарий успешно запущен');
  } catch (error) {
    console.error('run-scenario error:', error);
    return fail(
      res,
      502,
      'N8N_ERROR',
      'Не удалось запустить сценарий в n8n',
      error.payload || error.message
    );
  }
});

app.use((req, res) => {
  return fail(res, 404, 'NOT_FOUND', 'Маршрут не найден');
});

app.listen(PORT, '0.0.0.0', () => {
  console.log(`uno-bff listening on port ${PORT}`);
});
