import { existsSync } from 'node:fs'
import { createError, defineEventHandler, readBody, setHeader, type H3Event } from 'h3'

type RenderRecoveryDocumentPdfBody = {
  fileName?: string
  html?: string
  title?: string
}

const sanitizeDownloadBaseName = (value?: string) => {
  const sanitized = String(value || 'document')
    .replace(/[\\/:*?"<>|\r\n]+/g, '_')
    .replace(/\s+/g, '_')
    .replace(/\.+$/g, '')
    .trim()

  return sanitized || 'document'
}

const toAsciiDownloadFileName = (value: string) => {
  const ascii = value
    .normalize('NFKD')
    .replace(/[^\x20-\x7E]+/g, '_')
    .replace(/["%;\\]/g, '_')
    .replace(/\s+/g, '_')
    .replace(/_+/g, '_')
    .replace(/^_+|_+$/g, '')

  return ascii || 'document'
}

const encodeContentDispositionFileName = (value: string) =>
  encodeURIComponent(value).replace(/['()*]/g, (character) =>
    `%${character.charCodeAt(0).toString(16).toUpperCase()}`
  )

const resolveRequestOrigin = (event: H3Event) => {
  const forwardedProtoHeader = event.node.req.headers['x-forwarded-proto']
  const forwardedProto = Array.isArray(forwardedProtoHeader)
    ? forwardedProtoHeader[0]
    : forwardedProtoHeader?.split(',')[0]
  const hostHeader = event.node.req.headers['x-forwarded-host'] || event.node.req.headers.host
  const host = Array.isArray(hostHeader) ? hostHeader[0] : hostHeader
  const protocol = forwardedProto || 'http'

  return host ? `${protocol}://${host}/` : 'http://localhost:5700/'
}

const injectBaseHrefIfMissing = (html: string, baseHref: string) => {
  if (/<base\b/i.test(html)) {
    return html
  }

  const escapedBaseHref = baseHref.replace(/"/g, '&quot;')
  if (/<head[^>]*>/i.test(html)) {
    return html.replace(/<head([^>]*)>/i, `<head$1><base href="${escapedBaseHref}" />`)
  }

  return `<!DOCTYPE html><html><head><base href="${escapedBaseHref}" /></head><body>${html}</body></html>`
}

const getBundledChromiumExecutablePath = (chromium: { executablePath: () => string }) => {
  try {
    return chromium.executablePath()
  } catch {
    return ''
  }
}

const resolveBrowserRuntime = async () => {
  const { chromium } = await import('playwright-core')
  const candidateBrowserPaths = [
    process.env.PDF_RENDERER_EXECUTABLE_PATH,
    getBundledChromiumExecutablePath(chromium),
    process.env.CHROME_EXECUTABLE_PATH,
    process.env.GOOGLE_CHROME_BIN,
    'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
    'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe',
    '/usr/bin/google-chrome',
    '/usr/bin/google-chrome-stable',
    '/usr/bin/chromium',
    '/usr/bin/chromium-browser',
    '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'
  ].filter((value): value is string => Boolean(value))

  const executablePath = candidateBrowserPaths.find((candidate) => existsSync(candidate))
  if (!executablePath) {
    throw createError({
      statusCode: 500,
      statusMessage: 'PDF 렌더러용 Chromium/Chrome 실행 파일을 찾지 못했습니다.'
    })
  }

  return {
    chromium,
    executablePath
  }
}

export default defineEventHandler(async (event) => {
  const body = await readBody<RenderRecoveryDocumentPdfBody>(event)
  const html = body?.html?.trim()

  if (!html) {
    throw createError({
      statusCode: 400,
      statusMessage: 'PDF 렌더링용 HTML이 비어 있습니다.'
    })
  }

  const { chromium, executablePath } = await resolveBrowserRuntime()
  const baseHref = resolveRequestOrigin(event)
  const htmlWithBaseHref = injectBaseHrefIfMissing(html, baseHref)
  const browser = await chromium.launch({
    executablePath,
    headless: true,
    args: ['--no-sandbox', '--disable-setuid-sandbox']
  })

  try {
    const page = await browser.newPage({
      viewport: {
        width: 1120,
        height: 900
      }
    })

    await page.setContent(htmlWithBaseHref, { waitUntil: 'load' })
    await page.emulateMedia({ media: 'print' })

    try {
      await page.waitForFunction(() => (window as Window & { __printReady?: boolean }).__printReady === true, {
        timeout: 15000
      })
    } catch {
      await page.waitForTimeout(1200)
    }

    const pdfBuffer = await page.pdf({
      format: 'A4',
      printBackground: true,
      preferCSSPageSize: true,
      displayHeaderFooter: false
    })

    const fileBaseName = sanitizeDownloadBaseName(body?.fileName || body?.title || 'document')
    const unicodeFileName = `${fileBaseName}.pdf`
    const asciiFileName = `${toAsciiDownloadFileName(fileBaseName)}.pdf`

    setHeader(event, 'Content-Type', 'application/pdf')
    setHeader(
      event,
      'Content-Disposition',
      `attachment; filename="${asciiFileName}"; filename*=UTF-8''${encodeContentDispositionFileName(unicodeFileName)}`
    )
    setHeader(event, 'Cache-Control', 'no-store')

    return pdfBuffer
  } finally {
    await browser.close()
  }
})
