use std::{fs::File, io::Read};

use mime_guess::from_path;

use crate::{assets::cache::{Asset, LruCache}, request::Request, response::{HTTP_OK, Response}, utils::timestamp};

pub(crate) mod cache;

pub(crate) struct Assets {
    path: String,
    size: usize,
    cache: LruCache,
    expires: u128,
}

impl Assets {
    pub fn new(path: String, max_size_kilobytes_cache_size: usize, expires_in_seconds: u128) -> Self {
        return Self {
            path: path,
            size: max_size_kilobytes_cache_size,
            cache: LruCache::new(100),
            expires: expires_in_seconds
        }
    }

    pub fn handle<'a>(&mut self, req: &'a mut Request, res: &'a mut Response) {
        if req.path.trim_matches('/').is_empty() {
            return;
        }

        if let Some(asset) = self.get_asset(format!("{}/{}", self.path, req.path.trim_start_matches("/"))) {
            res.view = None; // Clear view if set to not found (404)
            res.body(&asset.data)
                .header("Content-Type", &asset.content_type)
                .status_code(HTTP_OK);
        }
    }

    fn get_asset(&mut self, filename: String) -> Option<Asset> {
        return match self.cache.get(&filename, self.expires) {
            Some(asset) => {
                Some(asset.clone())
            },
            None => {
                match self.read_asset(&filename) {
                    Some(asset) => {
                        if self.size <= asset.size {
                            self.cache.insert(String::from(filename), asset.clone());
                        }

                        return Some(asset);
                    },
                    None => {
                        None
                    },
                }
            },
        }
    }

    fn read_asset(&self, filename: &str) -> Option<Asset> {
        return match File::open(filename) {
            Ok(mut file) => {
                let mut data= String::new();

                return Some(Asset {
                    size: file.read_to_string(&mut data).unwrap(),
                    expires: timestamp().unwrap() + (1000 * self.expires),
                    data: data.into_bytes(),
                    content_type: self.get_content_type(filename),
                });
            },
            Err(_) => {
                None
            },
        };
    }

    fn get_content_type(&self, filename: &str) -> String {
        return from_path(filename.split("/").last().unwrap_or("")).first_or_octet_stream().to_string();
    }
}