use std::sync::Arc;
use urlencoding::{encode,decode};
// use tokio::sync::RwLock;
use axum::{
    //extract::{self,Request,Query},
    http::{
        StatusCode,
        Uri,
        header::HeaderMap,// HeaderValue},
    },
    Json,
    routing::{get,post},
    Extension,
    extract::ConnectInfo,
    response::{IntoResponse,Response,Redirect},
//    http::Request, handler::HandlerWithoutStateExt, http::StatusCode, routing::get, Router,    
    Router,
};
use tower_cookies::{Cookie, Cookies};

use crate::{
    SESSION_COOKIE_NAME,
    DEFAULT_ROLES,
    route,
    defs::{
        AppDBs,
        AuthState,
        SessionStoreDB,
        ReqHandler,
        ReqHeaderMap,
        MailMessage,
//        TotpAlgorithm,
        TotpMode,
        Random,
        AppConnectInfo,
        // UserNotifyData,
        // TOKEN_AUTH_VALUE,
        // TOKEN_KEY_VALUE,
    },
    users::{
        User,
        // UserStore,
//        UserId,
        UserData,
        UserLogin,
        UserItem,
        UserStatus,
        UserInvitation,
    },
    login_password::{generate_hash,verify_password},
    handlers::{
        add_session_cookie,
        get_auth_state,
    },
};
pub fn users_router_handlers() -> Router {
    async fn main_handler(
        header: HeaderMap,
        uri: Uri,
        Extension(app_dbs): Extension<Arc<AppDBs>>,
        Extension(random): Extension<Random>,
        Extension(cookies): Extension<Cookies>,
        ConnectInfo(app_connect_info): ConnectInfo<AppConnectInfo>,
    ) -> Response {
        // if let Some(cookie_value) = get_cookie(&req) {
        //     println!("cookie_value: {}",&cookie_value.to_string());
        //     // TODO check value 
        // }
        //let  has_cookie: bool;
        // dbg!(&auth_state.session);
        // if auth_state.user.is_none() {
        //     eprintln!("No user found in session");
        // }
        let auth_state = get_auth_state(true, &cookies, &app_dbs).await;
        //    has_cookie=true;
            // TODO check value 
            // let sid: String = format!("{}",&auth_state.sid());
            // println!("auth_sid: {}",&sid);
            // println!("id: {}",&auth_state.id());
            // println!("is_expired: {}",&auth_state.ses_expired());
            // println!("is_destroyed: {}",&auth_state.ses_destroyed());
            // println!("validate: {}",&auth_state.ses_validate());
            // let _u = &auth_state.user;
        let mut req_handler = ReqHandler::new(
            ReqHeaderMap::new(header, &format!("{}",&uri.path().to_string()), &app_connect_info),
            &app_dbs,
            &uri,
            &auth_state,
            &random,
            "main_handler"
        );
        // if let Some(a) = auth_state {
        //     println!("Auth State from root");
        // }
        // let uri_path = format!("{}",&uri.path().to_string());
        // dbg!("uri: {}",&uri_path);
        req_handler.prepare_response();
        req_handler.context.insert("with_menu", "1");
        let result = if let Some(tpl) = app_dbs.config.tpls.get("main") {
            req_handler.render_template(&tpl,"main")
        } else {
            String::from("main")
        };
        let _ = req_handler.trace_req(String::from("main request"));
        // if !has_cookie {
        //     let mut u128_pool = [0u8; 16];
        //     match random.lock() {
        //         Ok(mut r) => r.fill_bytes(&mut u128_pool),
        //         Err(e) => println!("Error random: {}",e),
        //     }
        //     let session_token = u128::from_le_bytes(u128_pool).to_string();
        //     let user_data = format!("{}|{}","jesus" ,"admin,dev");
        //     let result_store = SessionStoreDB::store_session_data(&session_token,&user_data,&app_dbs).await;
        //     println!("Rest store: {}",&result_store);
        //     let cookie = Cookie::build(SESSION_COOKIE_NAME, session_token.to_owned())
        //     //  .domain(domain)
        //         .path("/")
        //         .secure(true)
        //         .http_only(true)
        //         .finish();
        //     cookies.remove(Cookie::new(SESSION_COOKIE_NAME, ""));
        //     cookies.add(cookie);
        // }
        (
            req_handler.req_header.header,
            result.to_owned()
        ).into_response()
    }
    // async fn auto_handler(
    //     //req: Request,
    //     header: HeaderMap,
    //     uri: Uri,
    //     Extension(app_dbs): Extension<Arc<AppDBs>>,
    //     Extension(random): Extension<Random>,
    //     Extension(cookies): Extension<Cookies>,
    //     // _auth_state: AuthState,
    // ) -> Response {
    //     // if let Some(cookie_value) = get_cookie(&req) {
    //     //     println!("cookie_value: {}",&cookie_value.to_string());
    //     //     // TODO check value 
    //     // }
    //     let auth_state = get_auth_state(true, &cookies, &app_dbs).await;
    //     let has_cookie: bool;
    //     if let Some(cookie_value) = cookies.get(SESSION_COOKIE_NAME) {
    //         println!("cookie_value: {}",&cookie_value.to_string());
    //         //has_cookie=true;
    //         has_cookie=false;
    //         // TODO check value 
    //     } else {
    //         has_cookie=false;
    //     }
    //     // if let Some(a) = auth_state {
    //     //     println!("Auth State from root");
    //     // }
    //     let uri_path = format!("{}",&uri.path().to_string());
    //     let file =  "hello.html";
    //     let result = app_dbs.tera.render(&file, &app_dbs.context).unwrap_or_else(|e|{
    //         println!("Error render {}: {}",&file,e);
    //         String::from("")
    //     });
    //     /*
    //     if !has_cookie {
    //         let mut u128_pool = [0u8; 16];
    //         match random.lock() {
    //             Ok(mut r) => r.fill_bytes(&mut u128_pool),
    //             Err(e) => println!("Error random: {}",e),
    //         }
    //         let session_token = u128::from_le_bytes(u128_pool).to_string();
    //         let user_data = format!("{}|{}","jesus" ,"admin,dev");
    //         let result_store = SessionStoreDB::store_session_data(&session_token,&user_data,&app_dbs).await;
    //         println!("Rest store: {}",&result_store);
    //         let cookie = Cookie::build(SESSION_COOKIE_NAME, result_store)
    //         //  .domain(domain)
    //             .path("/")
    //             .secure(true)
    //             .http_only(true)
    //             .finish();
    //         cookies.remove(Cookie::new(SESSION_COOKIE_NAME, ""));
    //         cookies.add(cookie);
    //     }
    //    */
    //     dbg!("uri: {}",&uri_path);
    //     (
    //         header,
    //         result.to_owned()
    //     ).into_response()
    // //     "Hello, World!"
    // }

    async fn signup_handler(
        header: HeaderMap,
        uri: Uri,
        Extension(app_dbs): Extension<Arc<AppDBs>>,
        Extension(cookies): Extension<Cookies>,
        Extension(random): Extension<Random>,
        ConnectInfo(app_connect_info): ConnectInfo<AppConnectInfo>,
    ) -> Response {
        let auth_state = get_auth_state(true, &cookies, &app_dbs).await;
        let mut req_handler = ReqHandler::new(
            ReqHeaderMap::new(header, &format!("{}",&uri.path().to_string()), &app_connect_info),
            &app_dbs,
            &uri,
            &auth_state,
            &random,
            "signup_handler"
        );
        if !auth_state.is_admin() {
           let total_users = User::count(&app_dbs.user_store).await.unwrap_or_else(|e|{ 
                println!("Count error: {}",e);
                0
            });
            if ! app_dbs.config.signup_mode.contains("open") && total_users != 0 {
                let msg = format!("Config signup mode not open: {}", app_dbs.config.signup_mode);
                println!("{}",&msg);
                let _ = req_handler.trace_req(msg);
                return Redirect::temporary( &format!("/")).into_response();
            }
            let total_users = User::count(&app_dbs.user_store).await.unwrap_or_else(|e|{ 
                println!("Count error: {}",e);
                -1
            });
            // Fake to insert firt time admin user ...
            if total_users < 1  {
                let isadmin=true;
                req_handler.context.insert("isadmin", &isadmin);
            }
        }
        let mut res_headers = HeaderMap::new();
        if req_handler.req_header.is_browser() {
            res_headers.append(axum::http::header::CONTENT_TYPE,"text/html; charset=utf-8".parse().unwrap());
        }
        req_handler.context.insert("password_score", &app_dbs.config.password_score);
        req_handler.context.insert("totp_mode", &format!("{}",&app_dbs.config.totp_mode));
        // req_handler.context.insert("with_menu", "1");
        if app_dbs.config.totp_mode != TotpMode::No {
            match req_handler.otp_generate() {
                Ok(totp) => {
                    req_handler.context.insert("otp_code", &totp.get_secret_base32());
                    req_handler.context.insert("otp_url", &totp.get_url());
                    req_handler.context.insert("otp_qr", &totp.get_qr().unwrap_or_default());
                    req_handler.context.insert("totp_digits", &app_dbs.config.totp_digits);
                    req_handler.context.insert("totp_algorithm",&format!("{}",&app_dbs.config.totp_algorithm));
                },
                Err(e) => {
                    println!("Error TOTP generartor: {}",e);
                }
            }
        }
        req_handler.context.insert("admin_fields", &app_dbs.config.admin_fields);
        let result = if let Some(tpl) = app_dbs.config.tpls.get("signup") {
            req_handler.render_template(&tpl,"signup")
        } else {
            String::from("signup")
        };
        let _ = req_handler.trace_req(format!("Signup request"));
        (
            res_headers,
            result.to_owned()
        ).into_response()
    }
    
    async fn post_signup_handler(
        header: HeaderMap,
        uri: Uri,
        Extension(app_dbs): Extension<Arc<AppDBs>>,
        Extension(random): Extension<Random>,
        Extension(cookies): Extension<Cookies>,
        ConnectInfo(app_connect_info): ConnectInfo<AppConnectInfo>,
        Json(user_data): Json<UserData>,
    ) -> Response {
        let auth_state = get_auth_state(true, &cookies, &app_dbs).await;
        let req_handler = ReqHandler::new(
            ReqHeaderMap::new(header, &format!("{}",&uri.path().to_string()), &app_connect_info),
            &app_dbs,
            &uri,
            &auth_state,
            &random,
            "post_signup_handler"
        );
        if user_data.name.is_empty() {
            let _ = req_handler.trace_req(format!("No name found"));
            return (
                StatusCode::BAD_REQUEST,
                req_handler.req_header.header,
                "Error"
            ).into_response();
        }
        if user_data.password.is_empty() {
            let _ = req_handler.trace_req(format!("user '{}' no password found",&user_data.name));
            return (
                StatusCode::BAD_REQUEST,
                req_handler.req_header.header,
                "Error"
            ).into_response();
        }
        let passwd_score = User::password_score(&user_data.password);
        if passwd_score < app_dbs.config.password_score {
            let _ = req_handler.trace_req(format!("User '{}' password '{}' score: {} under {}"
                ,&user_data.name,&user_data.password, passwd_score,app_dbs.config.password_score)
            );
            return (
                StatusCode::UNAUTHORIZED,
                req_handler.req_header.header,
                "Error"
            ).into_response();
        }
        let new_hash = generate_hash(&user_data.password);
        // println!("password: {}", &new_hash);
        // // if  user_data.password.is_empty() {
        // //     return Err(error_page(&SignupError::MissingDetails));
        // // }
        // match verify_password(&user_data.password, &new_hash) {
        //     Ok(_) => { 
        //         println!("Is valid!");
        //     },
        //     Err(e) => {
        //         println!("NOT valid {}",e);
        //         //return Err(error_page(&SignupError::PasswordsDoNotMatch))
        //     }
        // }
        let otp_enabled: bool;
        let otp_verified: bool;
        let otp_base32: String;
        let otp_auth_url: String;
        let otp_defs: String;
        if app_dbs.config.totp_mode != TotpMode::No && !user_data.otp_auth.is_empty() {
            match req_handler.otp_check(&user_data.otp_code,&user_data.otp_auth, "") {
                Ok(val) => {
                    if val {
                        otp_enabled = true;
                    } else {
                        let _ = req_handler.trace_req(format!("User '{}' not valid TOTP code",&user_data.name));
                        // otp_enabled = false;
                        return (
                            StatusCode::UNAUTHORIZED,
                            req_handler.req_header.header,
                            "Error"
                        ).into_response();
                    }
                },
                Err(e) => {
                    println!("TOTP check: {}", e);
                    let _ = req_handler.trace_req(format!("User '{}' TOTP check error: {}",&user_data.name,e));
                    return (
                        StatusCode::UNAUTHORIZED,
                        req_handler.req_header.header,
                        "Error"
                    ).into_response();
                }
            } 
        } else {
            otp_enabled = false;
        }
        if otp_enabled  {
            otp_verified= true;
            otp_base32 = user_data.otp_code.to_owned();
            otp_auth_url = user_data.otp_url.to_owned();
            otp_defs = format!("{},{}",
                &app_dbs.config.totp_digits,
                format!("{}",&app_dbs.config.totp_algorithm),
            );
        } else {
            otp_verified = false;
            otp_base32 = String::from("");
            otp_auth_url = String::from("");
            otp_defs = String::from("");
        }
        let roles = if ! user_data.roles.is_empty() {
            user_data.roles.to_owned()
        } else {
            DEFAULT_ROLES.to_owned()
        };
        let total_users = User::count(&app_dbs.user_store).await.unwrap_or_else(|e|{ 
           let _ = req_handler.trace_req( format!("Users count error: {}",e));
            println!("Count error: {}",e);
            -1
        });
        let isadmin = if user_data.id == "A" || total_users < 1 {
            true
        } else { 
            false
        };
        let mut new_items = user_data.items.to_owned();
        new_items.remove("invite_key");
        new_items.remove("invite_id");
        let user = User{
            id: 0,
            name: user_data.name.to_owned(),
            fullname: user_data.name.to_owned(),
            email:   user_data.email.to_owned(),
            description: user_data.description.to_owned(),
            password: new_hash,
            otp_enabled,
            otp_verified,
            otp_base32,
            otp_auth_url,
            otp_defs,
            created: chrono::Utc::now().timestamp().to_string(),
            lastaccess: String::from(""),
            status: UserStatus::Created,
            items: User::json_items(new_items),
            isadmin,
            roles,
        };
        let usr_sel = User::select("name", &user_data.name, false, &app_dbs.user_store).await.unwrap_or_default();
        if usr_sel.name == user_data.name {
            // User already exists
            let _ = req_handler.trace_req(format!("User 'name' = '{}' already exists",&user_data.name));
            return (
                StatusCode::BAD_REQUEST,
                req_handler.req_header.header,
                "Error"
            ).into_response();
        }
        let str_user_data =  match user.add(&app_dbs.user_store).await {
            Ok(id) => {
                println!("user {} created -> {}", &user_data.name, id);
                format!("{}|{}|{}",id, &user_data.name, &user_data.roles)
            },
            Err(e) => {
                let _ = req_handler.trace_req(format!("User '{}' create error: {}",&user_data.name,e));
                println!("user {} error -> {:#}", &user_data.name, e);
                return (
                    StatusCode::NOT_FOUND,
                    req_handler.req_header.header,
                    "Error"
                ).into_response();
            }
        };
        let session_token = req_handler.new_token();
        println!("session: {}", &session_token.to_string());
        let session_cookie = add_session_cookie(true,&cookies, &session_token, &str_user_data, 0, &app_dbs, "/").await;
        let _ = req_handler.trace_req(format!("User '{}' created",&user_data.name));
        if app_dbs.config.verbose > 1 { println!("session cookie: {}", &session_cookie); }
        (
            req_handler.req_header.header,
            "Ok"
         ).into_response()
    }
    async fn login_handler(
        header: HeaderMap,
        uri: Uri,
        Extension(app_dbs): Extension<Arc<AppDBs>>,
        Extension(cookies): Extension<Cookies>,
        Extension(random): Extension<Random>,
        ConnectInfo(app_connect_info): ConnectInfo<AppConnectInfo>,
    ) -> Response {
        SessionStoreDB::cleanup_data(&app_dbs).await;
        let auth_state = get_auth_state(true, &cookies, &app_dbs).await;
        let mut req_handler = ReqHandler::new(
            ReqHeaderMap::new(header, &format!("{}",&uri.path().to_string()), &app_connect_info),
            &app_dbs,
            &uri,
            &auth_state,
            &random,
            "login_handler"
        );
        let total_users = User::count(&app_dbs.user_store).await.unwrap_or_else(|e|{ 
           let _ = req_handler.trace_req( format!("Users count error: {}",e));
            println!("Count error: {}",e);
            -1
        });
        if total_users < 1 {
           let _ = req_handler.trace_req(String::from("No users found"));
          return Redirect::temporary( &format!("/signup")).into_response();
        }

        let mut res_headers = HeaderMap::new();
        if req_handler.req_header.is_browser() {
            res_headers.append(axum::http::header::CONTENT_TYPE,"text/html; charset=utf-8".parse().unwrap());
        }
        // req_handler.context.insert("with_menu", "1");
        req_handler.context.insert("use_mail", &app_dbs.config.use_mail);
        req_handler.context.insert("totp_mode", &format!("{}",&app_dbs.config.totp_mode));
        if app_dbs.config.totp_mode != TotpMode::No {
            req_handler.context.insert("with_totp", "1");
            req_handler.context.insert("totp_digits", &app_dbs.config.totp_digits);
        }
        let _ = req_handler.trace_req(String::from("login request"));
        let result = if let Some(tpl) = app_dbs.config.tpls.get("login") {
            req_handler.render_template(&tpl,"login")
        } else {
            String::from("login")
        };
        (
            res_headers,
            result.to_owned()
        ).into_response()
    }
    async fn post_login_handler(
        header: HeaderMap,
        uri: Uri,
        Extension(app_dbs): Extension<Arc<AppDBs>>,
        Extension(cookies): Extension<Cookies>,
        Extension(random): Extension<Random>,
        ConnectInfo(app_connect_info): ConnectInfo<AppConnectInfo>,
        Json(user_login): Json<UserLogin>,
    ) -> Response {
        let auth_state = get_auth_state(true, &cookies, &app_dbs).await;
        let mut req_handler = ReqHandler::new(
            ReqHeaderMap::new(header, &format!("{}",&uri.path().to_string()), &app_connect_info),
            &app_dbs,
            &uri,
            &auth_state,
            &random,
            "post_login_handler"
        );
        if user_login.name.is_empty() || user_login.password.is_empty() {
            let _ = req_handler.trace_req(String::from("Empty name or password"));
        //     return Err(error_page(&SignupError::MissingDetails));
            return (
                StatusCode::BAD_REQUEST,
                req_handler.req_header.header,
                "Error"
            ).into_response();
        }
        let field = if user_login.name.contains("@") {
            "email"
        } else {
            "name"
        };
        let mut user_sel = User::select(&field, &user_login.name, false, &app_dbs.user_store).await.unwrap_or_else(|e|{
            println!("Error select: {}", e);
            User::default()
        });
        if user_sel.name.is_empty() {
            let _ = req_handler.trace_req(format!("No name '{}' found",&user_login.name));
            // User not exists
            return (
                StatusCode::BAD_REQUEST,
                req_handler.req_header.header,
                "Error data"
            ).into_response();
        }
        if user_sel.status != UserStatus::Active && user_sel.status != UserStatus::Created {
            let _ = req_handler.trace_req(format!("user '{}' in not valid status: {}",&user_login.name, &user_sel.status));
            return (
                StatusCode::UNAUTHORIZED,
                req_handler.req_header.header,
                "Error status"
            ).into_response();
        }
        if verify_password(&user_login.password, &user_sel.password).is_err() {
            let _ = req_handler.trace_req(format!("user '{}' not valid password: {}",&user_login.name, &user_sel.password));
            println!("password NOT valid");
            // TODO
            //return Err(error_page(&SignupError::PasswordsDoNotMatch))
            return (
                StatusCode::BAD_REQUEST,
                req_handler.req_header.header,
                "Error data"
            ).into_response();
        }
        let result=format!("{}:{}","OK",&user_sel.otp_verified);
        if app_dbs.config.totp_mode != TotpMode::No {
            if user_login.otp_auth.is_empty() 
                && (app_dbs.config.totp_mode == TotpMode::Mandatory || user_sel.otp_enabled)
            {
                let _ = req_handler.trace_req(format!("user '{}' not valid Totp: {}",&user_login.name, &user_sel.otp_enabled));
                return (
                    req_handler.req_header.header,
                    result 
                ).into_response();
            } else if user_sel.otp_enabled && user_sel.otp_verified 
                && !user_sel.otp_base32.is_empty() && !user_sel.otp_defs.is_empty() 
            {
                match req_handler.otp_check(&user_sel.otp_base32,&user_login.otp_auth, &user_sel.otp_defs) {
                    Ok(val) => {
                        if !val {
                            let _ = req_handler.trace_req(format!("user '{}' not valid TOTP code",&user_login.name));
                            return (
                                StatusCode::UNAUTHORIZED,
                                req_handler.req_header.header,
                                "Error"
                            ).into_response();
                        }
                    },
                    Err(e) => {
                        let _ = req_handler.trace_req(format!("user '{}' TOTP check error: {}",&user_login.name,e));
                        println!("TOTP check: {}", e);
                        return (
                            StatusCode::UNAUTHORIZED,
                            req_handler.req_header.header,
                            "Error"
                        ).into_response();
                    }
                } 
            } 
        }
        user_sel.lastaccess = chrono::Utc::now().timestamp().to_string();
        if user_sel.status != UserStatus::Active { user_sel.status = UserStatus::Active }
        let user_data = user_sel.session_data();
        match user_sel.update(&app_dbs.user_store).await {
            Ok(_) => {
                let session_token = req_handler.new_token();
                let session_cookie = add_session_cookie(true,&cookies, &session_token, &user_data, 0, &app_dbs, "/").await;
                if app_dbs.config.verbose > 1 { println!("session cookie: {}", &session_cookie) };
                let new_auth_state = AuthState::from_cookie(session_cookie.to_string(), &app_dbs).await;
                req_handler = ReqHandler::new(
                    req_handler.req_header,
                    &app_dbs,
                    &uri,
                    &new_auth_state,
                    &random,
                    "post_login_handler"
                );
                let _ = req_handler.trace_req(format!("user '{}', new token: '{}', cookie: '{}' ",&user_login.name, &session_token, &session_cookie));
                // TODO Is this a bug ?? 
                // http -> tokio-runtime-worker' panicked at 'payload claims content-length of 10, custom content-length header claims 34
                if app_dbs.config.protocol == "https" {
                    (
                        req_handler.req_header.header,
                        result 
                    ).into_response()
                } else {
                    (
                       result 
                    ).into_response()
                }
            },
            Err(e) => {
                let _ = req_handler.trace_req(format!("user '{}' update error: {}",&user_login.name,e));
                (
                    StatusCode::BAD_REQUEST,
                    req_handler.req_header.header,
                    "Error"
                ).into_response()
            }
        }
    }

    async fn logout_handler(
        header: HeaderMap,
        uri: Uri,
        Extension(app_dbs): Extension<Arc<AppDBs>>,
        Extension(cookies): Extension<Cookies>,
        Extension(random): Extension<Random>,
        ConnectInfo(app_connect_info): ConnectInfo<AppConnectInfo>,
    ) -> Response {
        let auth_state = get_auth_state(true, &cookies, &app_dbs).await;
        let mut req_handler = ReqHandler::new(
            ReqHeaderMap::new(header, &format!("{}",&uri.path().to_string()), &app_connect_info),
            &app_dbs,
            &uri,
            &auth_state,
            &random,
            "logout_handler"
        );
        let id = auth_state.id();
        let res = auth_state.destroy(); 
        cookies.remove(Cookie::new(SESSION_COOKIE_NAME, ""));
        if app_dbs.config.verbose > 1 { println!("Session: {} destroyed: {}",&id, &res); }
        let _ = req_handler.trace_req(format!("Session '{}' logout",&id));
        let auth_state = AuthState::default();
        req_handler = ReqHandler::new(
            req_handler.req_header,
            &app_dbs,
            &uri,
            &auth_state,
            &random,
            "logout_handler"
        );
        let mut res_headers = HeaderMap::new();
        if req_handler.req_header.is_browser() {
            res_headers.append(axum::http::header::CONTENT_TYPE,"text/html; charset=utf-8".parse().unwrap());
        }
        // these is to use user_items like color_theme 
        // let user_items = User::hash_items(&auth_state.user_items());
        // req_handler.context.insert("usr_items", &user_items);
        req_handler.context.insert("with_menu", "1");
        let result = if let Some(tpl) = app_dbs.config.tpls.get("logout") {
            req_handler.render_template(&tpl,"logout")
        } else {
            String::from("logout")
        };
        (
            res_headers,
            result.to_owned()
        ).into_response()
    }
    async fn check_item_handler(
        header: HeaderMap,
        uri: Uri,
        Extension(app_dbs): Extension<Arc<AppDBs>>,
        Extension(cookies): Extension<Cookies>,
        Extension(random): Extension<Random>,
        ConnectInfo(app_connect_info): ConnectInfo<AppConnectInfo>,
        Json(user_item): Json<UserItem>,
    ) -> Response {
        let auth_state = get_auth_state(true, &cookies, &app_dbs).await;
        let req_handler = ReqHandler::new(
            ReqHeaderMap::new(header, &format!("{}",&uri.path().to_string()), &app_connect_info),
            &app_dbs,
            &uri,
            &auth_state,
            &random,
            "logout_handler"
        );
        let result = if user_item.name == "password" {
            let _ = req_handler.trace_req(format!("Password estimate"));
            User::estimate_password(&user_item.value)
        } else  {
            let user_sel = User::select(&user_item.name, &user_item.value, false,&app_dbs.user_store).await.unwrap_or_default();
            if !user_sel.name.is_empty() {
                let _ = req_handler.trace_req(format!("User '{}' = '{}' not found",&user_item.name,&user_item.value));
                // User not exists
                return (
                    StatusCode::BAD_REQUEST,
                    req_handler.req_header.header,
                    "Error"
                ).into_response();
            }
            String::from("OK")
        };
        (
            req_handler.req_header.header,
            result.to_owned()
        ).into_response()
    }
    // async fn edit_user_handler(
    //     header: HeaderMap,
    //     uri: Uri,
    //     Extension(app_dbs): Extension<Arc<AppDBs>>,
    //     Extension(cookies): Extension<Cookies>,
    //     //_auth_state: AuthState,
    // ) -> Response {
    //     let auth_state = get_auth_state(true, &cookies, &app_dbs).await;
    //     let mut req_handler = ReqHandler::new(
    //         ReqHeaderMap::new(header, &format!("{}",&uri.path().to_string())),
    //         &app_dbs,
    //         &uri,
    //         &auth_state,
    //         "root_handler"
    //     ); 
    //     let uri_path = format!("{}",&uri.path().to_string());
    //     let file =  "hello.html";
    //     let result = app_dbs.tera.render(&file, &app_dbs.context).unwrap_or_else(|e|{
    //         println!("Error render {}: {}",&file,e);
    //         String::from("")
    //     });
    //     req_handler.context.insert("with_menu", "1");
    //     dbg!("uri: {}",&uri_path);
    //     // let mut new_header = header.to_owned();
    //     //new_header.append("Set-Cookie", "session_token=_; Max-Age=0".parse().unwrap());
    //     // cookies.remove(Cookie::new(SESSION_COOKIE_NAME, ""));
    //     (
    //         req_handler.req_header.header,
    //         result.to_owned()
    //     ).into_response()
    // //     "Hello, World!"
    // }
    async fn user_settings_handler(
        header: HeaderMap,
        uri: Uri,
        Extension(app_dbs): Extension<Arc<AppDBs>>,
        Extension(cookies): Extension<Cookies>,
        Extension(random): Extension<Random>,
        ConnectInfo(app_connect_info): ConnectInfo<AppConnectInfo>,
    ) -> Response {
        let auth_state = get_auth_state(true, &cookies, &app_dbs).await;
        if auth_state.session.is_none() {
           return Redirect::temporary( &format!("/login?o={}",uri.path().to_string())).into_response();
        }
        let mut req_handler = ReqHandler::new(
            ReqHeaderMap::new(header, &format!("{}",&uri.path().to_string()), &app_connect_info),
            &app_dbs,
            &uri,
            &auth_state,
            &random,
            "user_settings_handler"
        ); 
        let mut res_headers = HeaderMap::new();
        if req_handler.req_header.is_browser() {
            res_headers.append(axum::http::header::CONTENT_TYPE,"text/html; charset=utf-8".parse().unwrap());
        }
        let user_id = auth_state.user_id();
        if user_id.is_empty() {
            let _ = req_handler.trace_req(format!("user not id found"));
            // User not exists
            return Redirect::temporary( &format!("/")).into_response();
        }
        let user_sel = User::select("id", &user_id, true, &app_dbs.user_store).await.unwrap_or_default();
        if user_sel.name.is_empty() {
            let _ = req_handler.trace_req(format!("Edit user id '{}' not found ",&user_id));
            // User not exists
            return Redirect::temporary( &format!("/")).into_response();
        }
        req_handler.context.insert("with_menu", "1");
        req_handler.context.insert("user", &user_sel);
        req_handler.context.insert("admin_fields", &app_dbs.config.admin_fields);
        req_handler.context.insert("totp_mode", &format!("{}",&app_dbs.config.totp_mode));
        // let user_items = User::hash_items(&user_sel.items);
        // req_handler.context.insert("usr_items", &user_items);
        req_handler.context.insert("no_edit", "true");
        req_handler.context.insert("edit_target", "main");
        let result = if let Some(tpl) = app_dbs.config.tpls.get("user_settings") {
            req_handler.render_template(&tpl,"user setting")
        } else {
            String::from("user settings")
        };
        let _ = req_handler.trace_req(format!("User '{}' settings",&user_sel.id));
        (
            res_headers,
            result.to_owned()
        ).into_response()
    }
  async fn user_settings_edit_handler(
        header: HeaderMap,
        uri: Uri,
        Extension(app_dbs): Extension<Arc<AppDBs>>,
        Extension(cookies): Extension<Cookies>,
        Extension(random): Extension<Random>,
        ConnectInfo(app_connect_info): ConnectInfo<AppConnectInfo>,
        axum::extract::Path(data): axum::extract::Path<String>,
    ) -> Response {
        let auth_state = get_auth_state(true, &cookies, &app_dbs).await;
        if auth_state.session.is_none() {
           return Redirect::temporary( &format!("/login?o={}",uri.path().to_string())).into_response();
        }
        let mut req_handler = ReqHandler::new(
            ReqHeaderMap::new(header, &format!("{}",&uri.path().to_string()), &app_connect_info),
            &app_dbs,
            &uri,
            &auth_state,
            &random,
            "user_settings_edit_handler"
        ); 
        let mut res_headers = HeaderMap::new();
        if req_handler.req_header.is_browser() {
            res_headers.append(axum::http::header::CONTENT_TYPE,"text/html; charset=utf-8".parse().unwrap());
        }
        if data != "main" && data != "password" && data != "totp" {
            let _ = req_handler.trace_req(format!("Edit user not data section '{}' ",&data));
            return Redirect::temporary( &format!("/")).into_response();
        }
        let user_id = auth_state.user_id();
        if user_id.is_empty() {
            // User not exists
            let _ = req_handler.trace_req(format!("Edit user not id "));
            return Redirect::temporary( &format!("/")).into_response();
        }
        let user_sel = User::select("id", &user_id, true, &app_dbs.user_store).await.unwrap_or_default();
        if user_sel.name.is_empty() {
            let _ = req_handler.trace_req(format!("Edit user id '{}' not found ",&user_id));
            // User not exists
            return Redirect::temporary( &format!("/")).into_response();
        }
        req_handler.context.insert("with_menu", "1");
        req_handler.context.insert("user", &user_sel);
        req_handler.context.insert("edit_target", &data);
        req_handler.context.insert("admin_fields", &app_dbs.config.admin_fields);
        req_handler.context.insert("totp_mode", &format!("{}",&app_dbs.config.totp_mode));
        if data == "totp" && app_dbs.config.totp_mode != TotpMode::No {
            if !user_sel.otp_base32.is_empty() {
                match req_handler.otp_make(&user_sel.otp_base32, &user_sel.otp_defs) {
                    Ok(totp) => {
                        req_handler.context.insert("otp_code", &user_sel.otp_base32);
                        req_handler.context.insert("otp_url", &user_sel.otp_auth_url);
                        req_handler.context.insert("otp_qr", &totp.get_qr().unwrap_or_default());
                    },
                    Err(e) => {
                        println!("User settings error totp: {}",e);
                    }
                }
            } else {
                match req_handler.otp_generate() {
                    Ok(totp) => {
                        req_handler.context.insert("otp_code", &totp.get_secret_base32());
                        req_handler.context.insert("otp_url", &totp.get_url());
                        req_handler.context.insert("otp_qr", &totp.get_qr().unwrap_or_default());
                    },
                    Err(e) => {
                        println!("Error TOTP generartor: {}",e);
                    }
                }
            }
            req_handler.context.insert("totp_digits", &app_dbs.config.totp_digits);
            req_handler.context.insert("totp_algorithm",&format!("{}",&app_dbs.config.totp_algorithm));
        }
        let _ = req_handler.trace_req(format!("Edit user '{}' settings",&user_id));
        // req_handler.context.insert("no_edit", "true");
        let result = if let Some(tpl) = app_dbs.config.tpls.get("user_settings") {
            req_handler.render_template(&tpl,"user setting")
        } else {
            String::from("user settings")
        };
        (
            res_headers,
            result.to_owned()
        ).into_response()
    }
    async fn post_user_settings_handler(
        header: HeaderMap,
        uri: Uri,
        Extension(app_dbs): Extension<Arc<AppDBs>>,
        Extension(cookies): Extension<Cookies>,
        Extension(random): Extension<Random>,
        ConnectInfo(app_connect_info): ConnectInfo<AppConnectInfo>,
        Json(user_data): Json<UserData>,
    ) -> Response {
        let auth_state = get_auth_state(true, &cookies, &app_dbs).await;
        let req_handler = ReqHandler::new(
            ReqHeaderMap::new(header, &format!("{}",&uri.path().to_string()), &app_connect_info),
            &app_dbs,
            &uri,
            &auth_state,
            &random,
            "post_user_settings_handler"
        );
        if auth_state.session.is_none() {
            let _ = req_handler.trace_req(format!("No session found for user '{}'",&user_data.id));
            return Redirect::temporary( &format!("/login?o={}",uri.path().to_string())).into_response();
        }
        let user_id = auth_state.user_id();
        if user_id.is_empty() {
            let _ = req_handler.trace_req(format!("No user found"));
            return (
                StatusCode::BAD_REQUEST,
                req_handler.req_header.header,
                "Error"
            ).into_response();
        }
        let mut user_sel = User::select("id", &user_id, false,&app_dbs.user_store).await.unwrap_or_default();
        if user_sel.name.is_empty() {
            let _ = req_handler.trace_req(format!("User '{}' not found",&user_id));
            return (
                StatusCode::BAD_REQUEST,
                req_handler.req_header.header,
                "Error"
            ).into_response();
        }
        if !user_data.password.is_empty() {
            let passwd_score = User::password_score(&user_data.password);
            if passwd_score < app_dbs.config.password_score {
                let _ = req_handler.trace_req(format!("User '{}' password '{}' score: {} under {}"
                    ,&user_id,&user_data.password, passwd_score,app_dbs.config.password_score
                ));
                return (
                    StatusCode::UNAUTHORIZED,
                    req_handler.req_header.header,
                    "Error"
                ).into_response();
            }
            user_sel.password = generate_hash(&user_data.password);
        }

        let otp_enabled: bool;
        if app_dbs.config.totp_mode != TotpMode::No && !user_data.otp_auth.is_empty() {
            match req_handler.otp_check(&user_data.otp_code,&user_data.otp_auth, "") {
                Ok(val) => {
                    if val {
                        otp_enabled = true;
                    } else {
                        // otp_enabled = fasle;
                        let _ = req_handler.trace_req(format!("User '{}' not valid TOTP code",&user_id));
                        return (
                            StatusCode::UNAUTHORIZED,
                            req_handler.req_header.header,
                            "Error"
                        ).into_response();
                    }
                },
                Err(e) => {
                    println!("TOTP check: {}", e);
                    let _ = req_handler.trace_req(format!("User '{}' TOTP check error: {}",&user_id,e));
                    return (
                        StatusCode::UNAUTHORIZED,
                        req_handler.req_header.header,
                        "Error"
                    ).into_response();
                }
            } 
        } else {
            otp_enabled = false;
        }
        if otp_enabled  {
            user_sel.otp_enabled= true;
            user_sel.otp_verified= true;
            user_sel.otp_base32 = user_data.otp_code.to_owned();
            user_sel.otp_auth_url = user_data.otp_url.to_owned();
            user_sel.otp_defs = format!("{},{}",
                &app_dbs.config.totp_digits,
                format!("{}",&app_dbs.config.totp_algorithm),
            );
        } else {
            user_sel.otp_enabled= false;
            user_sel.otp_verified = false;
            user_sel.otp_base32 = String::from("");
            user_sel.otp_auth_url = String::from("");
            user_sel.otp_defs = String::from("");
        }
        user_sel.from_data(user_data);
        let user_data = user_sel.session_data();
        match user_sel.update(&app_dbs.user_store).await {
            Ok(_) => {
                let session_token = auth_state.id();
                let session_cookie = add_session_cookie(true,&cookies, &session_token, &user_data, 0, &app_dbs, "/").await;
                if app_dbs.config.verbose > 1 { println!("session cookie: {}", &session_cookie) };
                let _ = req_handler.trace_req(format!("User '{}' updated",&user_id));
                let result =String::from("OK");
                (
                    req_handler.req_header.header,
                    result.to_owned()
                ).into_response()
            },
            Err(e) => {
                let _ = req_handler.trace_req(format!("User '{}' update error: {}",&user_id,e));
                (
                    StatusCode::BAD_REQUEST,
                    req_handler.req_header.header,
                    "Error"
                ).into_response()
            }
        }
    }
    async fn post_reset_password_handler(
        header: HeaderMap,
        uri: Uri,
        Extension(app_dbs): Extension<Arc<AppDBs>>,
        Extension(cookies): Extension<Cookies>,
        Extension(random): Extension<Random>,
        ConnectInfo(app_connect_info): ConnectInfo<AppConnectInfo>,
        Json(user_item): Json<UserItem>,
    ) -> Response {
        let auth_state = get_auth_state(true, &cookies, &app_dbs).await;
        let mut req_handler = ReqHandler::new(
            ReqHeaderMap::new(header, &format!("{}",&uri.path().to_string()), &app_connect_info),
            &app_dbs,
            &uri,
            &auth_state,
            &random,
            "post_reset_password_handler"
        );
        if ! app_dbs.config.use_mail {
            let _ = req_handler.trace_req(format!("Mail disabled in config, user '{}' password can not be reset",&user_item.name));
            return (
                StatusCode::BAD_REQUEST,
                req_handler.req_header.header,
                "Error no service"
            ).into_response();
        }
        if user_item.name.is_empty() {
            let _ = req_handler.trace_req(format!("No user name"));
            return (
                StatusCode::BAD_REQUEST,
                req_handler.req_header.header,
                "Error"
            ).into_response();
        }
        let field = if user_item.name.contains("@") {
            "email"
        } else {
            "name"
        };
        let mut user_sel = User::select(&field, &user_item.name, false, &app_dbs.user_store).await.unwrap_or_else(|e|{
            println!("Error select: {}", e);
            User::default()
        });
        if user_sel.name.is_empty() {
            let _ = req_handler.trace_req(format!("User '{}' = '{}' not found",&field,&user_item.name));
            // User not exists
            return (
                StatusCode::BAD_REQUEST,
                req_handler.req_header.header,
                "Error data"
            ).into_response();
        }
        if user_sel.status != UserStatus::Active && user_sel.status != UserStatus::Created {
            let _ = req_handler.trace_req(format!("user '{}' in not valid status: {}",&user_item.name, &user_sel.status));
            return (
                StatusCode::UNAUTHORIZED,
                req_handler.req_header.header,
                "Error status"
            ).into_response();
        }
        if app_dbs.config.totp_mode != TotpMode::No {
            if user_sel.otp_base32.is_empty() 
                && (app_dbs.config.totp_mode == TotpMode::Mandatory || user_sel.otp_enabled)
            {
                let _ = req_handler.trace_req(format!("user '{}' not valid Totp: {}",&user_sel.name, &user_sel.otp_enabled));
                return (
                    StatusCode::UNAUTHORIZED,
                    req_handler.req_header.header,
                   "Error data"
                ).into_response();
            } else if user_sel.otp_enabled && user_sel.otp_verified 
                && !user_sel.otp_base32.is_empty() && !user_sel.otp_defs.is_empty() 
            {
                match req_handler.otp_check(&user_sel.otp_base32,&user_item.value, &user_sel.otp_defs) {
                    Ok(val) => {
                        if !val {
                            let _ = req_handler.trace_req(format!("User '{}' not valid TOTP code",&user_item.name));
                            return (
                                StatusCode::UNAUTHORIZED,
                                req_handler.req_header.header,
                                "Error"
                            ).into_response();
                        }
                    },
                    Err(e) => {
                        println!("TOTP check: {}", e);
                        let _ = req_handler.trace_req(format!("User '{}' TOTP check error: {}",&user_item.name,e));
                        return (
                            StatusCode::UNAUTHORIZED,
                            req_handler.req_header.header,
                            "Error"
                        ).into_response();
                    }
                } 
            } 
        } 
        let session_token = req_handler.new_token();
        user_sel.status = UserStatus::Pending;
        let user_data = user_sel.session_data();
        let session_cookie = add_session_cookie(false,&cookies, &session_token, &user_data, app_dbs.config.session_expire, &app_dbs, "invitation").await;
        let session_encoded_key = encode(session_cookie.as_str());
        let body=format!("This is a user password reset request for docserver service");
        let subject = format!("DocServer password reset");
        let reset_url= format!(
            "{}://{}/reset",
            &app_dbs.config.protocol,
            &app_dbs.config.hostport,
        );
        let reset_expiration = format!("{} minutes",(&app_dbs.config.session_expire/60));
        req_handler.context.insert("reset_url", &reset_url);
        req_handler.context.insert("reset_expiration", &reset_expiration);
        req_handler.context.insert("email_body",&body);
        req_handler.context.insert("reset_key",&session_encoded_key);
        req_handler.context.insert("email_subject",&subject);
        let mail_content= if let Some(tpl) = app_dbs.config.tpls.get("reset_password_mail_txt") {
            req_handler.render_template(tpl, "reset password")
        } else {
            format!("{}\n{}\n{}/{}\n",&subject,&body,&reset_url,&session_cookie)
        };
        let mail_html_content = if let Some(tpl) = app_dbs.config.tpls.get("reset_password_mail_html") {
            req_handler.render_template(tpl, "invite")
        } else {
            format!("{}\n{}\n{}/{}\n",&subject,&body,&reset_url,&session_cookie)
        }; 
        let mail_check = MailMessage::check(&app_dbs);
        if ! mail_check.is_empty() {
            let _ = req_handler.trace_req(format!("Mail service check error: {}",&mail_check));
            (
                StatusCode::BAD_REQUEST,
                req_handler.req_header.header,
                "Error service"
            ).into_response()
        } else {
            match MailMessage::new(
                &app_dbs.config.mail_from,
                &user_sel.email,
            &app_dbs.config.mail_reply_to,
            ) {
                Ok(mail_message) => {
                    match mail_message.send_html_message(
                        &subject,
                        &mail_content,
                        &mail_html_content,
                        &app_dbs
                    ).await {
                        Ok(_) => {
                            let _ = req_handler.trace_req(format!("Mail sent to: '{}' reset url: {} cookie: {}",
                                &user_sel.name, &reset_url,&session_cookie
                            ));
                            (
                                StatusCode::OK,
                                format!("Mail sent to {}",&user_sel.name)
                            ).into_response()
                        },
                        Err(e) => { 
                            let _ = req_handler.trace_req(format!("Mail message send to: '{}' Error: {} ",&user_sel.name,e));
                            println!("Error mail message send: {}",e);
                            (
                                StatusCode::INTERNAL_SERVER_ERROR,
                                req_handler.req_header.header,
                                "Error service"
                            ).into_response()
                        }
                    }
                },
                Err(e) => {
                    let _ = req_handler.trace_req(format!("Mail message send to: '{}' Creation error: {} ",&user_sel.name,e));
                    println!("Error mail message creation: {}",e);
                    (
                        StatusCode::BAD_REQUEST,
                        req_handler.req_header.header,
                        "Error service"
                    ).into_response()
                }
            }
        }
        //let result=format!("{}","OK");
        // if ! auth_state.has_auth_role(&app_dbs.config.auth_roles) {
        //    return Redirect::temporary( &format!("/login?o={}",uri.path().to_string())).into_response();
        // //    return (
        // //         StatusCode::UNAUTHORIZED,
        // //         header,
        // //         "Error authorization"
        // //     ).into_response(); 
        // }
        // (
        //     //status,
        //     req_handler.req_header.header,
        //     result,
        // ).into_response()
    }
    async fn reset_password_handler(
        header: HeaderMap,
        uri: Uri,
        Extension(app_dbs): Extension<Arc<AppDBs>>,
        Extension(cookies): Extension<Cookies>,
        Extension(random): Extension<Random>,
        ConnectInfo(app_connect_info): ConnectInfo<AppConnectInfo>,
        // Query(req_params): Query<ReqParams>,
        axum::extract::Path(data): axum::extract::Path<String>,
    ) -> Response {
        // dbg!(&user_item);
        let session_cookie = decode(&data).unwrap_or_default().to_string();
        let auth_state = AuthState::from_cookie(session_cookie.to_owned(), &app_dbs).await;
        let mut req_handler = ReqHandler::new(
            ReqHeaderMap::new(header, &format!("{}",&uri.path().to_string()), &app_connect_info),
            &app_dbs,
            &uri,
            &auth_state,
            &random,
            "reset_password_handler"
        ); 
        let user_id = auth_state.user_id();
        if user_id.is_empty() {
            let _ = req_handler.trace_req(format!("No user found"));
            return (
                StatusCode::BAD_REQUEST,
                req_handler.req_header.header,
                "Error"
            ).into_response();
        }
        let user_sel = User::select("id", &user_id, false, &app_dbs.user_store).await.unwrap_or_else(|e|{
            println!("Error select: {}", e);
            User::default()
        });
        if user_sel.name.is_empty() {
            let _ = req_handler.trace_req(format!("User 'id' = '{}' not found",&user_id));
            // User not exists
            return (
                StatusCode::BAD_REQUEST,
                req_handler.req_header.header,
                "Error data"
            ).into_response();
        }
        if user_sel.status != UserStatus::Active {
            let _ = req_handler.trace_req(format!("user '{}' in not valid status: {}",&user_id, &user_sel.status));
            return (
                StatusCode::BAD_REQUEST,
                req_handler.req_header.header,
                "Error status" 
            ).into_response();
        }
        let mut res_headers = HeaderMap::new();
        if req_handler.req_header.is_browser() {
            res_headers.append(axum::http::header::CONTENT_TYPE,"text/html; charset=utf-8".parse().unwrap());
        }
        req_handler.context.insert("with_menu", "1");
        req_handler.context.insert("user", &user_sel);
        req_handler.context.insert("edit_target", "password");
        req_handler.context.insert("edit_reset", "password");
        req_handler.context.remove("web_menu_items");
        let result = if let Some(tpl) = app_dbs.config.tpls.get("user_settings") {
            req_handler.render_template(&tpl,"user setting")
        } else {
            String::from("user settings")
        };
        let user_data = user_sel.session_data();
        let session_token = req_handler.new_token();
        let session_cookie = add_session_cookie(true,&cookies, &session_token, &user_data, 0, &app_dbs, "/").await;
        if app_dbs.config.verbose > 1 { println!("session cookie: {}", &session_cookie) };
        let _ = req_handler.trace_req(format!("user '{}' reset password",&user_id));
        (
            res_headers,
            result.to_owned()
        ).into_response()
    }
    async fn post_user_password_handler(
        header: HeaderMap,
        uri: Uri,
        Extension(app_dbs): Extension<Arc<AppDBs>>,
        Extension(cookies): Extension<Cookies>,
        Extension(random): Extension<Random>,
        ConnectInfo(app_connect_info): ConnectInfo<AppConnectInfo>,
        Json(user_data): Json<UserData>,
    ) -> Response {
        let auth_state = get_auth_state(true, &cookies, &app_dbs).await;
        let req_handler = ReqHandler::new(
            ReqHeaderMap::new(header, &format!("{}",&uri.path().to_string()), &app_connect_info),
            &app_dbs,
            &uri,
            &auth_state,
            &random,
            "post_reset_password_handler"
        );
        if user_data.password.is_empty() {
            let _ = req_handler.trace_req(format!("No passwordfound"));
            return (
                StatusCode::BAD_REQUEST,
                req_handler.req_header.header,
                "Error"
            ).into_response();
        }
        if auth_state.session.is_none() {
            let _ = req_handler.trace_req(format!("No session found for user '{}'",&user_data.id));
            return Redirect::temporary( &format!("/login?o={}",uri.path().to_string())).into_response();
        }
        let user_id = auth_state.user_id();
        if user_id.is_empty() {
            let _ = req_handler.trace_req(format!("No user found"));
            return (
                StatusCode::BAD_REQUEST,
                req_handler.req_header.header,
                "Error"
            ).into_response();
        }
        let mut user_sel = User::select("id", &user_id, false,&app_dbs.user_store).await.unwrap_or_default();
        if user_sel.name.is_empty() {
            let _ = req_handler.trace_req(format!("User '{}' not found",&user_id));
            return (
                StatusCode::BAD_REQUEST,
                req_handler.req_header.header,
                "Error"
            ).into_response();
        }
        let passwd_score = User::password_score(&user_data.password);
        if passwd_score < app_dbs.config.password_score {
            let _ = req_handler.trace_req(format!("User '{}' password '{}' score: {} under {}"
                ,&user_id,&user_data.password, passwd_score,app_dbs.config.password_score)
            );
            return (
                StatusCode::UNAUTHORIZED,
                req_handler.req_header.header,
                "Error"
            ).into_response();
        }
        user_sel.password = generate_hash(&user_data.password);
        let user_data = user_sel.session_data();
        match user_sel.update(&app_dbs.user_store).await {
            Ok(_) => {
                let session_token = auth_state.id();
                let session_cookie = add_session_cookie(true,&cookies, &session_token, &user_data, 0, &app_dbs, "/").await;
                if app_dbs.config.verbose > 1 { println!("session cookie: {}", &session_cookie) };
                let _ = req_handler.trace_req(format!("user '{}' reset password OK",&user_id));
                let result =String::from("OK");
                (
                    req_handler.req_header.header,
                    result.to_owned()
                ).into_response()
            },
            Err(e) => {
                let _ = req_handler.trace_req(format!("Error user '{}' reset password: {}",&user_id,e));
                (
                    StatusCode::BAD_REQUEST,
                    req_handler.req_header.header,
                    "Error"
                ).into_response()
            },
        }
    }
    async fn invite_signup_handler(
        header: HeaderMap,
        uri: Uri,
        Extension(app_dbs): Extension<Arc<AppDBs>>,
        Extension(cookies): Extension<Cookies>,
        Extension(random): Extension<Random>,
        ConnectInfo(app_connect_info): ConnectInfo<AppConnectInfo>,
        axum::extract::Path(data): axum::extract::Path<String>,
        // auth_state: AuthState,
    ) -> Response {
        let auth_state = get_auth_state(true, &cookies, &app_dbs).await;
        let mut req_handler = ReqHandler::new(
            ReqHeaderMap::new(header, &format!("{}",&uri.path().to_string()), &app_connect_info),
            &app_dbs,
            &uri,
            &auth_state,
            &random,
            "invite_signup_handler"
        );
        if ! app_dbs.config.signup_mode.contains("invitation") {
            let msg = format!("Config signup mode not invitation: {}",
               app_dbs.config.signup_mode
            );
            println!("{}", &msg);
            let _ = req_handler.trace_req(msg);
            return Redirect::temporary( &format!("/")).into_response();
        }
        // println!("root_handler: {}",&session_cookie);
        let session_cookie = decode(&data).unwrap_or_default().to_string();
        let auth_state = AuthState::from_cookie(session_cookie.to_owned(), &app_dbs).await;
        if auth_state.session.is_none() {
            // TODO make it prettier 
            let _ = req_handler.trace_req(format!("No session found"));
            return (
                StatusCode::NOT_FOUND,
                req_handler.req_header.header,
                "No valid invitation found" 
            ).into_response();
        }
        //let user_data = auth_state.user_data();
        let mut usr = User::default();
        usr.roles = auth_state.user_roles();
        usr.email = auth_state.user_email();
        let usr_id = auth_state.user_id();
        let invite_id = if usr_id == "0" {
            usr_id
        } else {
          usr.email.to_owned()
        };
        // let _uri_path = format!("{}",&uri.path().to_string());
        // let file =  "hello.html";
        // let result = app_dbs.tera.render(&file, &app_dbs.context).unwrap_or_else(|e|{
        //     println!("Error render {}: {}",&file,e);
        //     String::from("")
        // });
    
        let mut res_headers = HeaderMap::new();
        if req_handler.req_header.is_browser() {
            res_headers.append(axum::http::header::CONTENT_TYPE,"text/html; charset=utf-8".parse().unwrap());
        }
        req_handler.context.insert("user", &usr);
        req_handler.context.insert("isadmin", "");
        req_handler.context.insert("totp_mode", &format!("{}",&app_dbs.config.totp_mode));
        req_handler.context.insert("invite_key", &data);
        req_handler.context.insert("invite_id", &invite_id);
        req_handler.context.insert("admin_fields", &app_dbs.config.admin_fields);
        let result = if let Some(tpl) = app_dbs.config.tpls.get("signup") {
            req_handler.render_template(&tpl,"signup")
        } else {
            String::from("signup")
        };
        let _ = req_handler.trace_req(format!("Invite to '{}' data: {}",&invite_id, &data));
        (
            res_headers,
            result.to_owned()
        ).into_response()
    }
    async fn post_invite_handler(
        header: HeaderMap,
        uri: Uri,
        Extension(app_dbs): Extension<Arc<AppDBs>>,
        Extension(cookies): Extension<Cookies>,
        Extension(random): Extension<Random>,
        ConnectInfo(app_connect_info): ConnectInfo<AppConnectInfo>,
        Json(user_invite): Json<UserInvitation>,
    ) -> Response {
        //dbg!(&user_item);
        let auth_state = get_auth_state(true, &cookies, &app_dbs).await;
        let mut req_handler = ReqHandler::new(
            ReqHeaderMap::new(header, &format!("{}",&uri.path().to_string()), &app_connect_info),
            &app_dbs,
            &uri,
            &auth_state,
            &random,
            "post_invite_handler"
        );
        if ! app_dbs.config.signup_mode.contains("invitation") {
            let msg = format!("Config signup mode not invitation: {}",
               app_dbs.config.signup_mode
            );
            println!("{}", &msg);
            let _ = req_handler.trace_req(msg);
           return Redirect::temporary( &format!("/")).into_response();
        }
        if ! auth_state.has_auth_role(&app_dbs.config.auth_roles) {
           let _ = req_handler.trace_req(format!("User '{}' not have role 'dev'",&auth_state.user_id()));
           return Redirect::temporary( &format!("/login?o={}",uri.path().to_string())).into_response();
        //    return (
        //         StatusCode::UNAUTHORIZED,
        //         header,
        //         "Error authorization"
        //     ).into_response(); 
        }
        if ! user_invite.email.contains("@") {
            let _ = req_handler.trace_req(format!("Invite email '{}' not contains '@'",&user_invite.email));
            return (
                StatusCode::BAD_REQUEST,
                req_handler.req_header.header,
                "Error invitation data"
            ).into_response(); 
        }
        let session_token = req_handler.new_token();
        let mut usr = User::default();
        usr.email = user_invite.email.to_owned();
        usr.roles = user_invite.roles.to_owned();
        usr.isadmin = user_invite.isadmin;
        usr.status = UserStatus::Pending;
        let user_data = usr.session_data();
        let session_cookie = add_session_cookie(false,&cookies, &session_token, &user_data, user_invite.expire, &app_dbs, "invitation").await;
        let session_encoded_key = encode(session_cookie.as_str());
        let body=format!("This is an invitation to docserver service");
        let subject = format!("DocServer Invitation");
        let signup_url= format!(
            "{}://{}/signup",
            &app_dbs.config.protocol,
            &app_dbs.config.hostport,
        );
        let invite_expiration = format!("{} minutes",
            (&app_dbs.config.session_expire/60)
        );
        req_handler.context.insert("signup_url", &signup_url);
        req_handler.context.insert("invite_expiration", &invite_expiration);
        req_handler.context.insert("email_body",&body);
        req_handler.context.insert("invite_key",&session_encoded_key);
        req_handler.context.insert("email_subject",&subject);
        let (status, result) = if app_dbs.config.use_mail && user_invite.send_email {
            let mail_content= if let Some(tpl) = app_dbs.config.tpls.get("invite_mail_txt") {
                req_handler.render_template(tpl, "invite")
            } else {
                format!("{}\n{}\n{}/signup/{}\n",&subject,&body,&signup_url,&session_cookie)
            };
            let mail_html_content = if let Some(tpl) = app_dbs.config.tpls.get("invite_mail_html") {
                req_handler.render_template(tpl, "invite")
            } else {
                format!("{}\n{}\n{}/signup/{}\n",&subject,&body,&signup_url,&session_cookie)
            }; 
            let mail_check = MailMessage::check(&app_dbs);
            if ! mail_check.is_empty() {
                (
                   StatusCode::BAD_REQUEST,
                    mail_check
                )
            } else {
                //"jesus.perezlorenzo@gmail.com",
                // "jesus@librecloud.online",
                match MailMessage::new(
                    &app_dbs.config.mail_from,
                    &user_invite.email,
                &app_dbs.config.mail_reply_to,
                ) {
                    Ok(mail_message) => {
                        //match mail_message.send_message(
                        match mail_message.send_html_message(
                            &subject,
                            &mail_content,
                            &mail_html_content,
                            &app_dbs
                        ).await {
                            Ok(_) => {
                                let _ = req_handler.trace_req(format!(
                                    "Invitation mail sent to: '{}' reset url: {} roles: {}, isadmin: {}, expiration: {} cookie: {}",
                                    &user_invite.email, &invite_expiration,  user_invite.roles, user_invite.isadmin, &signup_url,&session_cookie
                                ));
                                (StatusCode::OK, format!("Mail sent to {}",&user_invite.email))
                            },
                            Err(e) => { 
                                println!("Invitation to: {} Error mail message send: {}",&user_invite.email, e);
                                (
                                    StatusCode::INTERNAL_SERVER_ERROR,
                                    String::from("Error")
                                )
                            }
                        }
                    },
                    Err(e) => {
                        println!("Invitation to: {} Error mail message creation: {}",&user_invite.email, e);
                        (
                            StatusCode::BAD_REQUEST,
                            String::from("Error")
                        )
                    }
                }
            }
        } else {
            let _ = req_handler.trace_req(format!(
                "Created invitation: '{}' reset url: {} roles: {}, isadmin: {}, expiration: {} cookie: {}",
                &user_invite.email, &invite_expiration,  user_invite.roles, user_invite.isadmin, &signup_url,&session_cookie
            ));
            (StatusCode::OK, format!("No mail sent to {}",&user_invite.email))
        };
        req_handler.prepare_response();
        req_handler.context.insert("email_result",&result);
        let response = if let Some(tpl) = app_dbs.config.tpls.get("invite_output") {
            req_handler.render_template(tpl, "invite")
        } else {
            format!("{}\n{}\n{}/signup/{}\n",&subject,&body,&signup_url,&session_cookie)
        };
        (
            status,
            req_handler.req_header.header,
            response,
        ).into_response()
    }
    async fn invite_handler(
        header: HeaderMap,
        uri: Uri,
        Extension(app_dbs): Extension<Arc<AppDBs>>,
        Extension(cookies): Extension<Cookies>,
        Extension(random): Extension<Random>,
        ConnectInfo(app_connect_info): ConnectInfo<AppConnectInfo>,
    ) -> Response {
        let auth_state = get_auth_state(true, &cookies, &app_dbs).await;
        let mut req_handler = ReqHandler::new(
            ReqHeaderMap::new(header, &format!("{}",&uri.path().to_string()), &app_connect_info),
            &app_dbs,
            &uri,
            &auth_state,
            &random,
            "invite_handler"
        );
        if ! app_dbs.config.signup_mode.contains("invitation") {
            let msg = format!("Config signup mode not invitation: {}",
               app_dbs.config.signup_mode
            );
            println!("{}",&msg);
            let _ = req_handler.trace_req(msg);
           return Redirect::temporary( &format!("/")).into_response();
        }
        if ! auth_state.has_auth_role(&app_dbs.config.auth_roles) {
           let _ = req_handler.trace_req(format!("User '{}' not have role 'dev'",&auth_state.user_id()));
           return Redirect::temporary( &format!("/login?o={}",uri.path().to_string())).into_response();
        //    return (
        //         StatusCode::UNAUTHORIZED,
        //         header,
        //         "Error authorization"
        //     ).into_response(); 
        }
        
        let title = format!("DocServer Invitation");
        let invite_url= format!(
            "{}://{}/invite",
            &app_dbs.config.protocol,
            &app_dbs.config.hostport,
        );
        let invite_expire = format!("{} minutes",
            (&app_dbs.config.invite_expire/60)
        );
        req_handler.context.insert("target_url", &invite_url);
        req_handler.context.insert("invite_expire", &invite_expire);
        if app_dbs.config.use_mail {
            req_handler.context.insert("use_mail", &app_dbs.config.use_mail);
        }
        req_handler.prepare_response();
        req_handler.context.insert("with_menu", "1");
        let response = if let Some(tpl) = app_dbs.config.tpls.get("invite_create") {
            req_handler.render_template(tpl, "invite create")
        } else {
            format!("{} invite",&title)
        };
        let _ = req_handler.trace_req(format!(
            "Invitation: url: {}, expiration: {}",
            &invite_url, &invite_expire,
        ));
        (
            req_handler.req_header.header,
            response,
        ).into_response()
        // let uri_path = format!("{}",&uri.path().to_string());
        // let file =  "hello.html";
        // let result = app_dbs.tera.render(&file, &app_dbs.context).unwrap_or_else(|e|{
        //     println!("Error render {}: {}",&file,e);
        //     String::from("")
        // });
        //dbg!("uri: {}",&uri_path);
        // let mut new_header = header.to_owned();
        //new_header.append("Set-Cookie", "session_token=_; Max-Age=0".parse().unwrap());
        // cookies.remove(Cookie::new(SESSION_COOKIE_NAME, ""));
        // (
        //     header,
        //     result.to_owned()
        // ).into_response()
    //     "Hello, World!"
    }
    async fn update_user_item_handler(
        header: HeaderMap,
        uri: Uri,
        Extension(app_dbs): Extension<Arc<AppDBs>>,
        Extension(cookies): Extension<Cookies>,
        Extension(random): Extension<Random>,
        ConnectInfo(app_connect_info): ConnectInfo<AppConnectInfo>,
        Json(user_item): Json<UserItem>,
        //_auth_state: AuthState,
        //axum::extract::Path(data): axum::extract::Path<String>,
    ) -> Response {
        dbg!(&user_item);
        let auth_state = get_auth_state(true, &cookies, &app_dbs).await;
        let req_handler = ReqHandler::new(
            ReqHeaderMap::new(header, &format!("{}",&uri.path().to_string()), &app_connect_info),
            &app_dbs,
            &uri,
            &auth_state,
            &random,
            "update_user_item_handler"
        );
        if ! auth_state.has_auth_role(&app_dbs.config.auth_roles) {
            let _ = req_handler.trace_req(format!("User '{}' not have role 'dev'",&auth_state.user_id()));
           return Redirect::temporary( &format!("/login?o={}",uri.path().to_string())).into_response();
        //    return (
        //         StatusCode::UNAUTHORIZED,
        //         header,
        //         "Error authorization"
        //     ).into_response(); 
        }
        let result="";
        (
            //status,
            req_handler.req_header.header,
            result,
        ).into_response()
    }
    route("/", get(main_handler))
    //   .route("/auto", get(auto_handler))

        .route("/login", get(login_handler))
        .route("/login", post(post_login_handler))

        .route("/signup", get(signup_handler))
        .route("/signup", post(post_signup_handler))
        .route("/signup/:data", get(invite_signup_handler))

        .route("/logout", get(logout_handler)) 

        .route("/check", post(check_item_handler)) 

        .route("/invite", get(invite_handler)) 
        .route("/invite", post(post_invite_handler)) 

        .route("/reset/:data", get(reset_password_handler)) 
        .route("/reset", post(post_reset_password_handler)) 
        .route("/resetup", post(post_user_password_handler)) 

        .route("/settings", get(user_settings_handler)) 
        .route("/settings/:item", get(user_settings_edit_handler)) 
//         .route("/update", post(update_user_handler)) 
        .route("/settings", post(post_user_settings_handler)) 
        .route("/update_item", post(update_user_item_handler))
}
