import { existsSync } from 'node:fs'
/**
 * POI 照片处理：产出主包缩略图 + 分包大图两层。
 *
 * 为什么分两层：微信小程序主包上限 2 MB。30 张 800×600 照片约 2.6 MB，
 * 单独就超限。所以卡片场景（最大 192rpx ≈ 288 px）用小图放主包，
 * 详情页 hero（750×422rpx ≈ 1125×633 px）用大图放分包，随详情页按需下载。
 *
 * 两个产出目录都是提交进仓库的构建产物：
 *   src/pages-poi/static/photos/<poiId>.jpg   800×600  分包大图（详情页 hero）
 *   src/static/poi/photos/<poiId>.jpg         400×300  主包缩略图（各处卡片）
 *
 * 新增一张照片：
 *   1. 原图放入 src/photo-intake/（该目录不提交），命名为 <poiId>.jpg
 *      或用下方 NAME_TO_POI_ID 表里的中文名，例如「虹桥公园.jpg」
 *   2. 跑 `node scripts/process-poi-photos.mjs`
 *   3. 把 poiId 加入 src/data/poi/photos.ts 的 POI_COVER_PHOTOS_READY
 *
 * 不带 intake 直接跑：从分包大图重新生成全部主包缩略图（幂等）。
 */
import { mkdir, readdir, stat } from 'node:fs/promises'
import { dirname, join } from 'node:path'
import process from 'node:process'
import { fileURLToPath } from 'node:url'
import sharp from 'sharp'

const __dirname = dirname(fileURLToPath(import.meta.url))
const projectRoot = join(__dirname, '..')

/** 原图投放目录，不提交仓库 */
const intakeDir = join(projectRoot, 'src/photo-intake')
/** 分包大图：详情页 hero 使用，随分包按需下载 */
const fullDir = join(projectRoot, 'src/pages-poi/static/photos')
/** 主包缩略图：地图、行程、打卡等卡片使用 */
const thumbDir = join(projectRoot, 'src/static/poi/photos')

/** 分包大图规格。q66 是在 2 MB 分包上限内能给到 hero 的最高质量 */
const FULL = { width: 800, height: 600, quality: 66 }
/** 主包缩略图规格。400×300 覆盖最大卡片 192rpx（≈288 px @DPR3） */
const THUMB = { width: 400, height: 300, quality: 75 }

/** 微信包体上限 */
const MAIN_PACKAGE_LIMIT_KB = 2048
const SUB_PACKAGE_LIMIT_KB = 2048

// 中文名 → POI ID 映射（30 个景点）
const NAME_TO_POI_ID = {
  // 文化场馆
  文化艺术中心: 'poi_gm_culture_center',
  光明区图书馆: 'poi_amap_b0hbtu4bfr',
  光明区文化馆: 'poi_amap_b0ffk306gx',
  少年儿童图书馆: 'poi_amap_b02f38meio',
  深圳国际美术馆: 'poi_amap_b0jun773j3',
  城市规划馆: 'poi_amap_b0jbbzx4oo',
  茅洲河图书馆: 'poi_amap_b0k025w32e',
  // 公园湿地
  虹桥公园: 'poi_hongqiao_park',
  红花山公园: 'poi_honghuashan_park',
  科学公园: 'poi_amap_b0j2jc4n0w',
  鹅颈水湿地公园: 'poi_amap_b0ffmdhve1',
  明湖公园: 'poi_amap_b0fffwu1l5',
  光明新城公园: 'poi_amap_b0ffhfbsje',
  开明公园: 'poi_amap_b0ffjf1b20',
  左岸科技公园: 'poi_amap_b0g0kzrt5s',
  西田公园: 'poi_amap_b02f30abgs',
  大雁山森林公园: 'poi_amap_b0jgbp18uf',
  楼村湿地公园: 'poi_amap_b0grac6nzz',
  大顶岭山林公园: 'poi_amap_b02f309r88',
  // 田园休闲
  光明农场大观园: 'poi_farm_grand_view',
  华侨城光明欢乐田园: 'poi_happy_pastoral',
  双晖稻田农场: 'poi_amap_b0ffkkmzsc',
  // 人文地标
  弘源寺: 'poi_amap_b02f38nj9k',
  陈仙姑祠: 'poi_amap_b0ffgthbcf',
  黄氏大宗祠: 'poi_amap_b0fffttiov',
  玉律醒狮文化馆: 'poi_amap_b0kb4cxaby',
  // 绿道户外
  大顶岭绿道: 'poi_dadingling_greenway',
  光明湖碧道: 'poi_amap_b0ldbsbeea',
  茅洲河碧道: 'poi_amap_b0kup558j4',
  五指耙森林公园: 'poi_amap_b0jb2aurvw',
}

const IMAGE_RE = /\.(jpe?g|png|webp)$/i

/** 把源文件名解析成 poiId：优先当作已经是 poiId，否则查中文名映射 */
function resolvePoiId(fileName) {
  const base = fileName.replace(IMAGE_RE, '')
  if (base.startsWith('poi_'))
    return base
  return NAME_TO_POI_ID[base] ?? null
}

async function render(inputPath, outputPath, spec) {
  await sharp(inputPath)
    .rotate() // 按 EXIF 摆正
    .resize(spec.width, spec.height, {
      fit: 'cover',
      position: 'attention', // 智能裁剪，尽量保留主体
    })
    .jpeg({
      quality: spec.quality,
      mozjpeg: true,
      chromaSubsampling: '4:2:0',
    })
    .toFile(outputPath)
}

async function dirTotalKb(dir) {
  if (!existsSync(dir))
    return { count: 0, kb: 0 }
  const files = (await readdir(dir)).filter(f => f.endsWith('.jpg'))
  let bytes = 0
  for (const f of files)
    bytes += (await stat(join(dir, f))).size
  return { count: files.length, kb: Math.round(bytes / 1024) }
}

async function listIntake() {
  if (!existsSync(intakeDir))
    return []
  return (await readdir(intakeDir)).filter(f => IMAGE_RE.test(f))
}

async function main() {
  await mkdir(fullDir, { recursive: true })
  await mkdir(thumbDir, { recursive: true })

  const intake = await listIntake()
  const processed = new Set()

  // 1) 有 intake 原图的：同时生成大图和缩略图
  for (const file of intake) {
    const poiId = resolvePoiId(file)
    if (!poiId) {
      console.log(`SKIP  ${file} — 文件名既不是 poiId 也不在中文名映射表里`)
      continue
    }

    const inputPath = join(intakeDir, file)
    try {
      await render(inputPath, join(fullDir, `${poiId}.jpg`), FULL)
      await render(inputPath, join(thumbDir, `${poiId}.jpg`), THUMB)
      processed.add(poiId)
      console.log(`OK    ${file} → ${poiId}.jpg（大图 + 缩略图）`)
    }
    catch (error) {
      console.error(`FAIL  ${file}: ${error.message}`)
      process.exitCode = 1
    }
  }

  // 2) 其余已有大图的：从大图重新生成缩略图，保证两层始终同步
  const fullFiles = (await readdir(fullDir)).filter(f => f.endsWith('.jpg'))
  for (const file of fullFiles) {
    const poiId = file.replace(/\.jpg$/, '')
    if (processed.has(poiId))
      continue
    try {
      await render(join(fullDir, file), join(thumbDir, file), THUMB)
      console.log(`THUMB ${poiId}.jpg ← 由分包大图重新生成`)
    }
    catch (error) {
      console.error(`FAIL  ${file}: ${error.message}`)
      process.exitCode = 1
    }
  }

  // 3) 报告包体占用，超限直接失败
  const full = await dirTotalKb(fullDir)
  const thumb = await dirTotalKb(thumbDir)

  console.log('\n包体占用：')
  console.log(`  分包大图   ${String(full.count).padStart(2)} 张  ${String(full.kb).padStart(4)} KB  (分包上限 ${SUB_PACKAGE_LIMIT_KB} KB)`)
  console.log(`  主包缩略图 ${String(thumb.count).padStart(2)} 张  ${String(thumb.kb).padStart(4)} KB  (主包上限 ${MAIN_PACKAGE_LIMIT_KB} KB，还需容纳代码)`)

  if (full.kb >= SUB_PACKAGE_LIMIT_KB) {
    console.error(`\n分包大图已达 ${full.kb} KB，超过分包上限。请下调 FULL.quality 后重跑。`)
    process.exitCode = 1
  }
  if (thumb.kb >= MAIN_PACKAGE_LIMIT_KB) {
    console.error(`\n主包缩略图已达 ${thumb.kb} KB，超过主包上限。请下调 THUMB 规格后重跑。`)
    process.exitCode = 1
  }
}

main().catch((error) => {
  console.error(error)
  process.exitCode = 1
})
