import { PMTiles } from 'pmtiles'
import { IndexedDBSource } from './storage.ts'

const DB_NAME = 'me.bpev.maps.tiles'
const STORE_NAME = 'tiles'
const options = { logLevel: 'warn' }

let _db: IDBDatabase | null = null

async function getDb(): Promise<IDBDatabase> {
  if (!_db) _db = await IndexedDBSource.openDb(DB_NAME, STORE_NAME)
  return _db
}

/**
 * Readability probe for stored archives. WebKit can permanently lose
 * IDB-stored Blob bodies (legacy chunk format) even though the meta record
 * survives — `exists()` then lies. Reading the header surfaces that now
 * rather than as silent blank tiles; a failed read also clears the file
 * (see storage.ts), so callers can treat it as "not cached" and re-download
 * or stream. The header read is ~16 KB and PMTiles caches it, so a healthy
 * archive pays it exactly once.
 */
async function probeReadable(source: IndexedDBSource): Promise<PMTiles | null> {
  const pmtiles = new PMTiles(source)
  try {
    await pmtiles.getHeader()
    return pmtiles
  } catch {
    return null
  }
}

export async function downloadAndSavePMTiles(
  url: string,
  filename: string,
): Promise<PMTiles> {
  const db = await getDb()
  const source = new IndexedDBSource(db, filename, STORE_NAME, options)

  if (await source.exists()) {
    const cached = await probeReadable(source)
    if (cached) {
      console.log(`File ${filename} already cached, loading from IndexedDB`)
      return cached
    }
    console.warn(`Cached ${filename} was unreadable; re-downloading`)
  }

  console.log(`Downloading PMTiles from ${url}...`)
  const response = await fetch(url)
  if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`)
  if (!response.body) throw new Error(`No response body for ${url}`)
  // Stream straight into IndexedDB so the whole file is never buffered
  // in memory at once.
  await source.setSourceFromStream(response.body)
  console.log(`Successfully saved ${filename}`)

  return new PMTiles(source)
}

export async function isPMTilesCached(filename: string): Promise<boolean> {
  const db = await getDb()
  const source = new IndexedDBSource(db, filename, STORE_NAME, options)
  return await source.exists()
}

export async function getCachedPMTiles(
  filename: string,
): Promise<PMTiles | null> {
  const db = await getDb()
  const source = new IndexedDBSource(db, filename, STORE_NAME, options)
  if (!(await source.exists())) return null
  // An unreadable archive reports as not-cached (and has been cleared), so
  // callers fall back to downloading or streaming instead of drawing blanks.
  return await probeReadable(source)
}

export async function deletePMTiles(filename: string): Promise<void> {
  const db = await getDb()
  // Use the source so both the meta record and all chunk records are removed.
  const source = new IndexedDBSource(db, filename, STORE_NAME, options)
  await source.deleteFile()
}
