const nodemailer = require('nodemailer');
const { getDb, getSetting } = require('../db/database');
const {
  markNotificationError,
  markNotificationSuccess,
  markNotificationTestSuccess,
} = require('./statusRuntime');

// ── SMTP transport ────────────────────────────────────────────────────────────

function createTransport() {
  const host       = getSetting('notify_smtp_host');
  const port       = parseInt(getSetting('notify_smtp_port') || '587', 10);
  const encryption = getSetting('notify_smtp_encryption') || 'starttls';
  const username   = getSetting('notify_smtp_username');
  const password   = getSetting('notify_smtp_password');
  const selfSigned = getSetting('notify_smtp_self_signed') === 'true';

  if (!host) throw new Error('SMTP host is not configured');

  return nodemailer.createTransport({
    host,
    port,
    secure: encryption === 'ssl',
    auth: username ? { user: username, pass: password } : undefined,
    tls: {
      rejectUnauthorized: !selfSigned,
      ...(encryption === 'none' ? { ignoreTLS: true } : {}),
    },
  });
}

function senderAddress() {
  const name    = getSetting('notify_sender_name') || 'Bill Tracker';
  const address = getSetting('notify_sender_address');
  if (!address) throw new Error('Sender address is not configured');
  return `"${name}" <${address}>`;
}

// ── Email templates ───────────────────────────────────────────────────────────

const TYPE_META = {
  due_3d:    { subject: (b) => `Reminder: ${b.name} due in 3 days`,      urgency: 'upcoming' },
  due_1d:    { subject: (b) => `Reminder: ${b.name} due tomorrow`,        urgency: 'soon'     },
  due_today: { subject: (b) => `Due today: ${b.name}`,                    urgency: 'today'    },
  overdue:   { subject: (b) => `Overdue: ${b.name} hasn't been paid`,     urgency: 'overdue'  },
};

const URGENCY_COLOR = {
  upcoming: '#4f46e5',
  soon:     '#d97706',
  today:    '#dc7308',
  overdue:  '#dc2626',
};

function buildEmailHtml(bill, type, dueDate) {
  const meta   = TYPE_META[type];
  const color  = URGENCY_COLOR[meta.urgency];
  const amount = '$' + Number(bill.expected_amount || 0).toFixed(2);
  const fmt    = (d) => {
    if (!d) return '—';
    const [y, m, day] = d.split('-');
    return `${parseInt(m)}/${parseInt(day)}/${y}`;
  };

  const messages = {
    due_3d:    `<strong>${bill.name}</strong> is due in 3 days.`,
    due_1d:    `<strong>${bill.name}</strong> is due <strong>tomorrow</strong>.`,
    due_today: `<strong>${bill.name}</strong> is due <strong>today</strong>.`,
    overdue:   `<strong>${bill.name}</strong> was due on ${fmt(dueDate)} and has not been marked as paid.`,
  };

  return `<!DOCTYPE html>
<html>
<body style="margin:0;padding:0;background:#f5f6fa;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;">
<table width="100%" cellpadding="0" cellspacing="0" style="padding:32px 16px;">
  <tr><td align="center">
    <table width="540" cellpadding="0" cellspacing="0" style="background:#fff;border-radius:8px;border:1px solid #e2e4eb;overflow:hidden;">
      <tr>
        <td style="background:${color};padding:16px 24px;">
          <span style="color:white;font-size:18px;font-weight:700;">Bill Tracker</span>
        </td>
      </tr>
      <tr>
        <td style="padding:28px 24px;">
          <p style="margin:0 0 20px;font-size:15px;color:#1a1d2e;line-height:1.6;">
            ${messages[type]}
          </p>
          <table width="100%" cellpadding="0" cellspacing="0"
            style="border:1px solid #e2e4eb;border-radius:6px;overflow:hidden;font-size:14px;">
            <tr style="background:#f5f6fa;">
              <td style="padding:10px 14px;color:#6b7280;font-weight:600;border-bottom:1px solid #e2e4eb;">Bill</td>
              <td style="padding:10px 14px;border-bottom:1px solid #e2e4eb;">${esc(bill.name)}</td>
            </tr>
            <tr>
              <td style="padding:10px 14px;color:#6b7280;font-weight:600;border-bottom:1px solid #e2e4eb;">Due Date</td>
              <td style="padding:10px 14px;border-bottom:1px solid #e2e4eb;">${fmt(dueDate)}</td>
            </tr>
            <tr style="background:#f5f6fa;">
              <td style="padding:10px 14px;color:#6b7280;font-weight:600;">Amount</td>
              <td style="padding:10px 14px;font-weight:700;">${amount}</td>
            </tr>
          </table>
          <p style="margin:24px 0 0;font-size:12px;color:#9ca3af;">
            Sent by Bill Tracker &middot; ${new Date().toLocaleDateString()}
          </p>
        </td>
      </tr>
    </table>
  </td></tr>
</table>
</body>
</html>`;
}

function esc(s) {
  return String(s || '').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}

// ── Send ──────────────────────────────────────────────────────────────────────

async function sendEmail(to, subject, html) {
  const transport = createTransport();
  await transport.sendMail({ from: senderAddress(), to, subject, html });
}

async function sendTestEmail(to) {
  const html = `<!DOCTYPE html>
<html><body style="font-family:sans-serif;padding:32px;">
  <h2 style="color:#4f46e5;">Bill Tracker — Test Email</h2>
  <p>Your SMTP configuration is working correctly.</p>
  <p style="color:#6b7280;font-size:12px;">Sent at ${new Date().toISOString()}</p>
</body></html>`;
  try {
    await sendEmail(to, 'Bill Tracker — Test Email', html);
    markNotificationTestSuccess();
  } catch (err) {
    markNotificationError(err);
    throw err;
  }
}

// ── Notification tracking ─────────────────────────────────────────────────────

function hasNotification(db, billId, userId, year, month, type, date) {
  return !!db.prepare(`
    SELECT 1 FROM notifications
    WHERE bill_id=? AND user_id=? AND year=? AND month=? AND type=? AND sent_date=?
  `).get(billId, userId, year, month, type, date);
}

function recordNotification(db, billId, userId, year, month, type, date) {
  try {
    db.prepare(`
      INSERT INTO notifications (bill_id, user_id, year, month, type, sent_date)
      VALUES (?, ?, ?, ?, ?, ?)
    `).run(billId, userId, year, month, type, date);
  } catch {
    // Unique constraint: already recorded, ignore
  }
}

// ── Main notification runner (called by daily worker) ─────────────────────────

async function runNotifications() {
  const db = getDb();

  if (getSetting('notify_smtp_enabled') !== 'true') return;
  if (!getSetting('notify_smtp_host'))               return;
  if (!getSetting('notify_sender_address'))          return;

  const now   = new Date();
  const year  = now.getFullYear();
  const month = now.getMonth() + 1;
  const today = now.toISOString().slice(0, 10);

  const { getCycleRange, resolveDueDate } = require('./statusService');
  const { start, end } = getCycleRange(year, month);

  // Fetch all active bills. In global-notification mode, the single global recipient
  // legitimately receives every bill. In per-user mode, each recipient must only
  // see their own bills — the ownership filter is applied in the loop below.
  const bills = db.prepare('SELECT * FROM bills WHERE active = 1 AND deleted_at IS NULL').all();
  const allowUserConfig = getSetting('notify_allow_user_config') === 'true';
  const globalRecipient = getSetting('notify_global_recipient');

  // Gather recipient list
  const recipients = [];

  if (allowUserConfig) {
    const users = db.prepare(
      "SELECT * FROM users WHERE active = 1 AND role='user' AND notifications_enabled=1 AND notification_email IS NOT NULL AND notification_email != ''"
    ).all();
    recipients.push(...users);
  } else if (globalRecipient) {
    // Treat global recipient as a synthetic user-like object
    recipients.push({
      id: 0, notification_email: globalRecipient,
      notify_3d: 1, notify_1d: 1, notify_due: 1, notify_overdue: 1,
    });
  }

  if (!recipients.length) return;

  const errors = [];

  for (const bill of bills) {
    const payments = db.prepare(
      'SELECT * FROM payments WHERE bill_id = ? AND paid_date BETWEEN ? AND ? AND deleted_at IS NULL'
    ).all(bill.id, start, end);

    const totalPaid = payments.reduce((s, p) => s + p.amount, 0);
    const isPaid    = totalPaid >= bill.expected_amount;
    if (isPaid) continue;

    const dueDate = resolveDueDate(bill, year, month);
    const due     = new Date(dueDate + 'T00:00:00');
    // Compare calendar days, not timestamps, to avoid same-day bugs
    // (e.g., due today at midnight vs now at 3pm would give -0.625 days → floors to -1)
    const todayDate = new Date(now.getFullYear(), now.getMonth(), now.getDate());
    const dueDay    = new Date(due.getFullYear(), due.getMonth(), due.getDate());
    const diffDays  = Math.round((dueDay - todayDate) / 86400000);

    // Determine which type applies today
    let type = null;
    if      (diffDays === 3)  type = 'due_3d';
    else if (diffDays === 1)  type = 'due_1d';
    else if (diffDays === 0)  type = 'due_today';
    else if (diffDays < 0)    type = 'overdue';

    if (!type) continue;

    // Defensive: warn if a bill somehow has no owner
    if (!bill.user_id) {
      console.warn(`[notifications] Bill id=${bill.id} name="${bill.name}" has no user_id — skipping`);
      continue;
    }

    for (const recipient of recipients) {
      // In per-user mode, only send bills belonging to this recipient
      if (allowUserConfig && bill.user_id !== recipient.id) continue;

      // Check recipient's preferences
      if (type === 'due_3d'    && !recipient.notify_3d)       continue;
      if (type === 'due_1d'    && !recipient.notify_1d)       continue;
      if (type === 'due_today' && !recipient.notify_due)      continue;
      if (type === 'overdue'   && !recipient.notify_overdue)  continue;

      if (hasNotification(db, bill.id, recipient.id, year, month, type, today)) continue;

      const meta    = TYPE_META[type];
      const subject = meta.subject(bill);
      const html    = buildEmailHtml(bill, type, dueDate);

      try {
        await sendEmail(recipient.notification_email, subject, html);
        recordNotification(db, bill.id, recipient.id, year, month, type, today);
      } catch (err) {
        errors.push(`${recipient.notification_email}/${bill.name}: ${err.message}`);
      }
    }
  }

  if (errors.length) {
    markNotificationError(new Error(errors.join('; ')));
    console.error('[notifications] Send errors:', errors);
  } else {
    markNotificationSuccess();
    console.log(`[notifications] Run complete for ${today}`);
  }
}

module.exports = { runNotifications, sendTestEmail, createTransport };
