/**
 * Proposal content security validation — proposal-editor (wave-11 leaf-C).
 * Server-side guard: validates ProposalContent against the Zod schema and
 * then walks every `text` section's html for disallowed Tiptap node types.
 *
 * Mirrors validateTiptapContent from kb-article-editor (spec 101).
 */
import { proposalContentSchema } from '@zync/types'
import type { ProposalContent } from '@zync/types'

// ── Allowed HTML tag set (mirrors KB editor ALLOWED_NODE_TYPES) ───────────────

/**
 * Tiptap node types allowed in proposal text sections.
 * These correspond to the output HTML tags from @tiptap/starter-kit.
 */
export const ALLOWED_NODE_TYPES: Set<string> = new Set([
  'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
  'ul', 'ol', 'li',
  'strong', 'em', 'u', 'br',
  'blockquote', 'hr',
  // text nodes have no tag, allowed implicitly
])

/**
 * Disallowed tags that must never appear in proposal content.
 * Used as a deny-list check against the sanitized HTML.
 */
const DISALLOWED_TAGS: readonly string[] = [
  'script', 'iframe', 'object', 'embed', 'form', 'input',
  'textarea', 'button', 'select', 'style', 'link', 'meta',
  'base', 'applet', 'noscript', 'canvas', 'svg',
]

/**
 * Validate proposal content against the schema and content security rules.
 * Throws a 422-compatible error if validation fails.
 * Call this before any DB write in create/update/send routes.
 */
export function validateProposalContent(content: unknown): asserts content is ProposalContent {
  const parsed = proposalContentSchema.safeParse(content)
  if (!parsed.success) {
    const msg = parsed.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`).join('; ')
    const err = new Error(`Invalid proposal content: ${msg}`)
    ;(err as Error & { status: number }).status = 422
    throw err
  }

  for (const section of parsed.data.sections) {
    if (section.type === 'text') {
      validateHtmlSection(section.html)
    }
  }
}

function validateHtmlSection(html: string): void {
  for (const tag of DISALLOWED_TAGS) {
    // Check for opening tags like <script, <script>, <script ...>
    const pattern = new RegExp(`<\\s*${tag}[\\s/>]`, 'i')
    if (pattern.test(html)) {
      const err = new Error(`Disallowed HTML tag in proposal content: <${tag}>`)
      ;(err as Error & { status: number }).status = 422
      throw err
    }
  }

  // Check for javascript: protocol in href/src/action attributes
  if (/javascript\s*:/i.test(html)) {
    const err = new Error('Disallowed protocol in proposal content: javascript:')
    ;(err as Error & { status: number }).status = 422
    throw err
  }

  // Check for event handler attributes (onclick, onerror, etc.)
  if (/\bon\w+\s*=/i.test(html)) {
    const err = new Error('Disallowed event handler in proposal content')
    ;(err as Error & { status: number }).status = 422
    throw err
  }
}
