#!/usr/bin/env zsh
set -euo pipefail

campaign="${1:-}"
page="${2:-}"
desktop_url="${3:-${DESKTOP_URL:-}}"
mobile_url="${4:-${MOBILE_URL:-}}"

if [[ -z "$campaign" || -z "$page" ]]; then
  echo "usage: scripts/export-image-fills.v0.1.3.zsh <campaign> <page> [desktop_url] [mobile_url]"
  exit 1
fi

if [[ -z "${FIGMA_TOKEN:-}" ]]; then
  echo "ERROR Missing FIGMA_TOKEN"
  exit 1
fi

# STREAM_PATH_NOTE: "awareness" is the hardcoded default stream name.
preflight_json="campaigns/content/$campaign/awareness/_sologen/json/preflight.json"
pf_filekey=""
if [[ -z "$desktop_url" || -z "$mobile_url" ]]; then
  if [[ -s "$preflight_json" ]]; then
    pf_pair="$(node -e '
const fs=require("fs");
const path=process.argv[1];
const page=process.argv[2];
try{
  const j=JSON.parse(fs.readFileSync(path,"utf8"));
  const screens=Array.isArray(j?.screens)?j.screens:[];
  const hit=screens.find(s=>String(s?.id||"")===String(page));
  if(!hit){ process.exit(0); }
  process.stdout.write((hit.desktopUrl||"")+"\t"+(hit.mobileUrl||"")+"\t"+(j?.fileKey||""));
}catch(_e){}
' "$preflight_json" "$page")"
    if [[ -n "$pf_pair" ]]; then
      pf_desktop="${pf_pair%%$'\t'*}"
      rest="${pf_pair#*$'\t'}"
      pf_mobile="${rest%%$'\t'*}"
      pf_filekey="${rest#*$'\t'}"
      [[ -z "$desktop_url" ]] && desktop_url="$pf_desktop"
      [[ -z "$mobile_url" ]] && mobile_url="$pf_mobile"
    fi
  fi
fi

if [[ -z "$desktop_url" && -z "$mobile_url" ]]; then
  echo "ERROR Missing file key. Provide DESKTOP_URL/MOBILE_URL env vars or pass desktop_url/mobile_url as args."
  exit 1
fi

extract_file_key(){ printf '%s' "$1" | sed -En 's#^(https?://|figma://)[^/]+/(file|design|proto)/([A-Za-z0-9_-]+).*#\3#p' | head -n1; }

filekey=""
if [[ -n "$desktop_url" ]]; then filekey="$(extract_file_key "$desktop_url")"; fi
if [[ -z "$filekey" && -n "$mobile_url" ]]; then filekey="$(extract_file_key "$mobile_url")"; fi
if [[ -z "$filekey" && -n "$pf_filekey" ]]; then filekey="$pf_filekey"; fi
if [[ -z "$filekey" && -n "${FIGMA_FILE_KEY:-}" ]]; then filekey="${FIGMA_FILE_KEY}"; fi

if [[ -z "$filekey" ]]; then
  echo "ERROR Could not resolve file key from URLs/preflight. Set FIGMA_FILE_KEY or export preflight with fileKey."
  exit 2
fi

# STREAM_PATH_NOTE: "awareness" is the hardcoded default stream name.
# If multi-stream support is added, replace "awareness" with the stream name
# from preflight.json (same as resolveStreamFolderName in the generator).
campDir="campaigns/content/$campaign/awareness"
deskJson="$campDir/_sologen/json/$page/desktop.json"
mobJson="$campDir/_sologen/json/$page/mobile.json"
outDir="$campDir/images"
repDir="$campDir/_sologen/reports"

mkdir -p "$outDir" "$repDir"

# Load friendly asset name map produced by generate-asset-names.mjs (if present).
# Defines FILLNAME_<ref> and ASSETNAME_<safeId> variables used below.
asset_names_sh="campaigns/content/${campaign}/awareness/_sologen/json/asset-names.sh"
[[ -f "$asset_names_sh" ]] && source "$asset_names_sh"

if [[ ! -s "$deskJson" ]]; then echo "ERROR Missing or empty: $deskJson"; exit 3; fi
if [[ ! -s "$mobJson" ]]; then echo "ERROR Missing or empty: $mobJson"; exit 3; fi

tmpRefs="$(mktemp)"
tmpPairs="$(mktemp)"
tmpExportableIds="$(mktemp)"
tmpExportablePairs="$(mktemp)"

cleanup() {
  rm -f "$tmpRefs" "$tmpPairs" "$tmpExportableIds" "$tmpExportablePairs"
}
trap cleanup EXIT

# Collect IMAGE paint refs from fills[] only.
# IMPORTANT: Skip any node that is an explicit exportable (exportSettings),
# and skip its entire subtree, because exportables are handled by
# scripts/export-exportables.* and we don't want duplicates.
node -e '
const fs=require("fs");

const args = process.argv.slice(1).filter(Boolean);
if(args.length<2){ console.error("NODE_ERROR: expected 2 json paths"); process.exit(10); }

function readJson(p){ return JSON.parse(fs.readFileSync(p,"utf8")); }

function loadDoc(j){
  if (j && j.document) return j.document;
  if (j && j.nodes && typeof j.nodes==="object") {
    const keys = Object.keys(j.nodes);
    if (keys.length) {
      const n = j.nodes[keys[0]];
      if (n && n.document) return n.document;
    }
  }
  return null;
}

function isAssetExportable(n){
  const hasExport = Array.isArray(n?.exportSettings) && n.exportSettings.length > 0;
  if(!hasExport) return false;
  const t = String(n?.type || "").toUpperCase();
  return t !== "FRAME" && t !== "CANVAS" && t !== "SECTION";
}

function walk(n, fn, ancestorExportable){
  if(!n || typeof n!=="object") return;
  const selfExportable = isAssetExportable(n);
  const skip = ancestorExportable || selfExportable;
  fn(n, { ancestorExportable, selfExportable, skip });
  if(skip) return; // skip subtree under exportables
  const kids = Array.isArray(n.children) ? n.children : [];
  for(const k of kids) walk(k, fn, skip);
}

function collectRefs(doc, out){
  if(!doc) return;
  const scan = (paints)=>{
    if(!Array.isArray(paints)) return;
    for(const p of paints){
      if(p && p.type==="IMAGE" && p.imageRef) out.add(String(p.imageRef));
    }
  };
  walk(doc, (node, ctx)=>{
    if(ctx.ancestorExportable) return;
    scan(node.fills);
  }, false);
}

const dRaw=readJson(args[0]);
const mRaw=readJson(args[1]);

const dDoc=loadDoc(dRaw);
const mDoc=loadDoc(mRaw);

if(!dDoc || !mDoc){
  console.error("NODE_ERROR: could not find .document (or nodes[*].document) in one of the JSON files");
  process.exit(11);
}

const refs=new Set();
collectRefs(dDoc, refs);
collectRefs(mDoc, refs);

for(const r of [...refs].sort()) process.stdout.write(r+"\n");
' "$deskJson" "$mobJson" > "$tmpRefs"

refCount="$(wc -l < "$tmpRefs" | tr -d ' ')"
if [[ "$refCount" == "0" ]]; then
  echo "No IMAGE fills found in $deskJson / $mobJson"
  rm -f "$tmpRefs" "$tmpPairs"
  exit 0
fi

fillsJson="$repDir/image-fills.$campaign.$page.json"
curl -sS -H "X-Figma-Token: $FIGMA_TOKEN" "https://api.figma.com/v1/files/$filekey/images" -o "$fillsJson"

node -e '
const fs=require("fs");

const fillsPath = process.argv[1];
const refsPath  = process.argv[2];
if(!fillsPath || !refsPath){
  console.error("NODE_ERROR: expected fills.json path and refs.txt path");
  process.exit(20);
}

const fills=JSON.parse(fs.readFileSync(fillsPath,"utf8"));
const refs=fs.readFileSync(refsPath,"utf8").split(/\r?\n/).map(s=>s.trim()).filter(Boolean);
const map = (fills && fills.meta && fills.meta.images) ? fills.meta.images : {};

for(const r of refs){
  const url = map[r];
  if(url) process.stdout.write(r+"\t"+url+"\n");
}
' "$fillsJson" "$tmpRefs" > "$tmpPairs"

pairCount="$(wc -l < "$tmpPairs" | tr -d ' ')"
if [[ "$pairCount" == "0" ]]; then
  echo "ERROR No downloadable URLs returned from Figma for the imageRefs we found."
  echo "See: $fillsJson"
  exit 4
fi

manifest="$repDir/image-fills.manifest.$campaign.$page.json"
node -e '
const fs=require("fs");

const pairsPath = process.argv[1];
const outPath   = process.argv[2];
if(!pairsPath || !outPath){
  console.error("NODE_ERROR: expected pairs.tsv path and manifest output path");
  process.exit(30);
}

const lines=fs.readFileSync(pairsPath,"utf8").split(/\r?\n/).filter(Boolean);
const out=[];
for(const ln of lines){
  const [ref,url]=ln.split("\t");
  if(ref && url) out.push({imageRef:ref,url});
}
fs.writeFileSync(outPath, JSON.stringify({count: out.length, images: out}, null, 2));
' "$tmpPairs" "$manifest"

while IFS=$'\t' read -r ref url; do
  [[ -n "$ref" && -n "$url" ]] || continue
  # Use friendly name from asset-names.sh if available; fall back to imageRef.png
  fillVarname="FILLNAME_${ref}"
  fillFilename="${(P)fillVarname:-${ref}.png}"
  curl -sSL -o "$outDir/$fillFilename" "$url"
done < "$tmpPairs"

# Also download explicit exportable frame/group/component PNGs so the renderer can
# prefer authored frame exports over reconstructed image-fill crops when available.
node -e '
const fs=require("fs");

const args = process.argv.slice(1).filter(Boolean);
if(args.length<2){ console.error("NODE_ERROR: expected 2 json paths"); process.exit(40); }

function readJson(p){ return JSON.parse(fs.readFileSync(p,"utf8")); }
function loadDoc(j){
  if (j && j.document) return j.document;
  if (j && j.nodes && typeof j.nodes==="object") {
    const keys = Object.keys(j.nodes);
    if (keys.length) {
      const n = j.nodes[keys[0]];
      if (n && n.document) return n.document;
    }
  }
  return null;
}
function hasRasterExportSettings(n){
  const ex = Array.isArray(n?.exportSettings) ? n.exportSettings : [];
  if(!ex.length) return false;
  return ex.some(s => {
    const fmt = String(s?.format || "").toUpperCase();
    return fmt === "PNG" || fmt === "JPG" || fmt === "JPEG";
  });
}
function walk(n, fn, ancestorExportable){
  if(!n || typeof n!=="object") return;
  const selfExportable = hasRasterExportSettings(n);
  fn(n, ancestorExportable, selfExportable);
  const kids = Array.isArray(n.children) ? n.children : [];
  for(const k of kids) walk(k, fn, ancestorExportable || selfExportable);
}
const rankById = new Map();
function collect(doc, rank){
  walk(doc, (n, ancestorExportable, selfExportable) => {
    if(!(selfExportable && n.id)) return;
    const id = String(n.id);
    const prev = rankById.get(id);
    if(prev == null || rank < prev) rankById.set(id, rank);
  }, false);
}
for(const p of args){
  const j = readJson(p);
  const isDesktop = /desktop\.json$/i.test(p);
  const rank = isDesktop ? 1 : 2;
  const doc = loadDoc(j);
  if(doc) collect(doc, rank);
}
[...rankById.entries()]
  .sort((a,b)=>a[0].localeCompare(b[0]))
  .forEach(([id]) => process.stdout.write(id + "\n"));
' "$deskJson" "$mobJson" > "$tmpExportableIds"

exportableCount="$(wc -l < "$tmpExportableIds" | tr -d ' ')"
if [[ "$exportableCount" != "0" ]]; then
  exportableCsv="$(paste -sd, "$tmpExportableIds")"
  exportablesJson="$repDir/exportable-pngs.$campaign.$page.json"
  curl -sS -H "X-Figma-Token: $FIGMA_TOKEN" \
    "https://api.figma.com/v1/images/$filekey?ids=$exportableCsv&format=png&scale=1" \
    -o "$exportablesJson"

  node -e '
const fs=require("fs");
const imgs = JSON.parse(fs.readFileSync(process.argv[1],"utf8"));
const map = (imgs && imgs.images) ? imgs.images : {};
for(const [id,url] of Object.entries(map)){
  if(id && url) process.stdout.write(id + "\t" + url + "\n");
}
' "$exportablesJson" > "$tmpExportablePairs"

  while IFS=$'\t' read -r id url; do
    [[ -n "$id" && -n "$url" ]] || continue
    safeId="${id//[:\/]/-}"
    # Use friendly name from asset-names.sh if available; fall back to safeId.png
    pngVarname="ASSETNAME_${id//[^a-zA-Z0-9]/_}"
    pngFilename="${(P)pngVarname:-${safeId}.png}"
    curl -sSL -o "$outDir/$pngFilename" "$url"
  done < "$tmpExportablePairs"

  exportableManifest="$repDir/exportable-pngs.manifest.$campaign.$page.json"
  node -e '
const fs=require("fs");
const lines=fs.readFileSync(process.argv[1],"utf8").split(/\r?\n/).filter(Boolean);
const out=[];
for(const ln of lines){
  const [id,url]=ln.split("\t");
  if(id && url) out.push({id,url});
}
fs.writeFileSync(process.argv[2], JSON.stringify({count: out.length, images: out}, null, 2));
' "$tmpExportablePairs" "$exportableManifest"
fi

echo "Downloaded $pairCount image-fill assets to $outDir"
echo "Manifest: $manifest"
echo "API response: $fillsJson"
if [[ "${exportableCount:-0}" != "0" ]]; then
  echo "Downloaded $exportableCount exportable frame PNGs to $outDir"
fi
