use std::{
    io::Write,
    // sync::Arc,
    fmt::Debug,
    fs,
    path::{Path, PathBuf},
    io::{Error, ErrorKind},
    
};
use log::error;
// use async_session::{MemoryStore, Session, SessionStore};
use serde::{Deserialize, Serialize};
//use tiitls_utils::logs::file;
use uuid::Uuid;
use crate::defs::{
    FILE_SCHEME,
    SID_SETTINGS_FILE,
    SID_REQUESTS_FILE,
    SID_TRACE_FILE,
//    SID_UI_FILE,
//    UI_SETTINGS_FILE,
//    SidSettings,
    Config as SessionsConfig, 
    TraceData,
    UserAction,
};

#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
pub struct UserId(Uuid);

impl UserId {
    #[allow(dead_code)]
    pub fn new() -> Self {
        Self(Uuid::new_v4())
    }
    pub fn from(data: &str) -> Option<Self> {
        match Uuid::parse_str(data) {
            Ok(uuid) => Some(Self(uuid)),
            Err(_) => None,
        }
    }
}

impl std::fmt::Display for UserId {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}
impl UserId {

    /// Log user actions to config.user_store_access|reqs_store_file/user_id/config.user_store_access path
    /// Add a line with timestamp 
    /// It also get ReqHeaderMap to add more data ?
    #[allow(dead_code)]
    pub fn log_user_id_action(&self, action: UserAction, sessions_config: &SessionsConfig) -> std::io::Result<()> {
        let id_path = format!("{}/{}",
            sessions_config.users_store_uri.replace(FILE_SCHEME, ""),
            self);
        if ! Path::new(&id_path).exists() {
            fs::create_dir(&id_path)?;
        }
        let now = chrono::Utc::now().timestamp().to_string();
        let (log_data, file_path) = match action {
            UserAction::Access => (
               format!("{}\n",now),
               format!("{}/{}",&id_path,sessions_config.user_store_access)
            ),
            UserAction::Log(info) => (
               format!("notity:{}:{}\n",now,info),
               format!("{}/{}",&id_path,sessions_config.user_store_access)
            ),
            UserAction::Request(info) => (
               format!("req:{}:{}\n",now,info),
               format!("{}/{}",&id_path,sessions_config.user_store_access)
            ),
            UserAction::View(info) => (
               format!("view:{}:{}\n",now,info),
               format!("{}/{}",&id_path,sessions_config.user_store_access)
            ),
            UserAction::List(info) => (
               format!("list:{}:{}\n",now,info),
               format!("{}/{}",&id_path,sessions_config.user_store_access)
            ),
            UserAction::Profile(info) => (
               format!("profile:{}:{}\n",now,info),
               format!("{}/{}",&id_path,sessions_config.user_store_access)
            ),
            UserAction::Other => (
               format!("other:{}::\n",now),
               format!("{}/{}",&id_path,sessions_config.user_store_access)
            ),
        };
        self.write_data(&file_path, &log_data, false)
        // if ! Path::new(&file_path).exists() {
        //     fs::write(&file_path, log_data)?;
        // } else  {
        //     let access_id_file = fs::OpenOptions::new()
        //         .write(true)
        //         .append(true) // This is needed to append to file
        //         .open(&file_path);
        //     if let Ok(mut file) = access_id_file {
        //         let _ = file.write_all(log_data.as_bytes())?;
        //     }
        // }
        // Ok(())
    }
    fn id_path(&self, file: &str, sessions_config: &SessionsConfig) -> String {
        format!("{}/{}/{}",
          sessions_config.users_store_uri.replace(FILE_SCHEME, ""),
          self,file)
    }
    #[allow(dead_code)]
    fn write_data(&self, file_path: &str, data: &str, overwrite: bool) -> std::io::Result<()> {
        let check_path = |path: &Path| -> std::io::Result<()> {
            if ! Path::new(&path).exists() {
                if let Err(e) = std::fs::create_dir(&path) {
                    return Err(Error::new( ErrorKind::InvalidInput,
                        format!("Error create path {}: {}",&path.display(), e)
                    ));
                    // std::process::exit(2)
                }
            }
            Ok(())
        };
        if file_path.is_empty() || data.is_empty() {
            return Err(Error::new(
                ErrorKind::InvalidInput,
                format!("Error save {}",&file_path)
            ));
        }
        if ! Path::new(&file_path).exists() {
            let path = PathBuf::from(&file_path);
            if let Some(dir_path) =  path.parent() {
                if ! Path::new(&dir_path).exists() {
                    if let Some(parent_dir_path) = dir_path.parent() { 
                        if ! Path::new(&parent_dir_path).exists() {
                            let res = check_path(&parent_dir_path);
                            if res.is_err() { return res; }
                        }
                    }
                    let res = check_path(&dir_path);
                    if res.is_err() { return res; }
                }
            }
        }
        if overwrite || ! Path::new(&file_path).exists() {
            fs::write(&file_path, data)?;
            println!("Overwrite: {}",&file_path);
        } else {
            let sid_settings_file = fs::OpenOptions::new()
                .write(true)
                .append(true) // This is needed to append to file
                .open(&file_path);
            if let Ok(mut file) = sid_settings_file {
                file.write_all(data.as_bytes())?;
            }
            println!("write: {}",&file_path);
        }
        Ok(())
    }
    
    #[allow(dead_code)]
    pub fn sid_settings_path(&self, sessions_config: &SessionsConfig) -> String {
       self.id_path(SID_SETTINGS_FILE, sessions_config)
    }
    #[allow(dead_code)]
    pub fn sid_settings_content(&self, sessions_config: &SessionsConfig) -> String {
        let file_path = self.sid_settings_path(sessions_config);
        if ! Path::new(&file_path).exists() {
            String::from("")
        } else {
            match std::fs::read_to_string(&file_path) {
                Ok(content) => content,
                Err(e) => {
                    error!("Error read {}: {}",&file_path,e);
                    String::from("")
                }
            }
        }
    }
    // pub fn sid_settings(&self, sessions_config: &SessionsConfig) -> SidSettings {
    //     let file_path = self.id_path(SID_SETTINGS_FILE, sessions_config);
    //     if Path::new(&file_path).exists() {
    //         match std::fs::read_to_string(&file_path) {
    //             Ok(content) => sessions_config.sid_settings.from_json(&content,&file_path),
    //             Err(e) => {
    //                 error!("Error read {}: {}",&file_path,e);
    //                 sessions_config.sid_settings.to_owned()
    //             }
    //         }
    //     } else {
    //         //dbg!(&sessions_config.sid_settings);
    //         sessions_config.sid_settings.to_owned()
    //     }
    // }
    // pub fn save_sid_settings(&self, sid_settings: SidSettings, sessions_config: &SessionsConfig) -> std::io::Result<()> {
    //     let file_path = self.id_path(SID_SETTINGS_FILE, sessions_config);
    //     let data = sid_settings.to_json();
    //     self.write_data(&file_path, &data, true)
    // }
    #[allow(dead_code)]
    pub fn save_trace_data(&self, trace_data: TraceData, sessions_config: &SessionsConfig) -> std::io::Result<()> {
        // let file_curenv_path = self.id_path(&format!("{}/{}",trace_data.server,SID_CURENV_FILE), sessions_config);
        // let res_curenv = self.write_data(&file_curenv_path, &trace_data.curenv, true);
        // let _ = res_curenv.as_ref().unwrap_or_else(|e|{
        //     println!("Error save trace curenv: {}",&e);
        //     &()
        // });
        // if res_curenv.is_err() {
        //     return res_curenv;
        // }
        // if trace_data.ui.len() > 0 {
        //     let file_ui_path = self.id_path(&format!("{}/{}",trace_data.server,SID_UI_FILE), sessions_config);
        //     let res_ui = self.write_data(&file_ui_path, &trace_data.ui, true); 
        //     let _ = res_ui.as_ref().unwrap_or_else(|e|{
        //         println!("Error save trace ui: {}",&e);
        //         &()
        //     });
        //     if res_ui.is_err() { return res_ui; }
        // }
        let file_trace_path = self.id_path(&format!("{}/{}",trace_data.server,SID_TRACE_FILE), sessions_config);
        let contents = trace_data.contents_to_json();
        let mut result = Ok(());
        let lines = contents.len() - 1;
        for (idx, line) in contents.iter().enumerate() {
            let prfx = if idx == 0 && Path::new(&file_trace_path).exists() {
              ",\n"
            } else {
              ""
            };
            let sfx = if idx == lines {
                ""
            } else {
                ",\n"
            };
           result = self.write_data(
                &file_trace_path, 
                format!("{}{}{}",&prfx,&line,&sfx).as_str(),
                false
            );
            let _ = result.as_ref().unwrap_or_else(|e|{
                println!("Error save trace contets: {} line {}",&e, &idx);
                &()
            });
            if result.is_err() {
                break;
            }
        }
        result
    }
    #[allow(dead_code)]
    pub fn save_sid_request(&self, data: &str, sessions_config: &SessionsConfig) -> std::io::Result<()> {
        let file_path = self.id_path(SID_REQUESTS_FILE, sessions_config);
        println!("---- {}",&file_path);
        self.write_data(&file_path, &data, false)
    }
    #[allow(dead_code)]
    pub fn read_sid_requests(&self, sessions_config: &SessionsConfig) -> Result<Vec<String>, Box<dyn std::error::Error>> {
        let file_path = self.id_path(SID_REQUESTS_FILE, sessions_config);
        let data = fs::read_to_string(file_path)?;
        Ok(data.split("\n").map(|s| s.to_string()).collect())
    }
}