/**
 * Thin Resend wrapper. No SDK — one HTTP POST keeps the dep list small.
 *
 * Dev fallback: if RESEND_API_KEY is missing, we log the email to stdout
 * (including any accept/action URL) so local dev flows still work without
 * an outbound email provider. Production MUST set RESEND_API_KEY.
 */

interface SendEmailInput {
  to: string
  subject: string
  html: string
  /** Optional — surfaces in server logs during dev fallback. */
  devPreviewUrl?: string
}

const FROM = process.env.RESEND_FROM ?? 'Spanlens <notifications@spanlens.io>'

async function attemptSend(input: SendEmailInput, apiKey: string): Promise<{ sent: boolean; id?: string; error?: string }> {
  const res = await fetch('https://api.resend.com/emails', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${apiKey}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      from: FROM,
      to: input.to,
      subject: input.subject,
      html: input.html,
    }),
    signal: AbortSignal.timeout(8000),
  })

  if (!res.ok) {
    const text = await res.text().catch(() => '')
    return { sent: false, error: `Resend ${res.status}: ${text.slice(0, 200)}` }
  }
  const body = (await res.json().catch(() => ({}))) as { id?: string }
  return body.id ? { sent: true, id: body.id } : { sent: true }
}

export async function sendEmail(input: SendEmailInput): Promise<{ sent: boolean; id?: string; error?: string }> {
  const apiKey = process.env.RESEND_API_KEY
  if (!apiKey) {
    // Dev fallback: print the essentials. This intentionally does NOT log the
    // full HTML — too noisy. The accept URL is the one thing devs actually need.
    // eslint-disable-next-line no-console
    console.log(`[email-dev] to=${input.to} subject="${input.subject}"` +
      (input.devPreviewUrl ? ` url=${input.devPreviewUrl}` : ''))
    return { sent: false }
  }

  // One retry on network errors (ECONNRESET, timeout, etc.)
  try {
    return await attemptSend(input, apiKey)
  } catch (err) {
    const isNetworkErr = err instanceof TypeError || (err as NodeJS.ErrnoException).code === 'ECONNRESET'
    if (!isNetworkErr) return { sent: false, error: String(err) }
    // Wait 1s then retry once
    await new Promise((r) => setTimeout(r, 1000))
    return attemptSend(input, apiKey)
  }
}

export function renderInvitationEmail(params: {
  orgName: string
  inviterEmail: string
  role: string
  acceptUrl: string
}): { subject: string; html: string } {
  const { orgName, inviterEmail, role, acceptUrl } = params
  const subject = `You're invited to ${orgName} on Spanlens`
  // PLG Loop ⑤ — quiet brand footer. The invitee often hasn't heard of Spanlens
  // before; one informational line tells them what they've been invited into
  // without acting like an ad. Tone matches the waitlist email's closing.
  // utm tags identify invite-driven traffic in marketing analytics.
  const brandLink =
    'https://www.spanlens.io?utm_source=invite_email&utm_medium=email&utm_campaign=plg'

  const html = `
    <div style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; max-width: 560px; margin: 0 auto; padding: 24px; color: #111;">
      <h2 style="margin: 0 0 16px; font-size: 20px;">You're invited to <strong>${escapeHtml(orgName)}</strong></h2>
      <p style="margin: 0 0 12px; color: #555;">${escapeHtml(inviterEmail)} invited you to join their Spanlens workspace as <strong>${escapeHtml(role)}</strong>.</p>
      <p style="margin: 24px 0;"><a href="${acceptUrl}" style="display: inline-block; padding: 10px 18px; background: #111; color: #fff; text-decoration: none; border-radius: 6px; font-weight: 500;">Accept invitation</a></p>
      <p style="margin: 16px 0 0; color: #888; font-size: 13px;">Or copy this link: <br/><span style="word-break: break-all;">${acceptUrl}</span></p>
      <p style="margin: 24px 0 0; color: #aaa; font-size: 12px;">This invitation expires in 7 days. If you weren't expecting it, you can safely ignore this email.</p>
      <div style="margin-top: 28px; padding-top: 16px; border-top: 1px solid #eee; font-size: 11.5px; color: #aaa;">
        <a href="${brandLink}" style="color: #aaa; text-decoration: none;">Spanlens</a> — open-source LLM observability
      </div>
    </div>
  `.trim()
  return { subject, html }
}

export function renderWaitlistConfirmationEmail(): { subject: string; html: string } {
  const subject = "You're on the Spanlens waitlist"
  const demoUrl = 'https://www.spanlens.io/demo/dashboard'

  const html = `
    <div style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; max-width: 560px; margin: 0 auto; padding: 32px 24px; color: #111;">
      <div style="margin-bottom: 28px;">
        <span style="display: inline-block; font-family: ui-monospace, monospace; font-size: 13px; font-weight: 600; letter-spacing: -0.3px;">spanlens</span>
      </div>

      <h2 style="margin: 0 0 12px; font-size: 22px; font-weight: 600; letter-spacing: -0.5px;">You're on the list 🎉</h2>

      <p style="margin: 0 0 16px; font-size: 14px; line-height: 1.65; color: #444;">
        Thanks for signing up — you're on the Spanlens early access list.<br/>
        We're launching on <strong>June 3, 2026</strong> and we'll send your
        access link the moment we go live.
      </p>

      <div style="background: #f5f5f4; border-radius: 8px; padding: 16px 18px; margin: 24px 0; font-size: 13px; color: #555; line-height: 1.6;">
        <div style="font-weight: 600; color: #111; margin-bottom: 6px; font-size: 13px;">Try the live demo while you wait</div>
        The full Spanlens dashboard is available right now in demo mode —
        explore request logs, agent traces, cost tracking, and anomaly
        detection with sample data. No signup required.
      </div>

      <p style="margin: 0 0 24px;">
        <a href="${demoUrl}" style="display: inline-block; padding: 11px 20px; background: #111; color: #fff; text-decoration: none; border-radius: 7px; font-weight: 500; font-size: 13px;">Explore the live demo →</a>
      </p>

      <p style="margin: 0 0 8px; font-size: 13px; line-height: 1.65; color: #444;">
        Questions? Reply to this email or reach us at <a href="mailto:hi@spanlens.io" style="color: #111;">hi@spanlens.io</a>.
      </p>
      <p style="margin: 0; font-size: 13px; color: #444;">Thanks,<br/>The Spanlens team</p>

      <div style="margin-top: 36px; padding-top: 20px; border-top: 1px solid #e5e5e5; font-size: 11.5px; color: #aaa;">
        You received this because you joined the Spanlens waitlist. We'll only email you once more — on launch day.
      </div>
    </div>
  `.trim()

  return { subject, html }
}

function escapeHtml(s: string): string {
  return s
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;')
}

function ageString(isoOrNull: string | null, fallbackIso: string): string {
  const ref = isoOrNull ?? fallbackIso
  const days = Math.floor((Date.now() - Date.parse(ref)) / 86_400_000)
  if (isoOrNull == null) return `never used (created ${days}d ago)`
  return `last used ${days}d ago`
}

export function renderStaleKeyDigestEmail(params: {
  orgName: string
  thresholdDays: number
  keys: Array<{ name: string; provider: string; last_used_at: string | null; created_at: string }>
  dashboardUrl: string
}): { subject: string; html: string } {
  const { orgName, thresholdDays, keys, dashboardUrl } = params
  const subject = `[Spanlens] ${keys.length} unused provider key${keys.length === 1 ? '' : 's'} in '${orgName}'`

  const rows = keys
    .map((k) => `
      <tr>
        <td style="padding: 8px 12px; border-bottom: 1px solid #eee; font-family: ui-monospace, monospace; font-size: 13px;">${escapeHtml(k.name)}</td>
        <td style="padding: 8px 12px; border-bottom: 1px solid #eee; font-family: ui-monospace, monospace; font-size: 12px; color: #666;">${escapeHtml(k.provider)}</td>
        <td style="padding: 8px 12px; border-bottom: 1px solid #eee; font-size: 12px; color: #666;">${escapeHtml(ageString(k.last_used_at, k.created_at))}</td>
      </tr>`)
    .join('')

  const html = `
    <div style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; max-width: 620px; margin: 0 auto; padding: 24px; color: #111;">
      <h2 style="margin: 0 0 8px; font-size: 19px;">Unused provider keys in <strong>${escapeHtml(orgName)}</strong></h2>
      <p style="margin: 0 0 18px; color: #555; font-size: 14px;">
        The following ${keys.length} key${keys.length === 1 ? ' has' : 's have'} not been used in <strong>${thresholdDays}+ days</strong>.
        For security, consider deleting any keys you no longer need.
      </p>
      <table style="width: 100%; border-collapse: collapse; border: 1px solid #eee; border-radius: 6px; overflow: hidden;">
        <thead>
          <tr style="background: #fafafa;">
            <th style="padding: 8px 12px; text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; color: #888; border-bottom: 1px solid #eee;">Name</th>
            <th style="padding: 8px 12px; text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; color: #888; border-bottom: 1px solid #eee;">Provider</th>
            <th style="padding: 8px 12px; text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; color: #888; border-bottom: 1px solid #eee;">Last activity</th>
          </tr>
        </thead>
        <tbody>${rows}</tbody>
      </table>
      <p style="margin: 22px 0;">
        <a href="${dashboardUrl}" style="display: inline-block; padding: 10px 18px; background: #111; color: #fff; text-decoration: none; border-radius: 6px; font-weight: 500; font-size: 13px;">Review keys in dashboard</a>
      </p>
      <p style="margin: 18px 0 0; color: #aaa; font-size: 11.5px;">
        Notification-only — Spanlens never auto-revokes keys.
        To stop these reminders: Settings → Provider keys → Stale key reminders.
      </p>
    </div>
  `.trim()

  return { subject, html }
}

export function renderSecurityAlertEmail(params: {
  orgName: string
  projectName: string
  requestFlags: Array<{ type: string; pattern: string; sample: string }>
  responseFlags: Array<{ type: string; pattern: string; sample: string }>
  dashboardUrl: string
}): { subject: string; html: string } {
  const { orgName, projectName, requestFlags, responseFlags, dashboardUrl } = params

  const allFlags = [
    ...requestFlags.map((f) => ({ ...f, direction: 'Request' as const })),
    ...responseFlags.map((f) => ({ ...f, direction: 'Response' as const })),
  ]

  const hasInjection = allFlags.some((f) => f.type === 'injection')
  const subject = hasInjection
    ? `[Spanlens] ⚠️ Prompt injection detected in '${projectName}'`
    : `[Spanlens] 🔍 PII detected in '${projectName}'`

  const flagRows = allFlags
    .map((f) => `
      <tr>
        <td style="padding: 7px 12px; border-bottom: 1px solid #eee; font-size: 12px; color: #666;">${escapeHtml(f.direction)}</td>
        <td style="padding: 7px 12px; border-bottom: 1px solid #eee; font-family: ui-monospace, monospace; font-size: 11px;">
          <span style="display: inline-block; padding: 2px 6px; border-radius: 3px; border: 1px solid ${f.type === 'injection' ? '#fca5a5' : '#e5e7eb'}; background: ${f.type === 'injection' ? '#fef2f2' : '#f9fafb'}; color: ${f.type === 'injection' ? '#991b1b' : '#6b7280'}; text-transform: uppercase; font-size: 10px; letter-spacing: 0.04em;">${escapeHtml(f.type)}</span>
          &nbsp;${escapeHtml(f.pattern)}
        </td>
        <td style="padding: 7px 12px; border-bottom: 1px solid #eee; font-family: ui-monospace, monospace; font-size: 11px; color: #9ca3af;">${escapeHtml(f.sample)}</td>
      </tr>`)
    .join('')

  const html = `
    <div style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; max-width: 600px; margin: 0 auto; padding: 24px; color: #111;">
      <div style="background: #fef2f2; border: 1px solid #fecaca; border-radius: 8px; padding: 14px 16px; margin-bottom: 18px;">
        <div style="font-weight: 600; font-size: 14px; color: #991b1b; margin-bottom: 4px;">Security event detected</div>
        <div style="font-size: 13px; color: #7f1d1d;">
          ${escapeHtml(String(allFlags.length))} flag${allFlags.length === 1 ? '' : 's'} found in project <strong>${escapeHtml(projectName)}</strong> (${escapeHtml(orgName)}).
        </div>
      </div>
      <table style="width: 100%; border-collapse: collapse; border: 1px solid #eee; border-radius: 6px; overflow: hidden; margin-bottom: 18px;">
        <thead>
          <tr style="background: #fafafa;">
            <th style="padding: 8px 12px; text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; color: #888; border-bottom: 1px solid #eee;">Direction</th>
            <th style="padding: 8px 12px; text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; color: #888; border-bottom: 1px solid #eee;">Type · Pattern</th>
            <th style="padding: 8px 12px; text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; color: #888; border-bottom: 1px solid #eee;">Sample (masked)</th>
          </tr>
        </thead>
        <tbody>${flagRows}</tbody>
      </table>
      <p style="margin: 18px 0;">
        <a href="${dashboardUrl}" style="display: inline-block; padding: 10px 18px; background: #111; color: #fff; text-decoration: none; border-radius: 6px; font-weight: 500; font-size: 13px;">View in Security dashboard</a>
      </p>
      <p style="margin: 18px 0 0; color: #aaa; font-size: 11.5px;">
        Spanlens flags only — no requests are blocked unless you enable Block mode.
        To stop these emails: Security → Alert emails → off.
      </p>
    </div>
  `.trim()

  return { subject, html }
}

export function renderLeakAlertEmail(params: {
  orgName: string
  keyName: string
  provider: string
  detectedAt: string
  dashboardUrl: string
}): { subject: string; html: string } {
  const { orgName, keyName, provider, detectedAt, dashboardUrl } = params
  const subject = `[Spanlens] 🚨 Provider key '${keyName}' may be leaked`

  const html = `
    <div style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; max-width: 580px; margin: 0 auto; padding: 24px; color: #111;">
      <div style="background: #fef2f2; border: 1px solid #fecaca; border-radius: 8px; padding: 14px 16px; margin-bottom: 18px;">
        <div style="font-weight: 600; font-size: 14px; color: #991b1b; margin-bottom: 4px;">⚠ Possible secret exposure detected</div>
        <div style="font-size: 13px; color: #7f1d1d;">A provider key in <strong>${escapeHtml(orgName)}</strong> matched a known-leaked-secrets database.</div>
      </div>
      <table style="width: 100%; font-size: 13px; margin-bottom: 16px;">
        <tr><td style="padding: 4px 0; color: #888; width: 110px;">Key</td><td style="font-family: ui-monospace, monospace;"><strong>${escapeHtml(keyName)}</strong></td></tr>
        <tr><td style="padding: 4px 0; color: #888;">Provider</td><td style="font-family: ui-monospace, monospace;">${escapeHtml(provider)}</td></tr>
        <tr><td style="padding: 4px 0; color: #888;">Detected at</td><td style="font-family: ui-monospace, monospace;">${escapeHtml(detectedAt)}</td></tr>
        <tr><td style="padding: 4px 0; color: #888;">Source</td><td>GitGuardian (HasMySecretLeaked)</td></tr>
      </table>
      <p style="margin: 0 0 14px; color: #444; font-size: 13.5px;">
        <strong>Recommended action:</strong> rotate or revoke this key in the dashboard immediately.
        Spanlens will not auto-revoke — admins decide.
      </p>
      <p style="margin: 18px 0;">
        <a href="${dashboardUrl}" style="display: inline-block; padding: 10px 18px; background: #b91c1c; color: #fff; text-decoration: none; border-radius: 6px; font-weight: 500; font-size: 13px;">Review in dashboard</a>
      </p>
      <p style="margin: 18px 0 0; color: #aaa; font-size: 11.5px;">
        False positives are possible — verify before revoking. The k-anonymity check transmits only a 5-char hash prefix to GitGuardian, never the key itself.
      </p>
    </div>
  `.trim()

  return { subject, html }
}

/**
 * P2.7 — past_due warning + auto-downgrade notification.
 *
 * Three flavours, picked by `params.stage`:
 *   • 'warning-d3' — payment failed 4 days ago, 3 days until auto-downgrade
 *   • 'warning-d1' — 6 days ago, 1 day until auto-downgrade
 *   • 'downgraded' — sent immediately after the cron flips the org to free
 *
 * Tone: calm reminder, not dunning. Most past_due is an expired card.
 */
export function renderPastDueEmail(params: {
  orgName: string
  stage: 'warning-d3' | 'warning-d1' | 'downgraded'
  pastDueSince: string
  billingUrl: string
}): { subject: string; html: string } {
  const { orgName, stage, pastDueSince, billingUrl } = params
  const sinceLabel = new Date(pastDueSince).toLocaleDateString('en-US', {
    year: 'numeric', month: 'short', day: 'numeric',
  })

  let subject: string
  let headerBg: string
  let headerBorder: string
  let headerColor: string
  let headerTitle: string
  let body: string
  let ctaLabel: string

  if (stage === 'warning-d3') {
    subject = `[Spanlens] Payment failed — update your card to keep your plan`
    headerBg = '#fff7ed'
    headerBorder = '#fed7aa'
    headerColor = '#9a3412'
    headerTitle = 'Payment failed'
    body = `
      Hi ${escapeHtml(orgName)} team,
      <br><br>
      Your most recent Spanlens invoice didn&apos;t go through — usually an expired
      or replaced card. We&apos;ll keep your plan active for <strong>3 more days</strong>;
      after that the workspace automatically drops to the Free plan and log
      retention shortens to 14 days.
      <br><br>
      Updating the card on file takes about 30 seconds and avoids any service
      change.
    `
    ctaLabel = 'Update payment method'
  } else if (stage === 'warning-d1') {
    subject = `[Spanlens] Last reminder — auto-downgrade in 24 hours`
    headerBg = '#fef2f2'
    headerBorder = '#fecaca'
    headerColor = '#991b1b'
    headerTitle = 'Auto-downgrade in 24 hours'
    body = `
      Hi ${escapeHtml(orgName)} team,
      <br><br>
      We still haven&apos;t been able to charge the card on file (first failure on
      ${escapeHtml(sinceLabel)}). If we don&apos;t recover by tomorrow, the workspace
      drops to the Free plan and log retention shortens to 14 days.
      <br><br>
      You can re-upgrade at any time — this is just a heads-up so it&apos;s not a
      surprise.
    `
    ctaLabel = 'Update payment method'
  } else {
    subject = `[Spanlens] Your workspace has been moved to the Free plan`
    headerBg = '#f1f5f9'
    headerBorder = '#e2e8f0'
    headerColor = '#0f172a'
    headerTitle = 'Plan changed to Free'
    body = `
      Hi ${escapeHtml(orgName)} team,
      <br><br>
      After 7 days without a successful payment (first failure
      ${escapeHtml(sinceLabel)}), we&apos;ve moved this workspace to the Free plan.
      Existing logs older than 14 days are no longer visible in the dashboard,
      but they remain in our database for a 7-day grace window — re-upgrade in
      that time and they reappear automatically.
      <br><br>
      Nothing has been deleted yet. Your data, projects, API keys, and integrations
      are all intact — only the plan tier changed.
    `
    ctaLabel = 'Re-upgrade'
  }

  const html = `
    <div style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; max-width: 580px; margin: 0 auto; padding: 24px; color: #111;">
      <div style="background: ${headerBg}; border: 1px solid ${headerBorder}; border-radius: 8px; padding: 14px 16px; margin-bottom: 18px;">
        <div style="font-weight: 600; font-size: 14px; color: ${headerColor};">${headerTitle}</div>
      </div>
      <p style="margin: 0 0 14px; color: #333; font-size: 13.5px; line-height: 1.55;">${body.trim()}</p>
      <p style="margin: 22px 0;">
        <a href="${billingUrl}" style="display: inline-block; padding: 10px 18px; background: #111; color: #fff; text-decoration: none; border-radius: 6px; font-weight: 500; font-size: 13px;">${ctaLabel}</a>
      </p>
      <p style="margin: 18px 0 0; color: #aaa; font-size: 11.5px;">
        Questions? Reply to this email — it goes straight to the team.
      </p>
    </div>
  `.trim()

  return { subject, html }
}
