//! Types for documents to be stored in the flora archive.

#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

/// Types of documents that can be stored in the flora archive.
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Document {
    /// A web page (typically HTML).
    WebPage(ArchivedPage),
}

impl Document {
    /// Creates a [`Document`] variant for a web page (HTML).
    ///
    /// Does not check that the given data is actually HTML.
    #[must_use]
    pub fn new_web_page_unchecked(data: Vec<u8>) -> Self {
        Self::WebPage(ArchivedPage { data })
    }

    /// Returns the HTML data if the document is an HTML or text web page.
    ///
    /// If it's not a web page, returns `None`.
    #[must_use]
    pub fn into_web_page(self) -> Option<Vec<u8>> {
        match self {
            Self::WebPage(page) => Some(page.into_data()),
        }
    }
}

/// Type for web pages to be stored within flora's archive.
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ArchivedPage {
    /// The data for this web page.
    data: Vec<u8>,
}

impl ArchivedPage {
    /// Returns a reference to the data for this page.
    #[must_use]
    pub fn data(&self) -> &[u8] {
        &self.data
    }

    /// Returns the data for this page, consuming the wrapper.
    #[must_use]
    pub fn into_data(self) -> Vec<u8> {
        self.data
    }
}

#[cfg(test)]
pub(crate) mod tests {
    use super::*;

    use std::env;
    use std::fs::{self, File};
    use std::io::Read;
    use std::path::PathBuf;

    use anyhow::Result;

    #[cfg(feature = "marshal")]
    use crate::Marshal;

    const WORKSPACE_DIR_KEY: &str = "CARGO_WORKSPACE_DIR";
    const TEST_WEB_PAGE_PATH: &str = "test_data/example.html";

    pub fn get_test_page_path() -> Result<PathBuf> {
        let (_, workspace_dir_path) = env::vars()
            .find(|(k, _)| k == WORKSPACE_DIR_KEY)
            .ok_or_else(|| anyhow::anyhow!("{WORKSPACE_DIR_KEY} is not set"))?;
        let workspace_dir_path = fs::canonicalize(workspace_dir_path)?;
        Ok(workspace_dir_path.join(TEST_WEB_PAGE_PATH))
    }

    pub(crate) fn load_test_web_doc() -> Result<Document> {
        let example_page_path = get_test_page_path()?;
        let mut example_file = File::open(&example_page_path)?;
        let mut page_data = Vec::new();
        example_file.read_to_end(&mut page_data)?;
        Ok(Document::new_web_page_unchecked(page_data))
    }

    #[test]
    #[cfg(feature = "marshal")]
    fn can_marshal_document() -> Result<()> {
        let archive_holder = load_test_web_doc()?;
        let serialized = archive_holder.to_byte_vec()?;
        let deserialized = Document::from_bytes(&serialized)?;
        insta::assert_debug_snapshot!(&deserialized);
        Ok(())
    }

    #[test]
    fn can_take_owned_data() -> Result<()> {
        let archive_holder = load_test_web_doc()?;
        insta::assert_debug_snapshot!(archive_holder.into_web_page());
        Ok(())
    }
}
