import { Cid } from '@atproto/lex-data'
import { BlockMap } from '../block-map.js'
import { CommitData } from '../types.js'
import { ReadableBlockstore } from './readable-blockstore.js'
import { RepoStorage } from './types.js'

export class MemoryBlockstore
  extends ReadableBlockstore
  implements RepoStorage
{
  blocks: BlockMap
  root: Cid | null = null
  rev: string | null = null

  constructor(blocks?: BlockMap) {
    super()
    this.blocks = new BlockMap()
    if (blocks) {
      this.blocks.addMap(blocks)
    }
  }

  async getRoot(): Promise<Cid | null> {
    return this.root
  }

  async getBytes(cid: Cid): Promise<Uint8Array | null> {
    return this.blocks.get(cid) || null
  }

  async has(cid: Cid): Promise<boolean> {
    return this.blocks.has(cid)
  }

  async getBlocks(cids: Cid[]): Promise<{ blocks: BlockMap; missing: Cid[] }> {
    return this.blocks.getMany(cids)
  }

  async putBlock(cid: Cid, block: Uint8Array): Promise<void> {
    this.blocks.set(cid, block)
  }

  async putMany(blocks: BlockMap): Promise<void> {
    this.blocks.addMap(blocks)
  }

  async updateRoot(cid: Cid, rev: string): Promise<void> {
    this.root = cid
    this.rev = rev
  }

  async applyCommit(commit: CommitData): Promise<void> {
    this.root = commit.cid
    const rmCids = commit.removedCids.toList()
    for (const cid of rmCids) {
      this.blocks.delete(cid)
    }
    commit.newBlocks.forEach((bytes, cid) => {
      this.blocks.set(cid, bytes)
    })
  }

  async sizeInBytes(): Promise<number> {
    let total = 0
    this.blocks.forEach((bytes) => {
      total += bytes.byteLength
    })
    return total
  }

  async destroy(): Promise<void> {
    this.blocks.clear()
  }
}

export default MemoryBlockstore
