use std::{fs, path::PathBuf};

use crate::{
    handling::response::{ResponseBody, Status},
    utils::mime::Mime,
};

#[derive(Debug)]
/// Struct to handle files on the server side.
/// Validates paths ignores actions like `..`
pub struct NamedFile {
    /// The length of the file in bytes, format: [usize]
    pub content_len: usize,
    /// The Mime Type of the file as a [Mime]
    pub content_type: Mime,
    /// The content of the file as a [`Vec<u8>`]
    pub content: Vec<u8>,
}

impl ResponseBody for NamedFile {
    fn get_data(&self) -> Vec<u8> {
        self.content.clone()
    }

    fn get_mime(&self) -> Mime {
        self.content_type
    }

    fn get_len(&self) -> usize {
        self.content_len
    }
}

impl NamedFile {
    /// Reads in a file as a [NamedFile]. Ignores seqences like `..`
    ///
    /// # Panics
    ///
    /// Panics if a [PathBuf] can't be convertet to a [str]
    ///
    /// # Errors
    ///
    /// Can give a [Status::NotFound] if it can't find the file
    ///
    /// This function will return an error if .
    pub fn open(path: PathBuf) -> Result<NamedFile, Status> {
        let path = proove_path(path);
        let data = fs::read(&path);
        let data = match data {
            Ok(dat) => dat,
            Err(_) => {
                return Err(Status::NotFound);
            }
        };
        Ok(NamedFile {
            content_len: data.len(),
            content_type: Mime::from_filename(path.to_str().unwrap()),
            content: data,
        })
    }
}

/// Validates a path so that seqences like `//` and `..` are omitted
///
/// # Panics
///
/// Panics if it can't convert a [PathBuf] to a [str]
fn proove_path(path: PathBuf) -> PathBuf {
    PathBuf::from(
        path.to_str()
            .unwrap()
            .split('/')
            .filter(|&val| val != ".." && !val.is_empty())
            .collect::<Vec<&str>>()
            .join("/"),
    )
}