/*
    This file is part of jellything (https://codeberg.org/metamuffin/jellything)
    which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
    Copyright (C) 2026 metamuffin <metamuffin.org>
*/

use crate::{
    backends::{ReadTransaction, WriteTransaction},
    sort::Index,
};
use anyhow::Result;
use jellycommon::jellyobject::ObjectBuffer;

pub type TableNum = u64;
pub type RowNum = u64;

pub struct Table {
    id: u32,
    pub(crate) indices: Vec<Box<dyn Index>>,
}
impl Table {
    pub fn new(id: u32) -> Self {
        Self {
            id,
            indices: Vec::new(),
        }
    }
    fn key(&self, row: RowNum) -> Vec<u8> {
        let mut key = Vec::new();
        key.extend(self.id.to_be_bytes());
        key.extend(row.to_be_bytes());
        key
    }
    pub fn insert(&self, db: &mut dyn WriteTransaction, entry: ObjectBuffer) -> Result<RowNum> {
        let mut id_counter = db
            .get(&self.id.to_be_bytes())?
            .map(|k| k.as_slice().try_into().map(RowNum::from_be_bytes).ok())
            .flatten()
            .unwrap_or(0);
        let row = id_counter;
        id_counter += 1;
        db.set(&self.id.to_be_bytes(), &id_counter.to_be_bytes())?;

        db.set(&self.key(row), bytemuck::cast_slice(entry.0.as_slice()))?;

        let ob = entry.as_object();
        for idx in &self.indices {
            idx.add(db, row, ob)?;
        }

        Ok(id_counter)
    }
    pub fn add_index<T: Index + Clone + 'static>(&mut self, index: T) -> T {
        self.indices.push(Box::new(index.clone()));
        index
    }
    pub fn get(&self, db: &dyn ReadTransaction, row: RowNum) -> Result<Option<ObjectBuffer>> {
        Ok(db.get(&self.key(row))?.map(ObjectBuffer::from))
    }
    pub fn remove(&self, db: &mut dyn WriteTransaction, row: RowNum) -> Result<bool> {
        let Some(entry) = self.get(db, row)? else {
            return Ok(false);
        };
        let ob = entry.as_object();
        for idx in &self.indices {
            idx.remove(db, row, ob)?;
        }
        db.del(&self.key(row))?;
        Ok(true)
    }
}
