use chrono::{DateTime, Duration, Utc};
use once_cell::sync::Lazy;
use reqwest::{Client};
use std::{collections::HashMap, vec};
use std::fmt::Debug;
use std::sync::{Arc};
use tokio::sync::{RwLock};
use dotenv::dotenv;
use strum_macros::Display;
use tokio::time::sleep;
use crate::config::Config;
use crate::post_history::{SeriesHistory};

mod config;
mod jnovel;
mod bot;
mod lemmy;
mod tui;
mod post_history;

pub static HTTP_CLIENT: Lazy<Client> = Lazy::new(|| {
    Client::builder()
        .timeout(Duration::seconds(30).to_std().unwrap())
        .connect_timeout(Duration::seconds(30).to_std().unwrap())
        .build()
        .expect("build client")
});

#[derive(Clone, Debug)]
pub(crate) struct SharedData {
    messages: Vec<Message>,
    config: Config,
    post_history: SeriesHistory,
    start: DateTime<Utc>,
}

impl SharedData {
    pub(crate) fn new() -> Self {
        SharedData {
            messages: vec![],
            config: Config {
                instance: "".to_string(),
                status_post_url: None,
                config_reload_seconds: 0,
                protected_communities: vec![],
                series: vec![],
            },
            post_history: SeriesHistory {
                series: HashMap::new(),
            },
            start: Utc::now(),
        }
    }

    pub(crate) fn get_messages(&self, errors: bool, warnings: bool, infos: bool) -> Vec<Message> {
        self.messages.iter().filter(|msg| {
            match msg {
                Message::Error(_) => errors,
                Message::Warning(_) => warnings,
                Message::Info(_) => infos,
            }
        }).cloned().collect()
    }
}

#[derive(Clone, Debug, Display, PartialEq)]
pub(crate) enum Message {
    Info(String),
    Warning(String),
    Error(String),
}

impl Message {
    pub(crate) fn content(&self) -> String {
        match self {
            Message::Info(msg) => msg.clone(),
            Message::Warning(msg) => msg.clone(),
            Message::Error(msg) => msg.clone(),
        }
    }
}

#[tokio::main]
async fn main() {
    dotenv().ok();
    let mut data = SharedData::new();


    loop {
        let write_data = Arc::new(RwLock::new(data.clone()));
        let read_data = write_data.clone();
        let persistent_data = write_data.clone();

        let tui_thread = tokio::spawn(async move { tui::run(read_data).await });
        let bot_thread = tokio::spawn(async move { bot::run(write_data).await });

        let _ = bot_thread.await;
        tui_thread.abort();

        data = persistent_data.read().await.clone();
        data.messages.push(Message::Error("Bot crashed due to unknown Error, restarting thread after wait...".to_string()));
        sleep(Duration::seconds(5).to_std().expect("Conversion should always work since static")).await;
    }
}