use std::path::PathBuf;
use std::sync::{Arc, RwLock};
use chrono::{Timelike, Utc};
use crate::settings::PostBody::Description;
use lemmy_db_schema::PostFeatureType;
use lemmy_db_schema::sensitive::SensitiveString;
use serde_derive::{Deserialize, Serialize};
use crate::lemmy::{Lemmy, PartInfo, PostType};
use crate::post_history::{SeriesHistory};
use crate::fetchers::{FetcherTrait, Fetcher};
use crate::fetchers::jnovel::{JNovelFetcher};
use crate::logging::Logging;
use crate::settings::PostBody;

#[derive(Serialize, Deserialize, Clone, Debug)]
pub(crate) struct SingleBotConfig {
    pub(crate) instance: String,
    username: SensitiveString,
    password: SensitiveString,
    pub(crate) status_post_url: Option<String>,
    pub(crate) protected_communities: Vec<String>,
    pub(crate) series: Vec<SeriesConfig>,
}

impl SingleBotConfig {
    pub(crate) fn load(&self) -> Self {
        if self.instance.is_empty() {
            panic!("bot instance not set!")
        }

        if self.username.is_empty() {
            panic!("bot username not set!")
        }

        if self.password.is_empty() {
            panic!("bot password not provided!")
        }

        self.series.iter().for_each(|series| {
            if series.prepub_community.post_body == Description {
                panic!("'Description' type Post Body only supported for Volumes!")
            }
        });
        self.clone()
    }

    pub(crate) fn get_path() -> PathBuf {
        confy::get_configuration_file_path(env!("CARGO_PKG_NAME"), "config").expect("Application will not without confy")
    }

    pub(crate) fn get_username(&self) -> SensitiveString {
        self.username.clone()
    }

    pub(crate) fn get_password(&self) -> SensitiveString {
        self.password.clone()
    }
}

impl Default for SingleBotConfig {
    fn default() -> Self {
        SingleBotConfig {
            instance: "".to_owned(),
            username: SensitiveString::from("".to_owned()),
            password: SensitiveString::from("".to_owned()),
            status_post_url: None,
            protected_communities: vec![],
            series: vec![],
        }
    }
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub(crate) struct SeriesConfig {
    pub(crate) slug: String,
    pub(crate) parted: bool,
    pub(crate) prepub_community: PostConfig,
    pub(crate) volume_community: PostConfig,
    pub(crate) fetcher: Fetcher
}

impl SeriesConfig {
    pub(crate) fn update(&self, history: &mut SeriesHistory, lemmy: &Lemmy, config: &Arc<RwLock<SingleBotConfig>>) {
        let info_msg = format!("Checking {} for Updates", self.slug);
        Logging::info(info_msg.as_str());

        let mut fetcher: Fetcher = match &self.fetcher {
            Fetcher::Jnc(_) => {
                Fetcher::Jnc(JNovelFetcher::new())
            },
            /*default => {
                let err_msg = format!("Fetcher {default} not implemented");
                error!(err_msg);
                return;
            }*/
        };

        match fetcher {
            Fetcher::Jnc(ref mut jnc) => {
                jnc.set_series(self.slug.clone());
                jnc.set_part_option(self.parted);
            }
        }

        let post_list = match fetcher.check_feed() {
            Ok(data) => data,
            Err(_) => {
                let err_msg = format!("While checking feed for {}", self.slug);
                Logging::error(err_msg.as_str());
                return;
            }
        };

        if post_list.is_empty() && Utc::now().minute() % 10 == 0 {
            let info_msg = "No Updates found";
            Logging::info(info_msg);
        }

        for post_info in post_list.iter() {
            if history.check_for_post(
                self.slug.as_str(),
                post_info.get_part_info().unwrap_or(PartInfo::NoParts).as_string().as_str(),
                post_info.get_info().title.as_str()
            ) {
                continue
            }

            let post_data = post_info.get_post_data(self, lemmy);

            let info = format!(
                "Posting '{}' to {}",
                post_info.get_info().title.as_str(),
                post_info.get_post_config(self).name.as_str()
            );
            Logging::info(info.as_str());

            let post_id = match lemmy.post(post_data) {
                Some(data) => data,
                None=> {
                    Logging::error("Error posting chapter");
                    return;
                }
            };

            let read_config = config.read().expect("Read Lock Failed").clone();

            if post_info.get_post_config(self).pin_settings.pin_new_post_community
                && !read_config
                .protected_communities
                .contains(&post_info.get_post_config(self).name)
            {
                let info = format!(
                    "Pinning '{}' to {}",
                    post_info.get_info().title,
                    post_info.get_post_config(self).name.as_str()
                );
                Logging::info(info.as_str());
                let pinned_posts = lemmy.get_community_pinned(lemmy.get_community_id(&post_info.get_post_config(self).name)).unwrap_or_else(|| {
                    Logging::error("Pinning of Post to community failed");
                    vec![]
                });
                if !pinned_posts.is_empty() {
                    let community_pinned_post = &pinned_posts[0];
                    if lemmy.unpin(community_pinned_post.post.id, PostFeatureType::Community).is_none() {
                        Logging::error("Error un-pinning post");
                    }
                }
                if lemmy.pin(post_id, PostFeatureType::Community).is_none() {
                    Logging::error("Error pinning post");
                }
            } else if read_config
                .protected_communities
                .contains(&post_info.get_post_config(self).name)
            {
                let message = format!(
                    "Community '{}' for Series '{}' is protected. Is this intended?",
                    &post_info.get_post_config(self).name, self.slug
                );
                Logging::warn(message.as_str());
            }

            if post_info.get_post_config(self).pin_settings.pin_new_post_local {
                let info = format!("Pinning '{}' to Instance", post_info.get_info().title);
                Logging::info(info.as_str());
                let pinned_posts = match lemmy.get_local_pinned() {
                    Some(data) => {data}
                    None => {
                        Logging::error("Error fetching pinned posts");
                        vec![]
                    }
                };

                if !pinned_posts.is_empty() {
                    for pinned_post in pinned_posts {
                        if read_config
                            .protected_communities
                            .contains(&pinned_post.community.name)
                        {
                            continue;
                        } else {
                            let community_pinned_post = &pinned_post;
                            if lemmy.unpin(community_pinned_post.post.id, PostFeatureType::Local).is_none() {
                                Logging::error("Error pinning post");
                                continue;
                            }
                            break;
                        }
                    }
                }
                if lemmy.pin(post_id, PostFeatureType::Local).is_none() {
                    Logging::error("Error pinning post");
                };
            }

            let mut series_history = history.get_series(self.slug.as_str());
            let mut part_history = series_history.get_part(post_info.get_part_info().unwrap_or(PartInfo::NoParts).as_string().as_str());

            match post_info.post_type {
                Some(post_type) => {
                    match post_type {
                        PostType::Chapter => part_history.chapter = post_info.get_info().title,
                        PostType::Volume => part_history.volume = post_info.get_info().title,
                    }
                }
                None => part_history.chapter = post_info.get_info().title,
            }

            series_history.set_part(post_info.get_part_info().unwrap_or(PartInfo::NoParts).as_string().as_str(), part_history);
            history
                .set_series(self.slug.as_str(), series_history);
            Logging::debug("Saving History");
            history.save_history();
        }
    }
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub(crate) struct PostConfig {
    pub(crate) name: String,
    pub(crate) pin_settings: PinConfig,
    pub(crate) post_body: PostBody,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub(crate) struct PinConfig {
    pub(crate) pin_new_post_local: bool,
    pub(crate) pin_new_post_community: bool,
}