use std::collections::HashMap;
use std::fs;
use std::fs::OpenOptions;
use std::io::Write;
use std::path::Path;
use serde_derive::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Clone, Debug)]
pub(crate) struct SeriesHistory {
    pub(crate) series: HashMap<String, PostHistory>,
}

impl SeriesHistory {
    pub(crate) fn load_history() -> Result<Self, String> {
        match Path::new("history.toml").exists() {
            true => {
                let file_contents: String = match fs::read_to_string("history.toml") {
                    Ok(data) => data,
                    Err(e) => return Err(format!("{}", e)),
                };

                let history: Result<SeriesHistory, toml::de::Error> = match file_contents.len() {
                    0 => return Ok(SeriesHistory {
                        series: HashMap::new(),
                    }),
                    _ => toml::from_str(file_contents.as_str()),
                };

                match history {
                    Ok(data) => Ok(data),
                    Err(e) => Err(format!("{}", e))
                }
            },
            false => {
                Ok(SeriesHistory {
                    series: HashMap::new(),
                })
            }
        }
    }

    pub(crate) fn save_history(&self) -> std::io::Result<usize> {
        let mut file = OpenOptions::new()
            .read(true)
            .write(true)
            .create(true)
            .open("history.toml")
            .unwrap();

        let json_data = toml::to_string_pretty(&self).unwrap();

        file.write(json_data.as_bytes())
    }

    pub(crate) fn check_for_post(&self, series: &str, part: &str, title: &str) -> bool {
        if let Some(series_map) = self.series.get(series) {
            if let Some(part_info) = series_map.parts.get(part) {
                return part_info.volume == title || part_info.chapter == title
            }
        }
        false
    }

    pub(crate) fn get_series(&self, series: &str) -> PostHistory {
        match self.series.get(series) {
            Some(history) => history.clone(),
            None => PostHistory {
                parts: HashMap::new()
            }
        }
    }

    pub(crate) fn set_series(&mut self, series: &str, data: PostHistory) {
        self.series.entry(series.to_string()).and_modify(|val| {
            *val = data.clone()
        })
            .or_insert(data);
    }
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub(crate) struct PostHistory {
    pub(crate) parts: HashMap<String, PostHistoryInner>,
}

impl PostHistory {
    pub(crate) fn get_part(&self, part: &str) -> PostHistoryInner {
        match self.parts.get(part) {
            Some(history) => history.clone(),
            None => PostHistoryInner {
                volume: "".to_string(),
                chapter: "".to_string(),
            }
        }
    }

    pub(crate) fn set_part(&mut self, part: &str, data: PostHistoryInner) {
        self.parts.entry(part.to_string()).and_modify(|val| {
                *val = data.clone()
            })
            .or_insert(data);
    }
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub(crate) struct PostHistoryInner {
    pub(crate) volume: String,
    pub(crate) chapter: String,
}