/**
 * Proposal HTML renderer — proposal-pdf-export.
 * Produces a self-contained, print-grade HTML string for the html-to-pdf worker.
 * No DOM dependency — pure string assembly.
 */
import type { ProposalContent, ProposalSection } from '@zync/types'
import type { ProposalTotals } from './pdf-totals'

// ── Types ─────────────────────────────────────────────────────────────────────

export interface RenderProposalInput {
  proposalName: string
  customerName: string
  createdAt: string
  expiresAt: string | null
  content: ProposalContent
  totals: ProposalTotals
  tenant: {
    name: string
    contactEmail: string | null
    phone: string | null
    logoUrl: string | null
    locale: string
  }
}

// ── Helpers ───────────────────────────────────────────────────────────────────

/** Escape untrusted content for safe HTML insertion */
function esc(s: string): string {
  return s
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;')
}

/**
 * Sanitize an HTML string from a rich-text editor using an allowlist approach.
 * Only known-safe tags are kept; all attributes are stripped except href on <a>
 * (validated against a safe-scheme allowlist after entity-decoding).
 * Denylist-based regex sanitizers have known bypasses; this approach does not.
 */
const ALLOWED_HTML_TAGS = new Set([
  'b', 'i', 'u', 'em', 'strong', 's', 'del', 'ins',
  'p', 'br', 'hr', 'blockquote', 'pre', 'code',
  'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
  'ul', 'ol', 'li',
  'a', 'span', 'div',
])
const SAFE_HREF_SCHEME = /^(https?|mailto|tel):/i

function decodeHtmlEntities(s: string): string {
  return s
    .replace(/&#(\d+);/g, (_, n: string) => String.fromCharCode(parseInt(n, 10)))
    .replace(/&#x([0-9a-fA-F]+);/g, (_, h: string) => String.fromCharCode(parseInt(h, 16)))
    .replace(/&amp;/g, '&')
    .replace(/&lt;/g, '<')
    .replace(/&gt;/g, '>')
    .replace(/&quot;/g, '"')
    .replace(/&#39;/g, "'")
}

function sanitizeHtml(html: string): string {
  return html.replace(
    /<(\/?)([a-zA-Z][a-zA-Z0-9]*)(\s[^>]*)?\s*\/?>/g,
    (_match, closing: string, rawTag: string, rawAttrs: string | undefined) => {
      const tag = rawTag.toLowerCase()
      if (!ALLOWED_HTML_TAGS.has(tag)) return ''
      if (closing) return `</${tag}>`

      // For <a> extract and validate href only
      if (tag === 'a' && rawAttrs) {
        const hrefMatch = rawAttrs.match(/\bhref\s*=\s*(?:"([^"]*)"|'([^']*)'|(\S+))/i)
        if (hrefMatch) {
          const raw = hrefMatch[1] ?? hrefMatch[2] ?? hrefMatch[3] ?? ''
          const decoded = decodeHtmlEntities(raw).replace(/\s/g, '')
          if (SAFE_HREF_SCHEME.test(decoded)) {
            return `<a href="${esc(decoded)}" rel="noopener noreferrer" target="_blank">`
          }
        }
        return '<a>'
      }

      return `<${tag}>`
    },
  )
}

/** Format currency using Intl */
function formatCurrency(amount: number, currency: string, locale: string): string {
  return new Intl.NumberFormat(locale === 'he' ? 'he-IL' : locale, {
    style: 'currency',
    currency,
    minimumFractionDigits: 2,
    maximumFractionDigits: 2,
  }).format(amount)
}

/** Format date for display */
function formatDate(iso: string, locale: string): string {
  try {
    return new Intl.DateTimeFormat(locale === 'he' ? 'he-IL' : locale, {
      year: 'numeric',
      month: 'long',
      day: 'numeric',
    }).format(new Date(iso))
  } catch {
    return iso
  }
}

// ── Section renderers ─────────────────────────────────────────────────────────

function renderTextSection(section: Extract<ProposalSection, { type: 'text' }>): string {
  return `<div class="section-text">${sanitizeHtml(section.html)}</div>`
}

function renderDividerSection(section: Extract<ProposalSection, { type: 'divider' }>): string {
  if (section.label) {
    return `<div class="section-divider"><span class="divider-label">${esc(section.label)}</span></div>`
  }
  return `<hr class="section-divider-line" />`
}

function renderImageSection(section: Extract<ProposalSection, { type: 'image' }>): string {
  const alignStyle =
    section.align === 'start'
      ? 'margin-inline-end: auto'
      : section.align === 'end'
        ? 'margin-inline-start: auto'
        : 'margin-inline: auto'
  return `<div class="section-image" style="display:flex;"><img src="${esc(section.url)}" alt="${esc(section.alt)}" style="max-width:100%;height:auto;display:block;${alignStyle};" /></div>`
}

function renderTestimonialsSection(
  section: Extract<ProposalSection, { type: 'testimonials' }>,
): string {
  if (!section.items.length) return ''
  const items = section.items
    .map(
      (item) => `
    <div class="testimonial-item">
      <blockquote class="testimonial-quote">${esc(item.quote)}</blockquote>
      <div class="testimonial-author">${esc(item.author)}${item.company ? ` &mdash; ${esc(item.company)}` : ''}</div>
    </div>`,
    )
    .join('')
  return `<div class="section-testimonials">${items}</div>`
}

function renderTeamSection(section: Extract<ProposalSection, { type: 'team' }>): string {
  if (!section.members.length) return ''
  const members = section.members
    .map(
      (m) => `
    <div class="team-member">
      <div class="team-member-role">${esc(m.role_label)}</div>
    </div>`,
    )
    .join('')
  return `<div class="section-team">${members}</div>`
}

function renderLineItemsSection(
  section: Extract<ProposalSection, { type: 'line_items' }>,
  totals: ProposalTotals,
  locale: string,
): string {
  if (!section.items.length) return ''

  const { currency, showLineTax } = totals

  const headerRow = `
    <div class="line-items-row line-items-header" role="row">
      <div class="col-desc" role="columnheader">Description</div>
      <div class="col-qty" role="columnheader">Qty</div>
      <div class="col-price" role="columnheader">Unit price</div>
      ${showLineTax ? '<div class="col-tax" role="columnheader">Tax %</div>' : ''}
      <div class="col-subtotal" role="columnheader">Subtotal</div>
    </div>`

  const rows = section.items
    .map((item) => {
      const lineSubtotal = item.quantity * item.unit_price
      return `
    <div class="line-items-row" role="row">
      <div class="col-desc" role="cell">${esc(item.description)}</div>
      <div class="col-qty" role="cell">${item.quantity}</div>
      <div class="col-price" role="cell">${formatCurrency(item.unit_price, currency, locale)}</div>
      ${showLineTax ? `<div class="col-tax" role="cell">${item.tax_pct}%</div>` : ''}
      <div class="col-subtotal" role="cell">${formatCurrency(lineSubtotal, currency, locale)}</div>
    </div>`
    })
    .join('')

  return `<div class="section-line-items" role="table" aria-label="Line items">${headerRow}${rows}</div>`
}

function renderSection(
  section: ProposalSection,
  totals: ProposalTotals,
  locale: string,
): string {
  switch (section.type) {
    case 'text':
      return renderTextSection(section)
    case 'divider':
      return renderDividerSection(section)
    case 'image':
      return renderImageSection(section)
    case 'testimonials':
      return renderTestimonialsSection(section)
    case 'team':
      return renderTeamSection(section)
    case 'line_items':
      return renderLineItemsSection(section, totals, locale)
  }
}

// ── Totals block ──────────────────────────────────────────────────────────────

function renderTotalsBlock(totals: ProposalTotals, locale: string): string {
  const {
    currency,
    subtotalExclVat,
    discountPct,
    discountAmount,
    vatRate,
    vatTotal,
    grandTotal,
    showSubtotal,
  } = totals

  const fmt = (n: number) => formatCurrency(n, currency, locale)

  let rows = ''

  if (showSubtotal) {
    rows += `
      <div class="totals-row">
        <span class="totals-label">Subtotal</span>
        <span class="totals-value">${fmt(subtotalExclVat + discountAmount)}</span>
      </div>`
  }

  if (discountPct > 0) {
    rows += `
      <div class="totals-row">
        <span class="totals-label">Discount (${discountPct}%)</span>
        <span class="totals-value">−${fmt(discountAmount)}</span>
      </div>`
  }

  if (vatRate > 0) {
    const vatPct = Math.round(vatRate * 100)
    rows += `
      <div class="totals-row">
        <span class="totals-label">VAT (${vatPct}%)</span>
        <span class="totals-value">${fmt(vatTotal)}</span>
      </div>`
  }

  rows += `
    <div class="totals-row totals-grand">
      <span class="totals-label">Total</span>
      <span class="totals-value">${fmt(grandTotal)}</span>
    </div>`

  return `<div class="totals-block">${rows}</div>`
}

// ── Main renderer ─────────────────────────────────────────────────────────────

export function renderProposalHtml(input: RenderProposalInput): string {
  const { proposalName, customerName, createdAt, expiresAt, content, totals, tenant } = input
  const locale = tenant.locale ?? 'he'
  const isRtl = locale === 'he' || locale === 'ar'
  const dir = isRtl ? 'rtl' : 'ltr'

  const hasLineItems = content.sections.some((s) => s.type === 'line_items')

  const sectionsHtml = content.sections
    .map((section) => renderSection(section, totals, locale))
    .join('\n')

  const totalsHtml = hasLineItems ? renderTotalsBlock(totals, locale) : ''

  const logoHtml = tenant.logoUrl
    ? `<img src="${esc(tenant.logoUrl)}" alt="${esc(tenant.name)} logo" class="tenant-logo" />`
    : ''

  const footerParts: string[] = [esc(tenant.name)]
  if (tenant.contactEmail) footerParts.push(`<a href="mailto:${esc(tenant.contactEmail)}">${esc(tenant.contactEmail)}</a>`)
  if (tenant.phone) footerParts.push(esc(tenant.phone))

  const createdFormatted = formatDate(createdAt, locale)
  const expiresFormatted = expiresAt ? formatDate(expiresAt, locale) : null

  return `<!DOCTYPE html>
<html lang="${esc(locale)}" dir="${dir}">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>${esc(proposalName)}</title>
  <style>
    *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

    body {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif;
      font-size: 14px;
      line-height: 1.6;
      color: oklch(0.13 0 0);
      background: oklch(1 0 0);
      direction: ${dir};
    }

    .page {
      max-width: 800px;
      margin: 0 auto;
      padding: 48px 40px;
    }

    /* Header */
    .header {
      display: flex;
      align-items: flex-start;
      justify-content: space-between;
      margin-bottom: 32px;
      padding-bottom: 24px;
      border-bottom: 2px solid oklch(0.92 0.005 264);
      gap: 24px;
    }
    .tenant-logo {
      max-height: 56px;
      max-width: 160px;
      object-fit: contain;
      flex-shrink: 0;
    }
    .header-meta { flex: 1; }
    .proposal-title {
      font-size: 22px;
      font-weight: 700;
      color: oklch(0.09 0 0);
      margin-bottom: 8px;
    }
    .header-row {
      font-size: 13px;
      color: oklch(0.52 0.012 264);
      margin-bottom: 4px;
    }
    .header-row strong { color: oklch(0.32 0.013 264); }

    /* Sections */
    .section-text {
      margin-bottom: 24px;
      line-height: 1.7;
    }
    .section-text p { margin-bottom: 12px; }
    .section-text h1, .section-text h2, .section-text h3 { margin-bottom: 8px; font-weight: 600; }

    .section-divider-line {
      border: none;
      border-top: 1px solid oklch(0.92 0.005 264);
      margin: 24px 0;
    }
    .section-divider {
      display: flex;
      align-items: center;
      gap: 12px;
      margin: 24px 0;
    }
    .section-divider::before,
    .section-divider::after {
      content: '';
      flex: 1;
      border-top: 1px solid oklch(0.92 0.005 264);
    }
    .divider-label {
      font-size: 12px;
      font-weight: 600;
      text-transform: uppercase;
      letter-spacing: 0.08em;
      color: oklch(0.69 0.01 264);
    }

    .section-image { margin-bottom: 24px; }

    .section-testimonials { margin-bottom: 24px; }
    .testimonial-item {
      background: oklch(0.98 0 0);
      border-inline-start: 3px solid oklch(0.55 0.22 264);
      padding: 16px 20px;
      margin-bottom: 16px;
      border-radius: 4px;
    }
    .testimonial-quote {
      font-style: italic;
      margin-bottom: 8px;
      color: oklch(0.32 0.013 264);
    }
    .testimonial-author { font-size: 13px; color: oklch(0.52 0.012 264); font-weight: 500; }

    .section-team { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 24px; }
    .team-member {
      background: oklch(0.98 0 0);
      border: 1px solid oklch(0.92 0.005 264);
      border-radius: 6px;
      padding: 12px 16px;
      font-size: 13px;
    }
    .team-member-role { color: oklch(0.52 0.012 264); font-size: 12px; margin-top: 2px; }

    /* Line items grid */
    .section-line-items {
      margin-bottom: 24px;
      border: 1px solid oklch(0.92 0.005 264);
      border-radius: 6px;
      overflow: hidden;
    }
    .line-items-row {
      display: grid;
      grid-template-columns: 1fr 80px 120px 100px;
      gap: 0;
      padding: 10px 16px;
      align-items: center;
    }
    .line-items-row + .line-items-row {
      border-top: 1px solid oklch(0.96 0.003 264);
    }
    .line-items-header {
      background: oklch(0.98 0 0);
      font-size: 12px;
      font-weight: 600;
      text-transform: uppercase;
      letter-spacing: 0.05em;
      color: oklch(0.52 0.012 264);
    }
    .line-items-row .col-qty,
    .line-items-row .col-price,
    .line-items-row .col-tax,
    .line-items-row .col-subtotal {
      text-align: end;
    }

    /* Totals block */
    .totals-block {
      margin-inline-start: auto;
      width: 320px;
      border: 1px solid oklch(0.92 0.005 264);
      border-radius: 6px;
      overflow: hidden;
      margin-bottom: 32px;
    }
    .totals-row {
      display: flex;
      justify-content: space-between;
      padding: 8px 16px;
      font-size: 14px;
    }
    .totals-row + .totals-row { border-top: 1px solid oklch(0.96 0.003 264); }
    .totals-label { color: oklch(0.52 0.012 264); }
    .totals-value { font-weight: 500; }
    .totals-grand {
      background: oklch(0.98 0 0);
      font-size: 15px;
      font-weight: 700;
      border-top: 2px solid oklch(0.92 0.005 264) !important;
    }
    .totals-grand .totals-label,
    .totals-grand .totals-value { color: oklch(0.09 0 0); }

    /* Footer */
    .footer {
      margin-top: 48px;
      padding-top: 16px;
      border-top: 1px solid oklch(0.92 0.005 264);
      font-size: 12px;
      color: oklch(0.69 0.01 264);
      text-align: center;
    }
    .footer a { color: oklch(0.52 0.012 264); text-decoration: none; }

    @media print {
      body { font-size: 12px; }
      .page { padding: 24px 20px; max-width: 100%; }
      .section-line-items, .totals-block { page-break-inside: avoid; }
    }
  </style>
</head>
<body>
  <div class="page">
    <div class="header">
      <div class="header-meta">
        <div class="proposal-title">${esc(proposalName)}</div>
        <div class="header-row"><strong>For:</strong> ${esc(customerName)}</div>
        <div class="header-row"><strong>Date:</strong> ${esc(createdFormatted)}</div>
        ${expiresFormatted ? `<div class="header-row"><strong>Valid until:</strong> ${esc(expiresFormatted)}</div>` : ''}
      </div>
      ${logoHtml}
    </div>

    ${sectionsHtml}

    ${totalsHtml}

    <div class="footer">
      ${footerParts.join(' &bull; ')}
    </div>
  </div>
</body>
</html>`
}
