/**
 * Extrait le pictogramme depuis ultimail-original.png :
 * - trim alpha (autocrop), carré transparent + padding symétrique, puis export
 * - renfort S/L sur violet coin haut-droit (fonds sombres)
 * - public/brand/ultimail-header-icon.png (288px)
 * - app/icon.png (32), app/apple-icon.png (180)
 *
 * pnpm run brand:header-icon
 */
import { existsSync, copyFileSync, mkdirSync } from "node:fs"
import { dirname, join } from "node:path"
import { fileURLToPath } from "node:url"
import sharp from "sharp"

const __dirname = dirname(fileURLToPath(import.meta.url))
const root = join(__dirname, "..")
const brandDir = join(root, "public/brand")
const appDir = join(root, "app")

/** Fond UI (--app-canvas) + blanc autour du master (strict : évite de manger le bas du picto gris clair). */
function isBackgroundPixel(r, g, b) {
  if (r >= 249 && g >= 249 && b >= 249) return true
  if (Math.abs(r - 250) <= 5 && Math.abs(g - 251) <= 5 && Math.abs(b - 252) <= 5) return true
  const avg = (r + g + b) / 3
  const spread = Math.max(r, g, b) - Math.min(r, g, b)
  return spread < 10 && avg > 246
}

/**
 * @param {Buffer} rgbaRaw
 * @param {number} w
 * @param {number} h
 */
function knockOutBackground(rgbaRaw, w, h) {
  const out = Buffer.from(rgbaRaw)
  const stride = 4
  for (let y = 0; y < h; y++) {
    for (let x = 0; x < w; x++) {
      const i = (y * w + x) * stride
      const r = out[i]
      const g = out[i + 1]
      const b = out[i + 2]
      if (isBackgroundPixel(r, g, b)) out[i + 3] = 0
    }
  }
  return out
}

function rgbToHsl(r255, g255, b255) {
  const r = r255 / 255
  const g = g255 / 255
  const b = b255 / 255
  const max = Math.max(r, g, b)
  const min = Math.min(r, g, b)
  const l = (max + min) / 2
  let h = 0
  let s = 0
  if (max !== min) {
    const d = max - min
    s = l > 0.5 ? d / (2 - max - min) : d / (max + min)
    switch (max) {
      case r:
        h = ((g - b) / d + (g < b ? 6 : 0)) / 6
        break
      case g:
        h = ((b - r) / d + 2) / 6
        break
      default:
        h = ((r - g) / d + 4) / 6
        break
    }
  }
  return { h: h * 360, s, l }
}

function hslToRgb(hDeg, s, l) {
  let h = hDeg / 360
  let r
  let g
  let b
  if (s === 0) {
    r = g = b = l
  } else {
    const hue2rgb = (p, q, t) => {
      let tt = t
      if (tt < 0) tt += 1
      if (tt > 1) tt -= 1
      if (tt < 1 / 6) return p + (q - p) * 6 * tt
      if (tt < 1 / 2) return q
      if (tt < 2 / 3) return p + (q - p) * (2 / 3 - tt) * 6
      return p
    }
    const q = l < 0.5 ? l * (1 + s) : l + s - l * s
    const p = 2 * l - q
    r = hue2rgb(p, q, h + 1 / 3)
    g = hue2rgb(p, q, h)
    b = hue2rgb(p, q, h - 1 / 3)
  }
  return {
    r: Math.round(Math.min(255, Math.max(0, r * 255))),
    g: Math.round(Math.min(255, Math.max(0, g * 255))),
    b: Math.round(Math.min(255, Math.max(0, b * 255))),
  }
}

/**
 * Accentue le coin haut-droit violet (lisibilité sur fond sombre).
 * @param {Buffer} rgbaRaw
 * @param {number} w
 * @param {number} h
 */
function boostPurpleTopRight(rgbaRaw, w, h) {
  const stride = 4
  const xMin = Math.floor(w * 0.44)
  const yMax = Math.floor(h * 0.44)
  for (let y = 0; y <= yMax; y++) {
    for (let x = xMin; x < w; x++) {
      const i = (y * w + x) * stride
      if (rgbaRaw[i + 3] < 28) continue
      const r = rgbaRaw[i]
      const g = rgbaRaw[i + 1]
      const b = rgbaRaw[i + 2]
      const { h: hue, s, l } = rgbToHsl(r, g, b)
      if (hue < 252 || hue > 322 || s < 0.05) continue
      const s2 = Math.min(1, s * 1.42 + 0.12)
      const l2 = Math.min(0.97, l * 1.22 + 0.09)
      const { r: r2, g: g2, b: b2 } = hslToRgb(hue, s2, l2)
      rgbaRaw[i] = r2
      rgbaRaw[i + 1] = g2
      rgbaRaw[i + 2] = b2
    }
  }
}

function findSourcePng() {
  const a = join(brandDir, "ultimail-original.png")
  const b = join(brandDir, "utlimail-original.png")
  if (existsSync(a)) return a
  if (existsSync(b)) return b
  throw new Error("Missing ultimail-original.png or utlimail-original.png in public/brand/")
}

/** Après trim : bbox du picto → carré transparent, picto centré + padding symétrique (évite off-center UI). */
async function padToCenteredSquare(trimmedPng) {
  const tMeta = await sharp(trimmedPng).metadata()
  const tw = tMeta.width ?? 0
  const th = tMeta.height ?? 0
  if (!tw || !th) throw new Error("trim produced empty image")

  const base = Math.max(tw, th)
  const padding = Math.max(8, Math.round(base * 0.055))
  const sq = base + padding * 2
  const left = Math.floor((sq - tw) / 2)
  const top = Math.floor((sq - th) / 2)

  return sharp({
    create: {
      width: sq,
      height: sq,
      channels: 4,
      background: { r: 0, g: 0, b: 0, alpha: 0 },
    },
  })
    .composite([{ input: trimmedPng, left, top }])
    .png({ compressionLevel: 9, effort: 9 })
    .toBuffer()
}

async function main() {
  mkdirSync(brandDir, { recursive: true })
  mkdirSync(appDir, { recursive: true })

  const src = findSourcePng()
  const canonical = join(brandDir, "ultimail-original.png")
  if (src !== canonical) copyFileSync(src, canonical)

  const base = sharp(canonical)
  const meta = await base.metadata()
  const W = meta.width ?? 0
  const H = meta.height ?? 0
  // Bande ~62 % : picto un peu plus bas dans le master, besoin marge sous le glyphe.
  const splitY = Math.min(H - 32, Math.max(80, Math.round(H * 0.62)))
  const topBuf = await base.clone().extract({ left: 0, top: 0, width: W, height: splitY }).png().toBuffer()
  const side = Math.min(W, splitY)
  const left = Math.max(0, Math.floor((W - side) / 2))
  // Toujours haut de bandeau : si W < splitY, un centrage vertical coupait le haut du picto.
  const topCrop = 0

  const canvas = { r: 250, g: 251, b: 252, alpha: 1 }
  const inner = 640
  const { data, info } = await sharp(topBuf)
    .extract({ left, top: topCrop, width: side, height: side })
    .resize(inner, inner, {
      fit: "contain",
      position: "north",
      background: canvas,
    })
    .ensureAlpha()
    .raw()
    .toBuffer({ resolveWithObject: true })

  if (info.channels !== 4) throw new Error("expected RGBA from ensureAlpha")

  const cleared = knockOutBackground(data, info.width, info.height)
  boostPurpleTopRight(cleared, info.width, info.height)
  const trimmedPng = await sharp(Buffer.from(cleared), {
    raw: { width: info.width, height: info.height, channels: 4 },
  })
    .trim({ threshold: 8 })
    .png({ compressionLevel: 9, effort: 9 })
    .toBuffer()

  const squared = await padToCenteredSquare(trimmedPng)

  const resizeOpts = { fit: "contain", position: "centre", background: { r: 0, g: 0, b: 0, alpha: 0 } }
  const headerOut = join(brandDir, "ultimail-header-icon.png")
  await sharp(squared).resize(288, 288, resizeOpts).png({ compressionLevel: 9, effort: 9 }).toFile(headerOut)
  console.log("Wrote", headerOut)

  const fav32 = join(appDir, "icon.png")
  await sharp(squared).resize(32, 32, resizeOpts).png({ compressionLevel: 9 }).toFile(fav32)
  console.log("Wrote", fav32)

  const apple = join(appDir, "apple-icon.png")
  await sharp(squared).resize(180, 180, resizeOpts).png({ compressionLevel: 9 }).toFile(apple)
  console.log("Wrote", apple)
}

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