// OFAC — US Treasury Office of Foreign Assets Control Sanctions
// No auth required. Monitors the Specially Designated Nationals (SDN) list
// and consolidated sanctions list for changes.

const EXPORTS_BASE = 'https://sanctionslistservice.ofac.treas.gov/api/PublicationPreview/exports';

// SDN list endpoints
const SDN_XML_URL = `${EXPORTS_BASE}/SDN.XML`;
const SDN_ADVANCED_URL = `${EXPORTS_BASE}/SDN_ADVANCED.XML`;
const CONS_ADVANCED_URL = `${EXPORTS_BASE}/CONS_ADVANCED.XML`;

// Parse basic info from SDN XML (publish date, entry count)
function parseSDNMetadata(xml) {
  if (!xml || xml.error) return { error: xml?.error || 'No data returned' };

  const raw = xml.rawText || '';

  // Extract publish date
  const publishDate = raw.match(/<Publish_Date>(.*?)<\/Publish_Date>/)?.[1]
    || raw.match(/<publish_date>(.*?)<\/publish_date>/i)?.[1]
    || null;

  // Count SDN entries
  const entryMatches = raw.match(/<sdnEntry>/gi);
  const entryCount = entryMatches ? entryMatches.length : null;

  // Extract record count if present
  const recordCount = raw.match(/<Record_Count>(.*?)<\/Record_Count>/)?.[1]
    || raw.match(/<records_count>(.*?)<\/records_count>/i)?.[1]
    || null;

  return {
    publishDate,
    entryCount,
    recordCount: recordCount ? parseInt(recordCount, 10) : null,
    hasData: raw.length > 0,
    dataSize: raw.length,
  };
}

// Fetch SDN list metadata (smaller initial chunk via timeout)
export async function getSDNMetadata() {
  const data = await fetchXmlSnippet(SDN_XML_URL);
  return parseSDNMetadata(data);
}

// Fetch advanced SDN data (includes more structured info)
export async function getSDNAdvanced() {
  const data = await fetchXmlSnippet(SDN_ADVANCED_URL);
  return parseSDNMetadata(data);
}

// Fetch consolidated list metadata
export async function getConsolidatedMetadata() {
  const data = await fetchXmlSnippet(CONS_ADVANCED_URL);
  return parseSDNMetadata(data);
}

// Parse recent SDN entries from XML snippet
function parseRecentEntries(xml) {
  if (!xml || xml.error) return [];

  const raw = xml.rawText || '';
  const entries = [];
  const entryRegex = /<sdnEntry>([\s\S]*?)<\/sdnEntry>/gi;
  let match;
  let count = 0;

  while ((match = entryRegex.exec(raw)) !== null && count < 20) {
    const content = match[1];
    const uid = content.match(/<uid>(.*?)<\/uid>/i)?.[1];
    const lastName = content.match(/<lastName>(.*?)<\/lastName>/i)?.[1];
    const firstName = content.match(/<firstName>(.*?)<\/firstName>/i)?.[1];
    const sdnType = content.match(/<sdnType>(.*?)<\/sdnType>/i)?.[1];

    // Extract programs
    const programs = [];
    const progRegex = /<program>(.*?)<\/program>/gi;
    let progMatch;
    while ((progMatch = progRegex.exec(content)) !== null) {
      programs.push(progMatch[1]);
    }

    if (uid || lastName) {
      entries.push({
        uid,
        name: [firstName, lastName].filter(Boolean).join(' '),
        type: sdnType,
        programs,
      });
      count++;
    }
  }

  return entries;
}

// Briefing — report on sanctions list status and metadata
export async function briefing() {
  const [sdnMeta, advancedMeta, advancedSnippet] = await Promise.all([
    getSDNMetadata(),
    getSDNAdvanced(),
    fetchXmlSnippet(SDN_ADVANCED_URL, { maxBytes: 256_000 }),
  ]);

  const sampleEntries = parseRecentEntries(advancedSnippet);

  return {
    source: 'OFAC Sanctions',
    timestamp: new Date().toISOString(),
    lastUpdated: sdnMeta.publishDate || advancedMeta.publishDate || 'unknown',
    sdnList: {
      publishDate: sdnMeta.publishDate,
      entryCount: sdnMeta.entryCount,
      recordCount: sdnMeta.recordCount,
      dataAvailable: sdnMeta.hasData,
    },
    advancedList: {
      publishDate: advancedMeta.publishDate,
      entryCount: advancedMeta.entryCount,
      recordCount: advancedMeta.recordCount,
      dataAvailable: advancedMeta.hasData,
    },
    sampleEntries: sampleEntries.slice(0, 10),
    endpoints: {
      sdnXml: SDN_XML_URL,
      sdnAdvanced: SDN_ADVANCED_URL,
      consolidatedAdvanced: CONS_ADVANCED_URL,
    },
  };
}

async function fetchXmlSnippet(url, opts = {}) {
  const timeout = opts.timeout || 9000;
  const maxBytes = opts.maxBytes || 128_000;
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), timeout);
  try {
    const res = await fetch(url, {
      headers: {
        'User-Agent': 'Crucix/2.0',
        'Range': `bytes=0-${maxBytes - 1}`,
      },
      signal: controller.signal,
    });
    if (!res.ok && res.status !== 206) {
      const body = await res.text().catch(() => '');
      return { error: `HTTP ${res.status}: ${body.slice(0, 160)}` };
    }
    if (!res.body?.getReader) {
      const text = await res.text();
      return { rawText: text.slice(0, maxBytes) };
    }
    const reader = res.body.getReader();
    const chunks = [];
    let bytes = 0;
    while (bytes < maxBytes) {
      const { done, value } = await reader.read();
      if (done) break;
      chunks.push(value);
      bytes += value.byteLength;
    }
    await reader.cancel().catch(() => {});
    const buffer = Buffer.concat(chunks.map(chunk => Buffer.from(chunk)), bytes);
    return { rawText: buffer.toString('utf8', 0, Math.min(bytes, maxBytes)) };
  } catch (error) {
    return { error: error.name === 'AbortError' ? `OFAC snippet request timed out after ${timeout}ms` : error.message };
  } finally {
    clearTimeout(timer);
  }
}

// Run standalone
if (process.argv[1]?.endsWith('ofac.mjs')) {
  const data = await briefing();
  console.log(JSON.stringify(data, null, 2));
}
