/**
 * services/emailTransport.js
 *
 * Pluggable email transport. The operator can pick any of:
 *   - resend     (Resend API — what we use by default)
 *   - smtp       (Direct SMTP — works with any mail server: Postfix,
 *                 iRedMail, Mailcow, Gmail with app password, etc.
 *                 No third party required.)
 *   - sendgrid   (SendGrid Web API)
 *   - mailgun    (Mailgun HTTP API)
 *   - postmark   (Postmark HTTP API)
 *   - file       (Write the .eml to disk — useful for local dev,
 *                 testing, or when the operator's mail server is
 *                 local-only)
 *
 * The active transport is stored in the `email_transport` table. The
 * `services/email.js` send() function asks this module which
 * transport to use at call time, so the operator can switch
 * transports without restarting the server.
 *
 * All transports implement the same interface:
 *   async send({ to, from, subject, text, html, replyTo }) → { id, provider, raw }
 *
 * The HTTP API services (resend, sendgrid, mailgun, postmark) need
 * their API key in the `config_json` of the active transport row.
 * The SMTP transport needs host + port + user + pass + tls.
 */

'use strict';

const fs = require('fs');
const path = require('path');
const nodemailer = require('nodemailer');

let _db = null;
function db() { if (!_db) _db = require('../config/db').db; return _db; }

const HTTP_TIMEOUT_MS = 20000;

/**
 * Returns the active transport config. If the table is empty, fall
 * back to the legacy `resend` behavior so the rest of the system
 * still works on first install.
 */
function getActive() {
  const row = db().prepare(`SELECT * FROM email_transport WHERE active = 1 ORDER BY updated_at DESC LIMIT 1`).get();
  if (row) {
    return {
      name: row.name,
      config: row.config_json ? JSON.parse(row.config_json) : {},
    };
  }
  // Legacy fallback: Resend via env var
  if (process.env.RESEND_API_KEY) {
    return { name: 'resend', config: { api_key: process.env.RESEND_API_KEY } };
  }
  // SMTP via env vars
  if (process.env.SMTP_HOST) {
    return { name: 'smtp', config: {
      host: process.env.SMTP_HOST, port: parseInt(process.env.SMTP_PORT, 10) || 587,
      user: process.env.SMTP_USER, pass: process.env.SMTP_PASS,
      secure: process.env.SMTP_SECURE === '1',
    }};
  }
  return null;
}

function listAll() { return db().prepare('SELECT * FROM email_transport ORDER BY name').all(); }
function setActive(name, config) {
  // Mark all others inactive
  db().prepare('UPDATE email_transport SET active = 0').run();
  const existing = db().prepare('SELECT id FROM email_transport WHERE name = ?').get(name);
  if (existing) {
    db().prepare(`UPDATE email_transport SET active = 1, config_json = ?, updated_at = ? WHERE id = ?`)
      .run(JSON.stringify(config), Date.now(), existing.id);
  } else {
    db().prepare(`INSERT INTO email_transport (name, active, config_json, updated_at) VALUES (?, 1, ?, ?)`)
      .run(name, JSON.stringify(config), Date.now());
  }
}

// ---- Transport implementations ----

async function sendResend({ to, from, subject, text, html, replyTo }, cfg) {
  const r = await fetch('https://api.resend.com/emails', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer ' + cfg.api_key,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ from, to, subject, text, html, reply_to: replyTo || undefined }),
    signal: AbortSignal.timeout(HTTP_TIMEOUT_MS),
  });
  if (!r.ok) throw new Error(`resend ${r.status}: ${(await r.text()).slice(0, 200)}`);
  const j = await r.json();
  return { id: j.id, provider: 'resend' };
}

async function sendSmtp({ to, from, subject, text, html, replyTo }, cfg) {
  const transport = nodemailer.createTransport({
    host: cfg.host,
    port: cfg.port || 587,
    secure: cfg.secure === true || cfg.secure === '1',
    auth: (cfg.user && cfg.pass) ? { user: cfg.user, pass: cfg.pass } : undefined,
    tls: cfg.tls ? cfg.tls : { rejectUnauthorized: true },
  });
  const info = await transport.sendMail({
    from, to, subject,
    text, html: html || undefined,
    replyTo: replyTo || undefined,
  });
  return { id: info.messageId, provider: 'smtp' };
}

async function sendSendgrid({ to, from, subject, text, html, replyTo }, cfg) {
  const r = await fetch('https://api.sendgrid.com/v3/mail/send', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer ' + cfg.api_key,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      personalizations: [{ to: [{ email: to }] }],
      from: { email: from },
      subject,
      content: [
        { type: 'text/plain', value: text },
        ...(html ? [{ type: 'text/html', value: html }] : []),
      ],
      reply_to: replyTo ? { email: replyTo } : undefined,
    }),
    signal: AbortSignal.timeout(HTTP_TIMEOUT_MS),
  });
  if (!r.ok) throw new Error(`sendgrid ${r.status}: ${(await r.text()).slice(0, 200)}`);
  return { id: r.headers.get('x-message-id') || 'sent', provider: 'sendgrid' };
}

async function sendMailgun({ to, from, subject, text, html, replyTo }, cfg) {
  const domain = cfg.domain;
  const auth = Buffer.from('api:' + cfg.api_key).toString('base64');
  const form = new URLSearchParams();
  form.set('from', from);
  form.set('to', to);
  form.set('subject', subject);
  form.set('text', text);
  if (html) form.set('html', html);
  if (replyTo) form.set('h:Reply-To', replyTo);
  const r = await fetch(`https://api.mailgun.net/v3/${domain}/messages`, {
    method: 'POST',
    headers: {
      'Authorization': 'Basic ' + auth,
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: form.toString(),
    signal: AbortSignal.timeout(HTTP_TIMEOUT_MS),
  });
  if (!r.ok) throw new Error(`mailgun ${r.status}: ${(await r.text()).slice(0, 200)}`);
  const j = await r.json();
  return { id: j.id, provider: 'mailgun' };
}

async function sendPostmark({ to, from, subject, text, html, replyTo }, cfg) {
  const r = await fetch('https://api.postmarkapp.com/email', {
    method: 'POST',
    headers: {
      'X-Postmark-Server-Token': cfg.api_key,
      'Content-Type': 'application/json',
      'Accept': 'application/json',
    },
    body: JSON.stringify({
      From: from, To: to, Subject: subject,
      TextBody: text, HtmlBody: html || undefined,
      ReplyTo: replyTo || undefined,
    }),
    signal: AbortSignal.timeout(HTTP_TIMEOUT_MS),
  });
  if (!r.ok) throw new Error(`postmark ${r.status}: ${(await r.text()).slice(0, 200)}`);
  const j = await r.json();
  return { id: j.MessageID, provider: 'postmark' };
}

async function sendFile({ to, from, subject, text }, cfg) {
  // Useful for local dev: write a .eml to ./data/outbox/<id>.eml
  const outbox = path.join(__dirname, '..', 'data', 'outbox');
  fs.mkdirSync(outbox, { recursive: true });
  const id = Date.now() + '-' + Math.random().toString(36).slice(2, 8);
  const lines = [
    `From: ${from}`,
    `To: ${to}`,
    `Subject: ${subject}`,
    `Date: ${new Date().toUTCString()}`,
    `Content-Type: text/plain; charset=utf-8`,
    '',
    text,
  ];
  const filename = path.join(outbox, id + '.eml');
  fs.writeFileSync(filename, lines.join('\r\n'));
  return { id, provider: 'file', raw: { path: filename } };
}

const TRANSPORTS = {
  resend:   sendResend,
  smtp:     sendSmtp,
  sendgrid: sendSendgrid,
  mailgun:  sendMailgun,
  postmark: sendPostmark,
  file:     sendFile,
};

async function send(opts) {
  const active = getActive();
  if (!active) throw new Error('NO_EMAIL_TRANSPORT_CONFIGURED');
  const fn = TRANSPORTS[active.name];
  if (!fn) throw new Error(`UNKNOWN_EMAIL_TRANSPORT: ${active.name}`);
  return fn(opts, active.config);
}

module.exports = { send, listAll, setActive, getActive, TRANSPORTS };
