import type { APIRoute } from 'astro'
import { Resvg } from '@resvg/resvg-js'
import { SITE } from '@/consts'
import { fetchRemotePost, normalizePost } from '@/lib/data-utils'
import { formatDate } from '@/lib/utils'

const WIDTH = 1200
const HEIGHT = 630
const PADDING_X = 80

const FONT_STACK =
  'Geist, "Inter", "Noto Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif'

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

function truncateChars(input: string, maxChars: number) {
  const chars = Array.from(input)
  if (chars.length <= maxChars) return input
  if (maxChars <= 3) return chars.slice(0, maxChars).join('')
  return chars.slice(0, maxChars - 3).join('') + '...'
}

function wrapText(input: string, maxChars: number, maxLines: number) {
  const text = input.replace(/\s+/g, ' ').trim()
  if (!text) return []
  const chars = Array.from(text)
  const lines: string[] = []
  let current: string[] = []

  for (const ch of chars) {
    if (current.length >= maxChars) {
      lines.push(current.join(''))
      current = []
      if (lines.length >= maxLines) break
    }
    current.push(ch)
  }

  if (lines.length < maxLines && current.length > 0) {
    lines.push(current.join(''))
  }

  const maxTotal = maxChars * maxLines
  if (chars.length > maxTotal && lines.length > 0) {
    lines[lines.length - 1] = truncateChars(
      lines[lines.length - 1],
      maxChars,
    )
  }

  return lines
}

function buildSvg({
  title,
  description,
  meta,
  siteHost,
}: {
  title: string
  description: string
  meta: string
  siteHost: string
}) {
  const titleLines = wrapText(title, 24, 2)
  const descriptionLines = wrapText(description, 44, 3)
  const titleLineHeight = 64
  const descLineHeight = 34
  const titleY = 220
  const descY =
    titleY + titleLines.length * titleLineHeight + (descriptionLines.length ? 18 : 0)
  const metaY = HEIGHT - 72

  const titleSpans = titleLines
    .map(
      (line, index) =>
        `<tspan x="${PADDING_X}" dy="${
          index === 0 ? 0 : titleLineHeight
        }">${escapeXml(line)}</tspan>`,
    )
    .join('')
  const descSpans = descriptionLines
    .map(
      (line, index) =>
        `<tspan x="${PADDING_X}" dy="${
          index === 0 ? 0 : descLineHeight
        }">${escapeXml(line)}</tspan>`,
    )
    .join('')

  return `<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="${WIDTH}" height="${HEIGHT}" viewBox="0 0 ${WIDTH} ${HEIGHT}" role="img" aria-label="${escapeXml(title)}">
  <defs>
    <linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
      <stop offset="0%" stop-color="#0b1020" />
      <stop offset="100%" stop-color="#101a2b" />
    </linearGradient>
    <linearGradient id="accent" x1="0" y1="0" x2="1" y2="1">
      <stop offset="0%" stop-color="#22d3ee" stop-opacity="0.5" />
      <stop offset="100%" stop-color="#38bdf8" stop-opacity="0.2" />
    </linearGradient>
  </defs>
  <style>
    .bg { fill: url(#bg); }
    .eyebrow { font: 600 22px ${FONT_STACK}; letter-spacing: 0.08em; text-transform: uppercase; fill: rgba(248, 250, 252, 0.7); }
    .title { font: 700 56px ${FONT_STACK}; fill: #f8fafc; }
    .desc { font: 400 28px ${FONT_STACK}; fill: rgba(226, 232, 240, 0.85); }
    .meta { font: 500 24px ${FONT_STACK}; fill: rgba(226, 232, 240, 0.8); }
  </style>
  <rect class="bg" width="${WIDTH}" height="${HEIGHT}" rx="32" />
  <circle cx="${WIDTH - 120}" cy="120" r="140" fill="url(#accent)" />
  <circle cx="${WIDTH - 40}" cy="${HEIGHT - 80}" r="120" fill="url(#accent)" />

  <text class="eyebrow" x="${PADDING_X}" y="96">${escapeXml(siteHost)}</text>
  <text class="title" x="${PADDING_X}" y="${titleY}">
    ${titleSpans}
  </text>
  ${
    descriptionLines.length
      ? `<text class="desc" x="${PADDING_X}" y="${descY}">
    ${descSpans}
  </text>`
      : ''
  }
  <text class="meta" x="${PADDING_X}" y="${metaY}">${escapeXml(meta)}</text>
</svg>`
}

export const GET: APIRoute = async ({ params }) => {
  let slug = params.slug ?? ''
  if (slug) {
    try {
      slug = decodeURIComponent(slug)
    } catch {
      // Keep the raw slug if decoding fails.
    }
  }
  const remotePost = slug ? await fetchRemotePost(slug) : null
  const post = remotePost ? normalizePost(remotePost.post) : null
  const title = post?.data?.title || SITE.title
  const description = post?.data?.description || SITE.description
  const dateValue = post?.data?.date ?? null
  const meta = dateValue
    ? `${SITE.title} | ${formatDate(dateValue)}`
    : SITE.title
  const siteHost = new URL(SITE.href).host

  const svg = buildSvg({
    title,
    description,
    meta,
    siteHost,
  })

  const png = new Resvg(svg, {
    fitTo: {
      mode: 'width',
      value: WIDTH,
    },
  })
    .render()
    .asPng()

  return new Response(png, {
    status: 200,
    headers: {
      'Content-Type': 'image/png',
      'Cache-Control':
        'public, max-age=0, s-maxage=86400, stale-while-revalidate=604800',
    },
  })
}
