export type ResourceQuotaAssessment = {
  allowed: boolean
  known: boolean
  usage: number | null
  quota: number | null
  incomingBytes: number
  reservedBytes: number
  availableBytes: number | null
  reason?: string
}

export const assessResourceQuota = (
  estimate: { usage?: number; quota?: number },
  incomingBytes: number,
  reserveRatio = 0.05,
): ResourceQuotaAssessment => {
  if (!Number.isSafeInteger(incomingBytes) || incomingBytes < 0) throw new RangeError('Resource byte length must be a non-negative safe integer.')
  if (!Number.isFinite(reserveRatio) || reserveRatio < 0 || reserveRatio >= 1) throw new RangeError('Resource reserve ratio must be between 0 and 1.')
  const usage = typeof estimate.usage === 'number' && Number.isFinite(estimate.usage) && estimate.usage >= 0 ? estimate.usage : null
  const quota = typeof estimate.quota === 'number' && Number.isFinite(estimate.quota) && estimate.quota >= 0 ? estimate.quota : null
  if (usage === null || quota === null) return { allowed: true, known: false, usage, quota, incomingBytes, reservedBytes: 0, availableBytes: null, reason: 'Browser storage quota is unavailable; write errors remain authoritative.' }
  const reservedBytes = Math.ceil(quota * reserveRatio)
  const availableBytes = Math.max(0, quota - usage - reservedBytes)
  const allowed = incomingBytes <= availableBytes
  return { allowed, known: true, usage, quota, incomingBytes, reservedBytes, availableBytes, reason: allowed ? undefined : `Resource requires ${incomingBytes} bytes but only ${availableBytes} bytes remain after the storage reserve.` }
}

export type ResourceSweepRecord = { hash: string; byteLength: number; refCount: number }

export type ResourceSweepPlan = {
  deleteFileHashes: string[]
  deleteRecordHashes: string[]
  missingFileHashes: string[]
}

export const planResourceSweep = (records: ResourceSweepRecord[], storedHashes: Iterable<string>): ResourceSweepPlan => {
  const recordByHash = new Map<string, ResourceSweepRecord>()
  for (const record of records) {
    if (!record.hash || !Number.isSafeInteger(record.byteLength) || record.byteLength < 0 || !Number.isSafeInteger(record.refCount) || record.refCount < 0) throw new RangeError('Resource sweep records require a hash and non-negative integer sizes/counts.')
    if (recordByHash.has(record.hash)) throw new RangeError(`Duplicate resource sweep record: ${record.hash}.`)
    recordByHash.set(record.hash, record)
  }
  const files = new Set(storedHashes)
  const deleteRecordHashes = records.filter((record) => record.refCount === 0).map((record) => record.hash).sort()
  const deleteFileHashes = [...files].filter((hash) => !recordByHash.has(hash) || recordByHash.get(hash)?.refCount === 0).sort()
  const missingFileHashes = records.filter((record) => record.refCount > 0 && !files.has(record.hash)).map((record) => record.hash).sort()
  return { deleteFileHashes, deleteRecordHashes, missingFileHashes }
}
