/**
 * Shared HTML shell for transactional emails. Centralises the table-
 * based layout + the per-port branding override surface so templates
 * don't each inline a different copy of the boilerplate.
 *
 * Per-port branding (R2-H15):
 *   - logoUrl       - replaces the default Port Nimara logo image
 *   - primaryColor  - used for the page-title accent color
 *   - emailHeaderHtml / emailFooterHtml - admin-authored HTML that
 *     appears above / below the body content (e.g. legal footer,
 *     custom marketing strip). When unset, the existing minimal
 *     "Thank you, {{portName}} CRM" sign-off is rendered by callers.
 *
 * Senders resolve a `BrandingShell` via `resolveBrandingShell(portId)`
 * (or pass `null` for no override) and forward it to the template
 * function. Templates call `renderShell({ title, body, branding })`.
 */

import type * as React from 'react';

import { absolutizeBrandingUrl } from '@/lib/branding/url';

// Neutral defaults - no tenant-specific imagery leaks across ports.
// When branding hasn't been configured the email renders without a logo
// and on a plain off-white background. Admins upload their own assets via
// /admin/branding which then flow through via getPortBrandingConfig().
const DEFAULT_LOGO_URL: string | null = null;
const DEFAULT_BACKGROUND_URL: string | null = null;
const DEFAULT_PRIMARY_COLOR = '#1e293b';

export interface BrandingShell {
  logoUrl: string | null;
  /** Phase 5: blurred page-background image rendered behind the white
   *  card. Defaults to the Port Nimara overhead image. Ports with
   *  their own marina photography override via system_settings. */
  backgroundUrl: string | null;
  primaryColor: string | null;
  emailHeaderHtml: string | null;
  emailFooterHtml: string | null;
}

interface ShellOpts {
  title: string;
  body: string;
  branding?: BrandingShell | null;
}

export function renderShell({ title, body, branding }: ShellOpts): string {
  // Branding URLs are stored path-only (so in-app rendering works across
  // any host). Mail clients have no app origin, so re-absolutize here.
  const logoUrl = absolutizeBrandingUrl(branding?.logoUrl ?? DEFAULT_LOGO_URL);
  const backgroundUrl = absolutizeBrandingUrl(branding?.backgroundUrl ?? DEFAULT_BACKGROUND_URL);
  // SECURITY / trust boundary (audit L16b): `emailHeaderHtml` / `emailFooterHtml`
  // are admin-authored branding HTML and are interpolated RAW into the email
  // body below (intentional - admins legitimately need to paste a styled
  // legal footer / marketing strip). Authoring them is gated on the
  // `manage_settings` permission, so the worst case is a self-XSS by the
  // highest-privileged user on a tenant they already control - not a
  // cross-tenant or privilege-escalation vector (each port reads only its
  // own settings rows). If a future tenant model has MULTIPLE admins with
  // mutually-distrusting `manage_settings` holders, allowlist-sanitize these
  // two fields here (e.g. via a sanitize-html allowlist of safe tags/attrs)
  // before interpolation. Until then we keep them raw by design.
  const headerHtml = branding?.emailHeaderHtml ?? '';
  const footerHtml = branding?.emailFooterHtml ?? '';

  const wrapperStyle = backgroundUrl
    ? `background-image: url('${backgroundUrl}'); background-size: cover; background-position: center; background-color:#f2f2f2;`
    : 'background-color:#f2f2f2;';
  const logoBlock = logoUrl
    ? `<center><img src="${logoUrl}" alt="Logo" width="100" style="margin-bottom:20px;" /></center>`
    : '';

  return `<!DOCTYPE html>
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <title>${title}</title>
  <style type="text/css">
    table, td { mso-table-lspace: 0pt; mso-table-rspace: 0pt; }
    img { border: 0; display: block; }
    p { margin: 0; padding: 0; }
  </style>
</head>
<body style="margin:0; padding:0; background-color:#f2f2f2;">
  <table role="presentation" width="100%" border="0" cellspacing="0" cellpadding="0" style="${wrapperStyle}">
    <tr>
      <td align="center" style="padding:30px 16px;">
        <table role="presentation" width="600" border="0" cellspacing="0" cellpadding="0" style="width:100%; max-width:600px; background-color:#ffffff; border-radius:8px; overflow:hidden; box-shadow:0 2px 4px rgba(0,0,0,0.1);">
          <tr>
            <td style="padding:20px; font-family: Arial, sans-serif; color:#333333; word-break:break-word;">
              ${logoBlock}
              ${headerHtml ? `<div>${headerHtml}</div>` : ''}
              ${body}
              ${footerHtml ? `<div style="margin-top:24px;">${footerHtml}</div>` : ''}
            </td>
          </tr>
        </table>
      </td>
    </tr>
  </table>
</body>
</html>`;
}

/** Surface the brand primary color to template bodies. */
export function brandingPrimaryColor(branding?: BrandingShell | null): string {
  return branding?.primaryColor ?? DEFAULT_PRIMARY_COLOR;
}

/**
 * Shared style conventions for transactional email bodies.
 *
 * Templates compose these instead of inlining one-off `style={{...}}` objects
 * so the visual rhythm stays consistent across every email - centered title
 * in the brand accent, body paragraphs left-aligned at 16px / 1.5 line-height,
 * centered CTA button, fine-print block separated by a soft divider, centered
 * sign-off in the same accent. Modeled on the hand-rolled templates from the
 * original portal (signature-notifications.ts) so the look carries forward.
 *
 * Functions accept an `accent` color (the resolved port primary) where it's
 * load-bearing; constants do not.
 */
export const emailStyle = {
  /** Page heading: centered, brand-accent, bold. Used once at the top. */
  title: (accent: string): React.CSSProperties => ({
    textAlign: 'center',
    fontSize: '22px',
    fontWeight: 'bold',
    color: accent,
    margin: '0 0 16px 0',
  }),
  /** Body paragraph: 16px / 1.5 line-height, left-aligned for readability. */
  paragraph: {
    fontSize: '16px',
    lineHeight: '1.5',
    margin: '0 0 16px 0',
    color: '#333333',
  } satisfies React.CSSProperties,
  /** Soft hairline divider above fine-print blocks. */
  divider: {
    border: 'none',
    borderTop: '1px solid #eee',
    margin: '28px 0 0 0',
  } satisfies React.CSSProperties,
  /** Fine print: 14px muted, line-height 1.5. */
  finePrint: {
    fontSize: '14px',
    color: '#666666',
    lineHeight: '1.5',
    margin: '12px 0 0 0',
  } satisfies React.CSSProperties,
  /** Sign-off block: left-aligned, 16px, sits BETWEEN the last body
   *  paragraph and the primary CTA so the email reads like a letter
   *  (greeting -> body -> sign-off -> button -> button-fallback fine
   *  print). Top margin is intentionally modest because preceding
   *  paragraphs already carry 16px bottom margin. */
  signoff: {
    textAlign: 'left',
    fontSize: '16px',
    color: '#333333',
    margin: '8px 0 0 0',
  } satisfies React.CSSProperties,
  /** Outer wrapper that centers the primary CTA button. */
  buttonRow: {
    textAlign: 'center',
    margin: '28px 0',
  } satisfies React.CSSProperties,
  /** Primary CTA button style. Compose with `buttonRow` for the surrounding center. */
  button: (accent: string): React.CSSProperties => ({
    display: 'inline-block',
    backgroundColor: accent,
    color: '#ffffff',
    textDecoration: 'none',
    padding: '14px 35px',
    borderRadius: '5px',
    fontWeight: 'bold',
    fontSize: '16px',
  }),
} as const;

/**
 * URL-safe escaper for `href="..."` interpolations inside email
 * templates. The email-deliverability audit flagged that every template
 * inlined `${data.link}` directly into href + visible text without
 * escaping - a `"` (or worse, a `javascript:` scheme) would break out
 * of the attribute or trigger an XSS when the recipient opens the email
 * in a webmail client that runs scripts.
 *
 * Two-step defense:
 *  1. Scheme allow-list - only http(s), mailto, tel survive; everything
 *     else (javascript:, data:, vbscript:, file:, …) is rewritten to
 *     `about:blank`.
 *  2. HTML-attribute escape on `"`, `<`, `>`, `&`, `'`, backtick.
 */
export function safeUrl(url: string | null | undefined): string {
  if (!url) return 'about:blank';
  const trimmed = String(url).trim();
  // Block dangerous schemes. The allow-list is intentionally short.
  const lower = trimmed.toLowerCase();
  const ok =
    lower.startsWith('http://') ||
    lower.startsWith('https://') ||
    lower.startsWith('mailto:') ||
    lower.startsWith('tel:') ||
    // Relative or root-relative paths are also acceptable - they
    // resolve against the host the email links to (rare in transactional
    // mail but used by tracking pixels and unsubscribe headers).
    lower.startsWith('/') ||
    lower.startsWith('#');
  if (!ok) return 'about:blank';
  return trimmed
    .replace(/&/g, '&amp;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/`/g, '&#96;');
}
