import { NextRequest, NextResponse } from 'next/server'
import { revalidatePath, revalidateTag } from 'next/cache'
import crypto from 'crypto'

export const runtime = 'nodejs'

const SECRET = process.env.REVALIDATE_SECRET || ''
const BLOG_CACHE_TAG = process.env.BLOG_CACHE_TAG || 'blog-content'
const BLOG_PATH = (process.env.BLOG_PATH || '').replace(/^\/+|\/+$/g, '')

/**
 * Compare strings in a timing-safe way
 */
function timingSafeEqual(a: string, b: string) {
  const aBuf = Buffer.from(a)
  const bBuf = Buffer.from(b)
  if (aBuf.length !== bBuf.length) return false
  return crypto.timingSafeEqual(aBuf, bBuf)
}

/**
 * Validate GitHub's HMAC signature header (X-Hub-Signature-256)
 */
function verifyGitHubSignature(rawBody: string, signatureHeader: string | null) {
  if (!SECRET || !signatureHeader) return false
  const expected = `sha256=${crypto.createHmac('sha256', SECRET).update(rawBody).digest('hex')}`
  return timingSafeEqual(expected, signatureHeader)
}

/**
 * Authorize request via:
 * - query param ?secret=
 * - header x-revalidate-secret
 * - GitHub webhook signature X-Hub-Signature-256
 */
function isAuthorized(req: NextRequest, rawBody?: string) {
  if (!SECRET) return false

  const urlSecret = req.nextUrl.searchParams.get('secret')
  if (urlSecret && timingSafeEqual(urlSecret, SECRET)) return true

  const headerSecret = req.headers.get('x-revalidate-secret')
  if (headerSecret && timingSafeEqual(headerSecret, SECRET)) return true

  const ghSig = req.headers.get('x-hub-signature-256')
  if (ghSig && rawBody && verifyGitHubSignature(rawBody, ghSig)) return true

  return false
}

function deriveSlugsFromChangedFiles(files: string[], blogPath: string) {
  const normalized = blogPath ? blogPath.replace(/^\/+|\/+$/g, '') : ''
  return files
    .filter((f) => f.endsWith('.mdx'))
    .filter((f) => {
      if (!normalized) return true
      // match if file is in configured BLOG_PATH
      const fNorm = f.replace(/^\/+|\/+$/g, '')
      return fNorm === normalized || fNorm.startsWith(`${normalized}/`)
    })
    .map((f) => {
      const name = f.split('/').pop() || f
      return name.replace(/\.mdx$/i, '')
    })
}

export async function GET(req: NextRequest) {
  if (!isAuthorized(req)) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
  }

  const paths = new Set<string>(['/blog'])
  const tag = req.nextUrl.searchParams.get('tag') || BLOG_CACHE_TAG

  const slug = req.nextUrl.searchParams.get('slug')
  if (slug) paths.add(`/blog/${slug}`)

  const pathParam = req.nextUrl.searchParams.get('path')
  if (pathParam) paths.add(pathParam)

  // Revalidate cache tag (refreshes GitHub fetches using next: { tags })
  revalidateTag(tag)
  // Revalidate listing and any provided paths
  for (const p of paths) revalidatePath(p)

  return NextResponse.json({
    revalidated: true,
    tag,
    paths: Array.from(paths),
    mode: 'GET',
  })
}

function isRecord(value: unknown): value is Record<string, unknown> {
  return typeof value === 'object' && value !== null && !Array.isArray(value)
}

function getChangedFilesFromGitHubPush(body: unknown): string[] {
  if (!isRecord(body) || !Array.isArray((body as Record<string, unknown>).commits)) return []
  const files: string[] = []
  const commits = (body as Record<string, unknown>).commits as unknown[]
  for (const c of commits) {
    if (!isRecord(c)) continue
    for (const key of ['added', 'modified', 'removed'] as const) {
      const arr = (c as Record<string, unknown>)[key]
      if (Array.isArray(arr)) {
        for (const f of arr) {
          if (typeof f === 'string') files.push(f)
        }
      }
    }
  }
  return files
}

export async function POST(req: NextRequest) {
  const raw = await req.text()

  if (!isAuthorized(req, raw)) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
  }

  let body: unknown = {}
  try {
    body = raw ? JSON.parse(raw) : {}
  } catch {
    // ignore parse errors for signature validation; continue with empty body
  }

  const paths = new Set<string>(['/blog'])
  const tag = BLOG_CACHE_TAG

  // Direct payload usage
  if (isRecord(body) && typeof body.path === 'string') paths.add(body.path)
  if (isRecord(body) && typeof body.slug === 'string') paths.add(`/blog/${body.slug}`)
  if (isRecord(body) && Array.isArray(body.paths)) {
    for (const p of body.paths as unknown[]) if (typeof p === 'string') paths.add(p)
  }
  if (isRecord(body) && Array.isArray(body.slugs)) {
    for (const s of body.slugs as unknown[]) if (typeof s === 'string') paths.add(`/blog/${s}`)
  }

  // GitHub webhook (push) payload
  const ghEvent = req.headers.get('x-github-event')
  if (ghEvent === 'push') {
    const changed = getChangedFilesFromGitHubPush(body)
    const slugs = deriveSlugsFromChangedFiles(changed, BLOG_PATH)
    for (const s of slugs) paths.add(`/blog/${s}`)
  }

  revalidateTag(tag)
  for (const p of paths) revalidatePath(p)

  return NextResponse.json({
    revalidated: true,
    tag,
    paths: Array.from(paths),
    mode: 'POST',
  })
}
