/**
 * Bulk catalog sync for Disney Lorcana via lorcana-api.com /bulk/cards.
 *
 * Single fetch returns all cards (~2.3k). Upserts keyed on scryfall_id (stores Unique_ID).
 */

import { sql as vercelSql } from '../sql.js';

const BATCH_SIZE = 100;
const BULK_URL = 'https://api.lorcana-api.com/bulk/cards';

/**
 * Map a Lorcana bulk API card object to cards-table insert shape.
 */
export function mapLorcanaCard(card) {
  const colors = card.Color
    ? card.Color.split(',').map((entry) => entry.trim()).filter(Boolean)
    : [];

  const keywords = [];
  if (card.Classifications) {
    keywords.push(
      ...card.Classifications.split(',').map((entry) => entry.trim()).filter(Boolean)
    );
  }
  if (card.Abilities) {
    keywords.push(...card.Abilities.split(',').map((entry) => entry.trim()).filter(Boolean));
  }
  if (card.Inkable === true) {
    keywords.push('Inkable');
  }

  const cost = card.Cost != null ? Number(card.Cost) : null;

  return {
    scryfallId: card.Unique_ID,
    name: card.Name,
    setName: card.Set_Name,
    setCode: card.Set_ID,
    cardNumber: card.Card_Num != null ? String(card.Card_Num) : null,
    rarity: card.Rarity || null,
    manaCost: cost != null ? String(cost) : null,
    cmc: cost,
    cardType: card.Type || null,
    colors: JSON.stringify(colors),
    colorIdentity: JSON.stringify(colors),
    oracleText: card.Body_Text || null,
    power: card.Strength != null ? String(card.Strength) : null,
    toughness: card.Willpower != null ? String(card.Willpower) : null,
    imageUrl: card.Image || null,
    stockImageUrl: card.Image || null,
    flavorText: card.Flavor_Text || null,
    artist: card.Artist || null,
    edhrecRank: card.Lore != null ? Number(card.Lore) : null,
    keywords: JSON.stringify(keywords),
  };
}

export function buildUpsertQuery(batch) {
  const columns = [
    'scryfall_id', 'name', 'set_name', 'set_code', 'card_number', 'rarity', 'game',
    'mana_cost', 'cmc', 'card_type', 'colors', 'color_identity', 'oracle_text',
    'power', 'toughness', 'image_url', 'stock_image_url', 'keywords', 'flavor_text',
    'artist', 'edhrec_rank', 'verified',
  ];

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

  for (const row of batch) {
    const rowPlaceholders = [];
    const rowValues = [
      row.scryfallId, row.name, row.setName, row.setCode, row.cardNumber, row.rarity,
      'Lorcana', row.manaCost, row.cmc, row.cardType, row.colors, row.colorIdentity,
      row.oracleText, row.power, row.toughness, row.imageUrl, row.stockImageUrl,
      row.keywords, row.flavorText, row.artist, row.edhrecRank, true,
    ];

    for (let i = 0; i < rowValues.length; i += 1) {
      rowPlaceholders.push(`$${paramIdx}`);
      paramIdx += 1;
    }
    placeholders.push(`(${rowPlaceholders.join(', ')})`);
    values.push(...rowValues);
  }

  const updateCols = columns
    .filter((col) => col !== 'scryfall_id')
    .map((col) => `${col} = EXCLUDED.${col}`)
    .join(', ');

  const query = `
    INSERT INTO cards (${columns.join(', ')})
    VALUES ${placeholders.join(',\n')}
    ON CONFLICT (scryfall_id) DO UPDATE SET
      ${updateCols},
      updated_at = CURRENT_TIMESTAMP
  `;

  return { query, values };
}

async function upsertBatch(sql, batch) {
  if (batch.length === 0) return 0;
  const { query, values } = buildUpsertQuery(batch);
  await sql.query(query, values);
  return batch.length;
}

export async function fetchLorcanaBulkCards() {
  const response = await fetch(BULK_URL, {
    headers: { 'User-Agent': 'DeckHearth/1.0', Accept: 'application/json' },
  });
  if (!response.ok) {
    throw new Error(`Lorcana bulk API error: ${response.status}`);
  }
  return response.json();
}

/**
 * Run a full Lorcana bulk sync. Returns summary compatible with catalog sync reporting.
 *
 * @param {{ sql?: { query: (q: string, v: unknown[]) => Promise<unknown> }, dryRun?: boolean }} options
 */
export async function runBulkLorcanaSync(options = {}) {
  const dryRun = options.dryRun ?? false;
  const sql = options.sql ?? vercelSql;

  const allCards = await fetchLorcanaBulkCards();
  const lorcanaCards = allCards.filter(
    (card) => !card.Gamemode || card.Gamemode === 'Lorcana'
  );

  let upserted = 0;
  let errors = 0;
  let batch = [];

  for (const card of lorcanaCards) {
    if (!card.Unique_ID) {
      errors += 1;
      continue;
    }

    batch.push(mapLorcanaCard(card));

    if (batch.length >= BATCH_SIZE) {
      try {
        if (!dryRun) {
          upserted += await upsertBatch(sql, batch);
        } else {
          upserted += batch.length;
        }
      } catch (err) {
        console.error('[lorcana-bulk] Batch error:', err.message);
        errors += batch.length;
      }
      batch = [];
    }
  }

  if (batch.length > 0) {
    try {
      if (!dryRun) {
        upserted += await upsertBatch(sql, batch);
      } else {
        upserted += batch.length;
      }
    } catch (err) {
      console.error('[lorcana-bulk] Final batch error:', err.message);
      errors += batch.length;
    }
  }

  const summary = {
    mode: 'bulk',
    game: 'lorcana',
    totalInFile: lorcanaCards.length,
    upserted,
    errors,
  };

  console.log('[lorcana-bulk]', JSON.stringify(summary));
  return summary;
}
