'use strict';

const crypto = require('crypto');
const { google } = require('googleapis');
const { buildLetterText, PROVIDER_DATA } = require('../services/mailer');

const ALLOWED_PROVIDERS = new Set(Object.keys(PROVIDER_DATA));
const STATE_MAX_BYTES   = 4096;
const SALT = () => process.env.SALT || 'resetea-oauth-default-insecure';

/* ── Elimina CRLF y tabuladores — previene header injection en RFC 2822 ── */
function sanitizeHeader(str, maxLen = 300) {
  return String(str || '').replace(/[\r\n\t\x00-\x1F\x7F]/g, ' ').trim().slice(0, maxLen);
}

/* ── Firma HMAC-SHA256 del state — previene state forgery en OAuth callback ── */
function signState(payload) {
  const nonce = crypto.randomBytes(16).toString('hex');
  const data  = { ...payload, _nonce: nonce };
  const sig   = crypto.createHmac('sha256', SALT())
    .update(JSON.stringify(data))
    .digest('hex');
  return Buffer.from(JSON.stringify({ ...data, _sig: sig })).toString('base64url');
}

function verifyState(raw) {
  if (!raw || typeof raw !== 'string') throw new Error('State ausente');
  if (Buffer.byteLength(raw, 'utf8') > STATE_MAX_BYTES) throw new Error('State demasiado grande');

  let obj;
  try { obj = JSON.parse(Buffer.from(raw, 'base64url').toString('utf8')); }
  catch { throw new Error('State malformado'); }

  const { _sig, ...data } = obj;
  if (typeof _sig !== 'string' || _sig.length !== 64) throw new Error('Firma ausente o malformada');

  const expected    = crypto.createHmac('sha256', SALT()).update(JSON.stringify(data)).digest('hex');
  const sigBuf      = Buffer.from(_sig, 'hex');
  const expectedBuf = Buffer.from(expected, 'hex');

  if (!crypto.timingSafeEqual(sigBuf, expectedBuf)) throw new Error('Firma inválida');

  /* Devuelve payload sin los campos internos */
  const { _nonce, ...params } = data;
  return params;
}

function getOAuth2Client() {
  return new google.auth.OAuth2(
    process.env.GOOGLE_CLIENT_ID,
    process.env.GOOGLE_CLIENT_SECRET,
    process.env.GOOGLE_REDIRECT_URI || 'https://resetea.net/api/gmail/callback'
  );
}

/* ── GET /api/gmail/auth ─────────────────────────────────────────── */
exports.authInit = (req, res) => {
  const { provider, name, email, nickname, phone, address, extra, requestType } = req.query;

  if (!provider || !email || !name)
    return res.status(400).json({ error: 'Faltan parámetros obligatorios (provider, name, email).' });

  if (!ALLOWED_PROVIDERS.has(provider))
    return res.status(400).json({ error: 'Proveedor no soportado.' });

  /* Sanitizamos los campos que luego irán a cabeceras de email */
  const state = signState({
    provider,
    name:        sanitizeHeader(name, 200),
    email:       sanitizeHeader(email, 200),
    nickname:    sanitizeHeader(nickname,  100),
    phone:       sanitizeHeader(phone,      30),
    address:     sanitizeHeader(address,   300),
    extra:       sanitizeHeader(extra,     500),
    requestType: sanitizeHeader(requestType, 20),
  });

  const oauth2Client = getOAuth2Client();
  const authUrl = oauth2Client.generateAuthUrl({
    access_type: 'online',
    scope: ['https://www.googleapis.com/auth/gmail.send'],
    prompt: 'consent',
    state,
  });

  res.redirect(authUrl);
};

/* ── GET /api/gmail/callback ─────────────────────────────────────── */
exports.authCallback = async (req, res) => {
  const { code, state, error } = req.query;

  if (error) return res.redirect('/plantillas.html?oauth=cancelled');

  if (!code || !state)
    return res.status(400).send('Parámetros OAuth inválidos.');

  let params;
  try {
    params = verifyState(state);
  } catch (e) {
    console.warn('OAuth state rejection:', e.message);
    return res.status(400).send('State OAuth inválido o expirado.');
  }

  /* Validar provider contra whitelist (por si el state fue manipulado antes de que
     implementáramos la firma, o si la firma falla silenciosamente en el futuro) */
  if (!ALLOWED_PROVIDERS.has(params.provider)) {
    return res.status(400).send('Proveedor no soportado.');
  }

  try {
    const oauth2Client = getOAuth2Client();
    const { tokens }   = await oauth2Client.getToken(code);
    oauth2Client.setCredentials(tokens);

    const gmail        = google.gmail({ version: 'v1', auth: oauth2Client });
    const providerInfo = PROVIDER_DATA[params.provider];

    if (!providerInfo.email) {
      return res.redirect(
        `/plantillas.html?oauth=no_email&provider=${encodeURIComponent(params.provider)}` +
        `&formUrl=${encodeURIComponent(providerInfo.formUrl || '')}`
      );
    }

    const letterText = buildLetterText({
      providerInfo,
      senderName:    params.name,
      senderEmail:   params.email,
      senderNick:    params.nickname || '',
      senderPhone:   params.phone    || '',
      senderAddress: params.address  || '',
      extra:         params.extra    || '',
    });

    /* Cabeceras RFC 2822 — name y email ya vienen sanitizados desde authInit */
    const subject  = `Ejercicio derecho de supresión (RGPD Art. 17) — ${providerInfo.name}`;
    const rawEmail = [
      `From: ${params.name} <${params.email}>`,
      `To: ${providerInfo.email}`,
      `Subject: ${subject}`,
      `MIME-Version: 1.0`,
      `Content-Type: text/plain; charset=UTF-8`,
      '',
      letterText,
    ].join('\r\n');

    await gmail.users.messages.send({
      userId: 'me',
      requestBody: { raw: Buffer.from(rawEmail).toString('base64url') },
    });

    res.redirect(`/plantillas.html?oauth=ok&provider=${encodeURIComponent(providerInfo.name)}`);

  } catch (err) {
    console.error('Gmail OAuth error:', err.message);
    res.redirect('/plantillas.html?oauth=error');
  }
};
