import { chromium, type BrowserContextOptions } from 'playwright'
import {
  isNumericMediaId,
  mediaIdFromThreadObject,
  postShortcode,
  shortcodeFromPermalink,
} from './threads-media-id'

const USER_AGENT =
  'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'

function walkJson(data: unknown, visit: (obj: Record<string, unknown>) => void): void {
  if (!data || typeof data !== 'object') return
  if (Array.isArray(data)) {
    for (const item of data) walkJson(item, visit)
    return
  }
  const obj = data as Record<string, unknown>
  visit(obj)
  for (const value of Object.values(obj)) {
    if (value && typeof value === 'object') walkJson(value, visit)
  }
}

function findMediaIdForShortcode(data: unknown, shortcode: string): string | undefined {
  const want = shortcode.trim().toLowerCase()
  if (!want) return undefined
  let found: string | undefined
  walkJson(data, (obj) => {
    if (found) return
    const code = postShortcode(obj)
    if (!code || code.toLowerCase() !== want) return
    const mediaId = mediaIdFromThreadObject(obj)
    if (mediaId) found = mediaId
  })
  return found
}

/** Open a Threads permalink and extract the numeric media id (pk) for API replies. */
export async function resolveMediaIdFromPermalink(
  permalink: string,
  storageState?: string,
): Promise<string | undefined> {
  const link = permalink.trim()
  const shortcode = shortcodeFromPermalink(link)
  if (!link || !shortcode) return undefined

  const browser = await chromium.launch({
    headless: process.env.PLAYWRIGHT_HEADLESS !== 'false',
    args: ['--disable-blink-features=AutomationControlled', '--no-first-run', '--no-default-browser-check'],
  })

  try {
    const contextOptions: BrowserContextOptions = {
      userAgent: USER_AGENT,
      viewport: { width: 1280, height: 900 },
      locale: 'zh-TW',
      timezoneId: 'Asia/Taipei',
    }
    const trimmedState = storageState?.trim() ?? ''
    if (trimmedState) {
      contextOptions.storageState = JSON.parse(trimmedState) as BrowserContextOptions['storageState']
    }

    const context = await browser.newContext(contextOptions)
    const page = await context.newPage()
    const payloads: unknown[] = []

    page.on('response', async (response) => {
      const url = response.url()
      if (!url.includes('graphql') && !url.includes('threads') && !url.includes('instagram')) return
      try {
        const contentType = response.headers()['content-type'] ?? ''
        if (!contentType.includes('json')) return
        payloads.push(await response.json())
      } catch {
        // ignore
      }
    })

    await page.goto(link, { waitUntil: 'domcontentloaded', timeout: 45_000 })
    await page.waitForTimeout(1500)

    for (const payload of payloads) {
      const mediaId = findMediaIdForShortcode(payload, shortcode)
      if (isNumericMediaId(mediaId)) return mediaId
    }

    const scripts = await page.locator('script[type="application/json"][data-sjs]').all()
    for (const script of scripts) {
      try {
        const raw = await script.textContent()
        if (!raw) continue
        const json = JSON.parse(raw)
        const mediaId = findMediaIdForShortcode(json, shortcode)
        if (isNumericMediaId(mediaId)) return mediaId
      } catch {
        // skip malformed script
      }
    }

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