#!/usr/bin/env tsx
/*
  Fetch the full English Bible from api.bible with careful rate limiting.
  - Requires env:
    - API_BIBLE_KEY: your api.scripture.api.bible key
    - API_BIBLE_BASE (optional): default https://api.scripture.api.bible
    - API_BIBLE_ID (optional): specific Bible ID to fetch (overrides abbr search)
    - API_BIBLE_ABBR (optional): preferred abbreviation to locate (e.g., BSB, NIV, KJV)
    - OUTPUT_DIR (optional): default data/en_bible

  Output: Creates OT/NT JSON in format compatible with scripts/import-romanian-versioned.ts
    data/en_bible/<ABBR>/old_testament.json
    data/en_bible/<ABBR>/new_testament.json

  Notes on rate limits:
    - Serializes requests (concurrency 1) with a base delay between calls (baseDelayMs).
    - Handles 429 with Retry-After header or exponential backoff.
    - Supports resume by writing per-chapter JSON; if a chapter file exists, it skips re-fetching it.
*/

import 'dotenv/config'
import fs from 'fs'
import path from 'path'

const API_KEY = process.env.API_BIBLE_KEY || ''
const API_BASE = (process.env.API_BIBLE_BASE || 'https://api.scripture.api.bible').replace(/\/$/, '')
const PREF_ABBR = process.env.API_BIBLE_ABBR || 'BSB'
const FORCE_BIBLE_ID = process.env.API_BIBLE_ID || ''
const OUTPUT_ROOT = process.env.OUTPUT_DIR || path.join('data', 'en_bible')

if (!API_KEY) {
  console.error('Missing API_BIBLE_KEY in environment')
  process.exit(1)
}

// Simple throttling and backoff
const baseDelayMs = 350 // ~3 requests/second baseline
function sleep(ms: number) { return new Promise(res => setTimeout(res, ms)) }

async function requestJson(url: string, init: RequestInit = {}, attempt = 0): Promise<any> {
  await sleep(baseDelayMs)
  const res = await fetch(url, {
    ...init,
    headers: {
      'accept': 'application/json',
      'api-key': API_KEY,
      ...(init.headers || {})
    }
  })

  if (res.status === 429) {
    const retry = parseInt(res.headers.get('retry-after') || '0', 10)
    const wait = Math.max(1000, (retry || 1) * 1000)
    if (attempt < 6) {
      console.warn(`429 rate limited. Waiting ${wait}ms and retrying...`)
      await sleep(wait)
      return requestJson(url, init, attempt + 1)
    }
  }

  if (!res.ok) {
    const body = await res.text()
    throw new Error(`HTTP ${res.status} for ${url}: ${body}`)
  }
  return res.json()
}

type BibleMeta = { id: string; name: string; abbreviation: string; language: { id: string; name: string } }

async function resolveBible(): Promise<BibleMeta> {
  if (FORCE_BIBLE_ID) {
    const data = await requestJson(`${API_BASE}/v1/bibles/${FORCE_BIBLE_ID}`)
    return data.data as BibleMeta
  }
  const list = await requestJson(`${API_BASE}/v1/bibles?language=eng`) // English
  const bibles: BibleMeta[] = list.data || []
  let chosen = bibles.find(b => (b.abbreviation || '').toUpperCase() === PREF_ABBR.toUpperCase())
  if (!chosen) chosen = bibles[0]
  if (!chosen) throw new Error('No English bibles found via API')
  return chosen
}

type Book = { id: string; name: string; abbreviation: string; ord: number }
type Chapter = { id: string; number: string }

async function fetchBooks(bibleId: string): Promise<Book[]> {
  const resp = await requestJson(`${API_BASE}/v1/bibles/${bibleId}/books`)
  const data = resp.data || []
  return data.map((b: any, i: number) => ({ id: b.id, name: b.name, abbreviation: b.abbreviation, ord: b.order || (i + 1) }))
}

async function fetchChapters(bibleId: string, bookId: string): Promise<Chapter[]> {
  const resp = await requestJson(`${API_BASE}/v1/bibles/${bibleId}/books/${bookId}/chapters`)
  const data = resp.data || []
  return data.map((c: any) => ({ id: c.id, number: c.number }))
}

// Fetch all verse IDs for a chapter, then fetch each verse text (contentType=text)
async function fetchChapterVerses(bibleId: string, chapterId: string): Promise<{ verseNum: number; text: string }[]> {
  const resp = await requestJson(`${API_BASE}/v1/bibles/${bibleId}/chapters/${chapterId}/verses`)
  const verses: any[] = resp.data || []
  const results: { verseNum: number; text: string }[] = []
  for (const v of verses) {
    const vId = v.id
    // Respect rate limits while fetching verse content
    const vResp = await requestJson(`${API_BASE}/v1/bibles/${bibleId}/verses/${vId}?content-type=text&include-notes=false&include-titles=false&include-chapter-numbers=false&include-verse-numbers=false&include-verse-spans=false`)
    const text: string = vResp?.data?.content?.trim?.() || vResp?.data?.content || ''
    // Extract verse number from reference when available, fallback to sequence
    const num = parseInt((v.reference || '').split(':')[1] || v.verseCount || v.position || results.length + 1, 10)
    results.push({ verseNum: Number.isFinite(num) ? num : (results.length + 1), text })
  }
  // Sort by verse number just in case
  results.sort((a, b) => a.verseNum - b.verseNum)
  return results
}

function ensureDir(p: string) { fs.mkdirSync(p, { recursive: true }) }
function writeJson(file: string, obj: any) { ensureDir(path.dirname(file)); fs.writeFileSync(file, JSON.stringify(obj, null, 2), 'utf-8') }
function exists(p: string) { try { fs.accessSync(p); return true } catch { return false } }

async function main() {
  const bible = await resolveBible()
  console.log(`Using Bible: ${bible.name} (${bible.abbreviation}) [${bible.id}]`)
  const outDir = path.join(OUTPUT_ROOT, bible.abbreviation.toUpperCase())
  ensureDir(outDir)

  const books = await fetchBooks(bible.id)
  // Partition into OT/NT by order threshold (first 39 = OT)
  const otBooks = books.filter(b => b.ord <= 39)
  const ntBooks = books.filter(b => b.ord > 39)

  const buildTestament = async (subset: Book[], label: 'Old Testament' | 'New Testament') => {
    const result: any = { testament: label, books: [] as any[] }
    for (const b of subset) {
      console.log(`Book: ${b.name}`)
      const bookOutDir = path.join(outDir, b.abbreviation || b.name)
      ensureDir(bookOutDir)
      const chs = await fetchChapters(bible.id, b.id)
      const chaptersArr: any[] = []
      for (const ch of chs) {
        const cacheFile = path.join(bookOutDir, `chapter-${ch.number}.json`)
        if (exists(cacheFile)) {
          const cached = JSON.parse(fs.readFileSync(cacheFile, 'utf-8'))
          chaptersArr.push(cached)
          continue
        }
        console.log(`  Chapter ${ch.number}`)
        const verses = await fetchChapterVerses(bible.id, ch.id)
        const chapterObj = { chapterNum: parseInt(ch.number, 10), verses }
        writeJson(cacheFile, chapterObj)
        chaptersArr.push(chapterObj)
      }
      result.books.push({ name: b.name, chapters: chaptersArr })
    }
    return result
  }

  const ot = await buildTestament(otBooks, 'Old Testament')
  const nt = await buildTestament(ntBooks, 'New Testament')

  const otFile = path.join(outDir, 'old_testament.json')
  const ntFile = path.join(outDir, 'new_testament.json')
  writeJson(otFile, ot)
  writeJson(ntFile, nt)
  console.log('Wrote:', otFile)
  console.log('Wrote:', ntFile)

  console.log('\nNext: import into the versioned schema using scripts/import-romanian-versioned.ts with LANG_CODE=en and TRANSLATION_CODE matching', bible.abbreviation)
}

main().catch(err => {
  console.error('Fetch failed:', err)
  process.exit(1)
})

