use std::sync::Arc;
use axum::{
    http::{
        StatusCode,
        Uri,
        header::HeaderMap,
    },
    Json,
    routing::{get,post},
    Extension,
    extract::ConnectInfo,
    response::{IntoResponse,Response,Redirect},
    Router,
};
use tower_cookies::Cookies;

use crate::{
    route,
    defs::{
        AppDBs,
        ReqHandler,
        ReqHeaderMap,
        TotpMode,
        Random,
        AppConnectInfo,
    },
    users::{
        User,
        UserData,
        UserItem,
        OpenidUser,
    },
    login_password::generate_hash,
    handlers::{
        add_session_cookie,
        get_auth_state,
    },
};

pub fn users_settings_router_handlers() -> Router {
    // 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");
    //     // 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();
        }
        let openid_sel = OpenidUser::list_selection("userid", &user_sel.id.to_string(), &app_dbs.user_store, true,false, "|").await.unwrap_or_else(|e| {
            println!("Error list selection {}: {}", &user_sel.name, e);
            Vec::new()
        });
        req_handler.context.insert("openid_sel", &openid_sel);
        let openid_sel_appkeys = openid_sel.iter().map(|id| id.appkey.to_string()).collect::<Vec<String>>().join(",");
        req_handler.context.insert("openid_sel_appkeys", &openid_sel_appkeys);
        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();
        }
        let openid_sel = OpenidUser::list_selection("userid", &user_sel.id.to_string(), &app_dbs.user_store, true,false, "|").await.unwrap_or_else(|e| {
            println!("Error list selection {}: {}", &user_sel.name, e);
            Vec::new()
        });
        req_handler.context.insert("openid_sel", &openid_sel);
        let openid_sel_appkeys = openid_sel.iter().map(|id| id.appkey.to_string()).collect::<Vec<String>>().join(",");
        req_handler.context.insert("openid_sel_appkeys", &openid_sel_appkeys);
        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_base64().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_base64().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 openid_sel = OpenidUser::list_selection("userid", &user_sel.id.to_string(), &app_dbs.user_store, true,false, "|").await.unwrap_or_else(|e| {
        //    println!("Error list selection {}: {}", &user_sel.name, e);
        //    Vec::new()
        //});
        //let openid_sel_appkeys = openid_sel.iter().map(|id| id.appkey.to_string()).collect::<Vec<String>>().join(",");
        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("");
        }
        let user_openids = user_data.opendis.to_owned();
        user_sel.from_data(user_data);
        let new_id= user_sel.id.to_string();
        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));
                OpenidUser::sync_ids(&new_id, &user_openids, &app_dbs.user_store).await;
                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 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 {
        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(); 
        }
        dbg!("{}",&user_item);
        let result="";
        (
            //status,
            req_handler.req_header.header,
            result,
        ).into_response()
    }
    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))
}
