use anyhow::{Result, anyhow};

use crate::{ShardManifest, SplittableFrag};

impl ShardManifest {
    pub fn describe_level(&self) -> String {
        format!("{},{}", self.base_slice().len(), self.delta_slice().len(),)
    }

    /// Splice a contiguous window of fragments with output fragments.
    pub fn splice(&mut self, input: &[SplittableFrag], output: Vec<SplittableFrag>) -> Result<()> {
        use crate::common::shard::FragmentLocation;

        let mut output = output;
        output.retain(|frag| {
            let size = frag.f.property.as_ref().map_or(0, |p| p.size);
            !(frag.f.property.is_none() && size == 0)
        });

        let first_frag = input.first().unwrap();
        let location = self.find_pos_by_frag_path(&first_frag.frag_path);

        match location {
            Some(FragmentLocation::Base(pos)) => {
                let end = pos + input.len();
                let removed: Vec<_> = self.base_fragments.splice(pos..end, output).collect();

                if removed != input {
                    return Err(anyhow!(
                        "base splice: input mismatch, expected={:?}, removed={:?}",
                        input,
                        removed
                    ));
                }

                Ok(())
            }
            Some(FragmentLocation::Delta(pos)) => {
                let end = pos + input.len();
                let removed: Vec<_> = self.delta_fragments.splice(pos..end, output).collect();

                if removed != input {
                    return Err(anyhow!(
                        "delta splice: input mismatch, expected={:?}, removed={:?}",
                        input,
                        removed
                    ));
                }

                Ok(())
            }
            None => Err(anyhow!("splice input not found: fragments {:?}", input)),
        }
    }

    pub fn current_level_sizes(&self) -> [u64; 2] {
        [
            self.base_slice().len() as u64,
            self.delta_slice().len() as u64,
        ]
    }

    pub fn delta_slice(&self) -> &[SplittableFrag] {
        &self.delta_fragments
    }

    pub fn delta_frag_paths(&self) -> Vec<String> {
        self.delta_slice()
            .iter()
            .map(|x| x.f.frag_path.clone())
            .collect::<Vec<_>>()
    }

    pub fn delta_cloned(&self) -> Vec<SplittableFrag> {
        self.delta_slice().to_vec()
    }

    pub fn base_slice(&self) -> &[SplittableFrag] {
        &self.base_fragments
    }

    pub fn base_cloned(&self) -> Vec<SplittableFrag> {
        self.base_slice().to_vec()
    }

    pub fn base_frag_paths(&self) -> Vec<String> {
        self.base_slice()
            .iter()
            .map(|x| x.f.frag_path.clone())
            .collect::<Vec<_>>()
    }

    pub fn has_base(&self) -> bool {
        !self.base_slice().is_empty()
    }

    pub fn base_seg_count(&self) -> usize {
        self.base_slice()
            .iter()
            .map(|x| x.seg_count())
            .sum::<usize>()
    }
}
