use std::sync::Arc;

use rspack_paths::Utf8PathBuf;

use crate::{ItemKey, ItemValue};

pub type PackKeys = Vec<Arc<ItemKey>>;
pub type PackContents = Vec<Arc<ItemValue>>;
pub type PackGenerations = Vec<usize>;

#[derive(Debug, Default)]
pub enum PackKeysState {
  #[default]
  Pending,
  Value(PackKeys),
}

impl PackKeysState {
  pub fn loaded(&self) -> bool {
    matches!(self, Self::Value(_))
  }
  pub fn set_value(&mut self, value: PackKeys) {
    *self = PackKeysState::Value(value);
  }
  pub fn get_value(&self) -> Option<&PackKeys> {
    match self {
      PackKeysState::Value(v) => Some(v),
      PackKeysState::Pending => None,
    }
  }
  pub fn expect_value(&self) -> &PackKeys {
    match self {
      PackKeysState::Value(v) => v,
      PackKeysState::Pending => panic!("pack key is not ready"),
    }
  }
  pub fn take_value(&mut self) -> Option<PackKeys> {
    match self {
      PackKeysState::Value(v) => Some(std::mem::take(&mut *v)),
      _ => None,
    }
  }
}

#[derive(Debug, Default)]
pub enum PackContentsState {
  #[default]
  Pending,
  Value(PackContents),
  Released,
}

impl PackContentsState {
  pub fn loaded(&self) -> bool {
    matches!(self, Self::Value(_))
  }
  pub fn set_value(&mut self, value: PackContents) {
    *self = PackContentsState::Value(value);
  }
  pub fn get_value(&self) -> Option<&PackContents> {
    match self {
      PackContentsState::Value(v) => Some(v),
      PackContentsState::Pending => None,
      PackContentsState::Released => None,
    }
  }
  pub fn expect_value(&self) -> &PackContents {
    match self {
      PackContentsState::Value(v) => v,
      PackContentsState::Pending => panic!("pack content is not ready"),
      PackContentsState::Released => panic!("pack content has been released"),
    }
  }
  pub fn take_value(&mut self) -> Option<PackContents> {
    match self {
      PackContentsState::Value(v) => Some(std::mem::take(&mut *v)),
      _ => None,
    }
  }
  pub fn release(&mut self) {
    *self = PackContentsState::Released;
  }
  pub fn is_released(&self) -> bool {
    matches!(self, Self::Released)
  }
}

#[derive(Debug)]
pub struct Pack {
  pub path: Utf8PathBuf,
  pub keys: PackKeysState,
  pub contents: PackContentsState,
  pub generations: PackGenerations,
}

impl Pack {
  pub fn new(path: Utf8PathBuf) -> Self {
    Self {
      path,
      keys: Default::default(),
      contents: Default::default(),
      generations: Default::default(),
    }
  }

  pub fn loaded(&self) -> bool {
    matches!(self.keys, PackKeysState::Value(_))
      && (matches!(self.contents, PackContentsState::Value(_))
        || matches!(self.contents, PackContentsState::Released))
  }

  pub fn size(&self) -> usize {
    let key_size = self
      .keys
      .expect_value()
      .iter()
      .fold(0_usize, |acc, item| acc + item.len());
    let content_size = self
      .contents
      .expect_value()
      .iter()
      .fold(0_usize, |acc, item| acc + item.len());
    let generation_size = self
      .generations
      .iter()
      .fold(0_usize, |acc, item| acc + item.to_string().len());
    key_size + content_size + generation_size
  }
}
