/**
 * scripts/refresh-blog-images-rerun.js
 *
 * Second pass for the 4 slugs that didn't match, plus a re-pick for the
 * webcam slug which landed on a pollinator-research video (off-topic).
 *
 * Run:  node scripts/refresh-blog-images-rerun.js
 */

'use strict';

const https = require('https');
const fs    = require('fs');
const path  = require('path');
const { db } = require('../config/db');

const UA = 'takedowns-app/1.6 (https://takedowns.vesamuni.com; admin@takedowns.vesamuni.com)';

const TOPICS = [
  ['ncii-takedown-complete-dmca-workflow-guide-2026',                'cease desist copyright letter',                    ['text', 'template', 'form', 'seal', 'memo']],
  ['how-to-remove-non-consensual-intimate-images-survivor-guide',    'digital privacy vault security',                    ['intimate', 'nude']],
  ['ai-generated-ncii-deepfakes-legal-landscape-2026',               'machine learning neural network',                   ['face', 'nude', 'portrait', 'person']],
  ['chain-of-custody-digital-evidence-courts-accept',                'justice courthouse columns law',                    []],
  ['webcam-model-protection-chaturbate-stripchat-independent',       'broadcast television studio camera',                ['pollinator', 'flower', 'insect', 'wildlife']],
];

function get(url) {
  return new Promise((resolve, reject) => {
    https.get(url, { headers: { 'User-Agent': UA, 'Accept': 'application/json' } }, res => {
      let d = '';
      res.on('data', c => d += c);
      res.on('end', () => resolve({ status: res.statusCode, body: d }));
    }).on('error', reject);
  });
}

function download(url, dest) {
  return new Promise((resolve, reject) => {
    const file = fs.createWriteStream(dest);
    https.get(url, { headers: { 'User-Agent': UA } }, res => {
      if (res.statusCode !== 200) {
        file.close();
        return reject(new Error('HTTP ' + res.statusCode + ' for ' + url));
      }
      res.pipe(file);
      file.on('finish', () => file.close(() => resolve(dest)));
    }).on('error', err => {
      fs.unlink(dest, () => {});
      reject(err);
    });
  });
}

const FREE_RE = /public domain|^cc0|^cc[- ]by|^cc[- ]by[- ]sa|^cc[- ]by[- ]nc|^cc[- ]by[- ]nd/i;

function pickCandidate(candidates, blocked) {
  for (const c of candidates) {
    if (c.width < 800) continue;
    const ratio = c.width / c.height;
    if (ratio < 1.4 || ratio > 2.4) continue;
    const t = c.title.toLowerCase();
    if (blocked.some(b => t.includes(b))) continue;
    return c;
  }
  return null;
}

async function findCandidates(query, limit = 25) {
  const search = JSON.parse((await get(
    'https://commons.wikimedia.org/w/api.php?action=query&list=search&srsearch=' +
    encodeURIComponent(query) + '&srnamespace=6&format=json&srlimit=' + limit
  )).body);
  if (!search.query || !search.query.search.length) return [];
  const titles = search.query.search.map(r => r.title).join('|');
  const info = JSON.parse((await get(
    'https://commons.wikimedia.org/w/api.php?action=query&titles=' +
    encodeURIComponent(titles) +
    '&prop=imageinfo&iiprop=url|size|mime|extmetadata&iiurlwidth=1600&format=json'
  )).body);
  const pages = info.query.pages;
  const out = [];
  for (const id in pages) {
    const p = pages[id];
    if (!p.imageinfo) continue;
    const ii = p.imageinfo[0];
    const md = ii.extmetadata || {};
    const lic = (md.LicenseShortName?.value || '').trim();
    if (!FREE_RE.test(lic)) continue;
    out.push({
      title: p.title,
      url: ii.url,
      width: ii.width, height: ii.height,
      mime: ii.mime,
      license: lic,
      artist: (md.Artist?.value || '').replace(/<[^>]+>/g, '').slice(0, 120),
      sourceUrl: ii.descriptionurl,
    });
  }
  return out;
}

async function processTopic(slug, query, blocked) {
  const candidates = await findCandidates(query, 25);
  const pick = pickCandidate(candidates, blocked);
  if (!pick) {
    // Fall back: log top candidates so a human can pick
    console.log('-- no clean match for', slug, '(query: ' + query + '). Top candidates:');
    for (const c of candidates.slice(0, 6)) {
      console.log('   ', c.title, c.width + 'x' + c.height, c.license);
    }
    return { slug, status: 'no-match' };
  }
  const ext = (pick.mime && pick.mime.split('/')[1]) || (pick.url.split('.').pop().split('?')[0]) || 'jpg';
  const safeExt = ['jpg', 'jpeg', 'png', 'webp', 'gif'].includes(ext) ? ext : 'jpg';
  const localPath = path.join(__dirname, '..', 'public', 'og', 'blog', slug + '.' + safeExt);
  try {
    await download(pick.url, localPath);
    const stat = fs.statSync(localPath);
    if (stat.size < 5_000) throw new Error('too small: ' + stat.size);
    const relPath = '/public/og/blog/' + slug + '.' + safeExt;
    const attribution = (pick.artist ? pick.artist + ' / ' : '') + pick.license + ' — Wikimedia Commons';
    db.prepare('UPDATE blog_posts SET hero_image_url = ?, og_image_url = ?, updated_at = ? WHERE slug = ?')
      .run(relPath, relPath, Date.now(), slug);
    return {
      slug, status: 'ok',
      picked: pick.title, license: pick.license,
      size: pick.width + 'x' + pick.height,
      localFile: relPath, bytes: stat.size,
      attribution,
    };
  } catch (err) {
    return { slug, status: 'download-failed', error: err.message, pick: pick.title };
  }
}

(async () => {
  for (const [slug, q, blocked] of TOPICS) {
    try {
      const r = await processTopic(slug, q, blocked);
      console.log(JSON.stringify(r, null, 0));
    } catch (err) {
      console.log(JSON.stringify({ slug, status: 'error', error: err.message }));
    }
  }
})();
