/**
 * Import Scryfall Tagger community tags (Oracle Tags + Art Tags) into the
 * `tags` and `card_tags` tables.
 *
 * Oracle tags describe card functionality (removal, ramp, draw, etc.)
 * Art tags describe what's depicted in the artwork (dragon, forest, battle, etc.)
 *
 * Tags join to cards via oracle_id (oracle tags) or illustration_id (art tags).
 * The card_tags table stores the raw join IDs so cards added later are automatically
 * matched without re-importing tags.
 *
 * Usage:
 *   POSTGRES_URL=<url> node scripts/import-scryfall-tags.js
 *
 * Options (env vars):
 *   TAG_TYPE     — "both" (default) | "oracle" | "art"
 *   BATCH_SIZE   — rows per INSERT batch (default 200)
 *   DRY_RUN      — "true" to fetch and count without writing to DB
 */

import { neon } from '@neondatabase/serverless';

if (!process.env.POSTGRES_URL) {
  console.error('POSTGRES_URL is required');
  process.exit(1);
}

const sql = neon(process.env.POSTGRES_URL, { fullResults: false });

const TAG_TYPE = process.env.TAG_TYPE || 'both';
const BATCH_SIZE = parseInt(process.env.BATCH_SIZE || '200', 10);
const DRY_RUN = process.env.DRY_RUN === 'true';

async function fetchBulkDownloadUrl(type) {
  const response = await fetch(`https://api.scryfall.com/bulk-data/${type}`, {
    headers: { 'User-Agent': 'DeckHearth/1.0', Accept: 'application/json' },
  });
  if (!response.ok) {
    throw new Error(`Failed to fetch bulk-data metadata for ${type}: ${response.status}`);
  }
  const data = await response.json();
  console.log(`[tag-import] File: ${data.name} (${(data.size / 1024 / 1024).toFixed(1)} MB)`);
  return data.download_uri;
}

async function downloadJson(url) {
  console.log(`[tag-import] Downloading...`);
  const response = await fetch(url, {
    headers: { 'User-Agent': 'DeckHearth/1.0' },
  });
  if (!response.ok) {
    throw new Error(`Download failed: ${response.status}`);
  }
  const text = await response.text();
  console.log(`[tag-import] Downloaded ${(text.length / 1024 / 1024).toFixed(1)} MB`);
  return JSON.parse(text);
}

async function upsertTags(tags, stats) {
  const batch = [];

  for (const tag of tags) {
    batch.push({
      id: tag.id,
      slug: tag.slug,
      label: tag.label,
      type: tag.type,
      description: tag.description || null,
      parentIds: JSON.stringify(tag.parent_ids || []),
      childIds: JSON.stringify(tag.child_ids || []),
      aliases: JSON.stringify(tag.aliases || []),
    });

    if (batch.length >= BATCH_SIZE) {
      await flushTagBatch(batch, stats);
      batch.length = 0;
    }
  }

  if (batch.length > 0) {
    await flushTagBatch(batch, stats);
  }
}

async function flushTagBatch(batch, stats) {
  if (DRY_RUN) {
    stats.tags += batch.length;
    return;
  }

  const placeholders = [];
  const values = [];
  let paramIdx = 1;

  for (const row of batch) {
    const rowP = [];
    const rowV = [row.id, row.slug, row.label, row.type, row.description, row.parentIds, row.childIds, row.aliases];
    for (let i = 0; i < rowV.length; i++) {
      rowP.push(`$${paramIdx}`);
      paramIdx += 1;
    }
    placeholders.push(`(${rowP.join(', ')})`);
    values.push(...rowV);
  }

  const query = `
    INSERT INTO tags (id, slug, label, type, description, parent_ids, child_ids, aliases)
    VALUES ${placeholders.join(',\n')}
    ON CONFLICT (id) DO UPDATE SET
      slug = EXCLUDED.slug,
      label = EXCLUDED.label,
      description = EXCLUDED.description,
      parent_ids = EXCLUDED.parent_ids,
      child_ids = EXCLUDED.child_ids,
      aliases = EXCLUDED.aliases,
      updated_at = CURRENT_TIMESTAMP
  `;

  try {
    await sql.query(query, values);
    stats.tags += batch.length;
  } catch (error) {
    console.error(`[tag-import] Tag batch failed:`, error.message);
    stats.tagErrors += batch.length;
  }
}

async function importTaggings(tags, stats) {
  let batch = [];
  let processed = 0;
  const total = tags.reduce((sum, t) => sum + (t.taggings?.length || 0), 0);
  console.log(`[tag-import] Total taggings to import: ${total}`);

  for (const tag of tags) {
    if (!tag.taggings || tag.taggings.length === 0) continue;

    for (const tagging of tag.taggings) {
      batch.push({
        tagId: tag.id,
        oracleId: tagging.oracle_id || null,
        illustrationId: tagging.illustration_id || null,
        weight: tagging.weight || 'median',
        annotation: tagging.annotation || null,
      });

      if (batch.length >= BATCH_SIZE) {
        await flushTaggingBatch(batch, stats);
        processed += batch.length;
        batch = [];

        if (processed % 10000 === 0) {
          console.log(`[tag-import] Tagging progress: ${processed}/${total} (${((processed / total) * 100).toFixed(1)}%)`);
        }
      }
    }
  }

  if (batch.length > 0) {
    await flushTaggingBatch(batch, stats);
    processed += batch.length;
  }

  console.log(`[tag-import] Taggings processed: ${processed}`);
}

async function flushTaggingBatch(batch, stats) {
  if (DRY_RUN) {
    stats.taggings += batch.length;
    return;
  }

  const placeholders = [];
  const values = [];
  let paramIdx = 1;

  for (const row of batch) {
    const rowP = [];
    const rowV = [row.tagId, row.oracleId, row.illustrationId, row.weight, row.annotation];
    for (let i = 0; i < rowV.length; i++) {
      rowP.push(`$${paramIdx}`);
      paramIdx += 1;
    }
    placeholders.push(`(${rowP.join(', ')})`);
    values.push(...rowV);
  }

  const uniqueCol = batch[0].oracleId ? 'card_tags_unique_tag_oracle' : 'card_tags_unique_tag_illustration';

  const query = `
    INSERT INTO card_tags (tag_id, oracle_id, illustration_id, weight, annotation)
    VALUES ${placeholders.join(',\n')}
    ON CONFLICT ON CONSTRAINT ${uniqueCol} DO UPDATE SET
      weight = EXCLUDED.weight,
      annotation = EXCLUDED.annotation
  `;

  try {
    await sql.query(query, values);
    stats.taggings += batch.length;
  } catch (error) {
    console.error(`[tag-import] Tagging batch failed:`, error.message);
    stats.taggingErrors += batch.length;
  }
}

async function importFile(type) {
  const bulkType = type === 'oracle' ? 'oracle_tags' : 'art_tags';
  console.log(`\n[tag-import] === Importing ${type} tags (${bulkType}) ===`);

  const url = await fetchBulkDownloadUrl(bulkType);
  const tags = await downloadJson(url);
  console.log(`[tag-import] Tags in file: ${tags.length}`);

  const stats = { tags: 0, tagErrors: 0, taggings: 0, taggingErrors: 0 };

  console.log(`[tag-import] Upserting tag definitions...`);
  await upsertTags(tags, stats);
  console.log(`[tag-import] Tags upserted: ${stats.tags}, errors: ${stats.tagErrors}`);

  console.log(`[tag-import] Importing taggings...`);
  await importTaggings(tags, stats);
  console.log(`[tag-import] Taggings upserted: ${stats.taggings}, errors: ${stats.taggingErrors}`);

  return stats;
}

async function run() {
  console.log(`[tag-import] Starting Scryfall Tagger import (type=${TAG_TYPE}, batch=${BATCH_SIZE}, dryRun=${DRY_RUN})`);

  const results = {};

  if (TAG_TYPE === 'oracle' || TAG_TYPE === 'both') {
    results.oracle = await importFile('oracle');
  }

  if (TAG_TYPE === 'art' || TAG_TYPE === 'both') {
    results.art = await importFile('art');
  }

  console.log(`\n[tag-import] === Summary ===`);
  console.log(JSON.stringify(results, null, 2));
}

run().catch((err) => {
  console.error('[tag-import] Fatal error:', err);
  process.exit(1);
});
