/**
 * scripts/refresh-blog-images.js
 *
 * Replaces the current blog-post feature images with copyright-free
 * ones sourced from Wikimedia Commons. The user explicitly does not
 * want AI-generated art; they want CC-licensed / public-domain
 * photography that visually fits each post.
 *
 * For each blog post we:
 *   1. Search Commons for a relevant image.
 *   2. Filter to CC-licensed or public-domain, landscape, >= 800px wide.
 *   3. Skip images that look like they have a lot of overlaid text
 *      (we skip by filename keyword and small captions in the title).
 *   4. Download the original to public/og/blog/<slug>.<ext>.
 *   5. Update the blog_posts row with the new path and a short
 *      attribution snippet (artist, license, source URL).
 *
 * Run:  node scripts/refresh-blog-images.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',                'DMCA takedown notice document letter',             ['text', 'template', 'form']],
  ['how-to-remove-non-consensual-intimate-images-survivor-guide',    'padlock shield protection secure',                  ['intimate', 'nude']],
  ['onlyfans-dmca-protection-stop-leaks-at-scale',                   'microphone podcast creator studio',                 ['onlyfans']],
  ['fansly-leak-detection-dmca-takedown-creator-workflow',           'camera lens recording studio',                      ['fansly', 'nude']],
  ['ai-generated-ncii-deepfakes-legal-landscape-2026',               'artificial intelligence robot circuit',             ['deepfake', 'nude', 'face']],
  ['chain-of-custody-digital-evidence-courts-accept',                'gavel courtroom justice evidence',                  []],
  ['reverse-image-search-leak-detection-methodology',                'fingerprint identification magnifier',              []],
  ['bulk-dmca-dispatch-agency-workflow-1000-takedowns-week',         'envelopes letter mail postal office',               []],
  ['ncii-laws-comparison-us-eu-uk-australia-india',                  'world map global earth continents',                 []],
  ['webcam-model-protection-chaturbate-stripchat-independent',       'webcam video camera recording',                     ['nude', 'strip']],
];

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 isAcceptable(meta) {
  const license = (meta.LicenseShortName?.value || '').trim();
  if (!FREE_RE.test(license)) return { ok: false, why: 'license=' + (license || '?') };
  return { ok: true, license };
}

function pickCandidate(title, candidates, blocked) {
  // blocked: lowercase keywords — skip files whose title or snippet
  // contains any of these (avoids explicit / textual / templated content).
  const lcTitle = title.toLowerCase();
  for (let i = 0; i < candidates.length; i++) {
    const c = candidates[i];
    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 = 20) {
  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,
      thumb: ii.thumburl,
      width: ii.width, height: ii.height,
      mime: ii.mime,
      license: lic,
      artist: (md.Artist?.value || '').replace(/<[^>]+>/g, '').slice(0, 120),
      sourceUrl: ii.descriptionurl || ('https://commons.wikimedia.org/wiki/' + encodeURIComponent(p.title)),
    });
  }
  return out;
}

async function processTopic(slug, query, blocked) {
  const candidates = await findCandidates(query, 25);
  const pick = pickCandidate(query, candidates, blocked);
  if (!pick) return { slug, query, status: 'no-match', tried: candidates.length };

  // Determine local extension
  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('downloaded file too small (' + stat.size + ' bytes)');

    // Update DB
    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, query, status: 'ok',
      picked: pick.title, license: pick.license,
      size: pick.width + 'x' + pick.height,
      localFile: relPath, bytes: stat.size,
      attribution,
    };
  } catch (err) {
    return { slug, query, status: 'download-failed', error: err.message, pick: pick.title };
  }
}

(async () => {
  // Make sure dir exists
  fs.mkdirSync(path.join(__dirname, '..', 'public', 'og', 'blog'), { recursive: true });

  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 }));
    }
  }
})();
