#!/usr/bin/env node
// Regenerate the Expo-managed mobile brand assets from the shared brain path.
//
// Outputs the 4 PNGs that app.json references: icon, adaptive-icon,
// splash-icon, favicon. The native ios/ folder is gitignored and gets
// regenerated by `expo prebuild` on every EAS build, so we don't touch it
// here — app.json + these four PNGs are the source of truth.
//
// Uses the sharp from docs/node_modules so we don't add a native dep to
// the root. Run from the repo root:
//
//   node apps/mobile/scripts/generate-brand-assets.mjs
//
// Brain icon by Sergey Patutin / The Noun Project (CC BY 3.0).

import { createRequire } from "node:module";
import { fileURLToPath } from "node:url";
import path from "node:path";

import {
  BRAIN_PATH_D,
  BRAIN_VIEWBOX,
  BRAIN_GROUP_TRANSFORM,
} from "../../www/src/lib/brain-path.mjs";

const require = createRequire(import.meta.url);
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
const repoRoot = path.resolve(scriptDir, "../../..");
const sharp = require(path.join(repoRoot, "docs/node_modules/sharp"));

const expoAssets = path.join(repoRoot, "apps/mobile/assets");

const BG = "#070a0f";
const BRAND = "#38bdf8";

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

/**
 * Build an SVG that centers the brain inside a square canvas of `size`.
 *
 * `widthFillRatio` is the fraction of the CANVAS WIDTH the brain should
 * occupy — since the brain is wider than it is tall (aspect ~1.241:1),
 * fitting it to the full width is what makes it feel big in a square
 * tile. Height is derived from the brain aspect and centered vertically.
 *
 * @param {number}        size             canvas width/height in px
 * @param {number}        widthFillRatio   0..1, brain width / canvas width
 * @param {string|null}   bg               background color, or null for transparent
 * @param {number}        strokeWidth      stroke width in user-space units (0 = fill only).
 *                                         Useful for the iOS icon where the node-graph
 *                                         lines need extra weight to survive home-screen scale.
 */
function brainSvg(size, widthFillRatio, bg, strokeWidth = 0) {
  const [, , vbW, vbH] = BRAIN_VIEWBOX.split(" ").map(Number);
  const brainAspect = vbW / vbH;
  const brainW = Math.round(size * widthFillRatio);
  const brainH = Math.round(brainW / brainAspect);
  const offsetX = Math.round((size - brainW) / 2);
  const offsetY = Math.round((size - brainH) / 2);
  const bgRect = bg
    ? `<rect width="${size}" height="${size}" fill="${bg}"/>`
    : "";
  const strokeAttrs = strokeWidth > 0
    ? ` stroke="${BRAND}" stroke-width="${strokeWidth}" stroke-linejoin="round" stroke-linecap="round"`
    : "";
  return `<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 ${size} ${size}">
  ${bgRect}
  <svg x="${offsetX}" y="${offsetY}" width="${brainW}" height="${brainH}" viewBox="${BRAIN_VIEWBOX}" fill="${BRAND}">
    <g transform="${BRAIN_GROUP_TRANSFORM}">
      <path d="${BRAIN_PATH_D}"${strokeAttrs} />
    </g>
  </svg>
</svg>`;
}

async function renderPng(svg, outPath) {
  await sharp(Buffer.from(svg))
    .png({ compressionLevel: 9 })
    .toFile(outPath);
  console.log(`✓ ${path.relative(repoRoot, outPath)}`);
}

/**
 * Render the brain edge-to-edge on a non-square canvas that matches the
 * tight viewBox aspect ratio. No padding baked into the PNG — the <Image>
 * consumer just needs to pass the same aspect to avoid whitespace.
 */
function brainTightRectSvg(width, strokeWidth = 0) {
  const [, , vbW, vbH] = BRAIN_VIEWBOX.split(" ").map(Number);
  const height = Math.round(width * (vbH / vbW));
  const strokeAttrs = strokeWidth > 0
    ? ` stroke="${BRAND}" stroke-width="${strokeWidth}" stroke-linejoin="round" stroke-linecap="round"`
    : "";
  return {
    svg: `<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="${BRAIN_VIEWBOX}" fill="${BRAND}">
  <g transform="${BRAIN_GROUP_TRANSFORM}">
    <path d="${BRAIN_PATH_D}"${strokeAttrs} />
  </g>
</svg>`,
    width,
    height,
  };
}

// ---------------------------------------------------------------------------
// Expo managed assets (referenced from app.json)
// ---------------------------------------------------------------------------

// iOS app icon — MUST be opaque (Apple rejects transparent icons).
// Heavy stroke (1.6) so the node-graph reads at the home-screen scale.
// Brain fills 85% of the tile width — enough to dominate the icon
// while leaving a visible margin inside the iOS squircle mask.
await renderPng(
  brainSvg(1024, 0.85, BG, 1.6),
  path.join(expoAssets, "icon.png")
);

// Android adaptive icon — transparent foreground. Background color is
// set separately in app.json. Brain stays inside the 66% safe zone so
// aggressive masks (circle, squircle) don't clip it.
await renderPng(
  brainSvg(1024, 0.6, null),
  path.join(expoAssets, "adaptive-icon.png")
);

// Splash screen logo — transparent, centered small. Background is set
// by app.json splash.backgroundColor + the native colorset below.
await renderPng(
  brainSvg(1024, 0.42, null),
  path.join(expoAssets, "splash-icon.png")
);

// Expo web favicon (tiny). Transparent, brain near-full.
await renderPng(
  brainSvg(48, 0.95, null),
  path.join(expoAssets, "favicon.png")
);

// In-app logo used by sign-in / onboarding / sidebar — transparent,
// edge-to-edge on a non-square canvas that matches the brain's aspect
// ratio so <Image> consumers can pass the same aspect and get zero
// baked-in padding. Heavier stroke (0.7) so the node-graph reads bold
// at the smaller display sizes inline UI tends to use. Separate from
// icon.png because iOS app icons must be opaque, which gives them a
// boxed look when reused inline in the UI.
{
  const { svg } = brainTightRectSvg(512, 0.7);
  await renderPng(svg, path.join(expoAssets, "logo.png"));
}
