use std::sync::Arc;
use urlencoding::{encode,decode};
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,
        AuthState,
        ReqHandler,
        ReqHeaderMap,
        MailMessage,
        Random,
        AppConnectInfo,
    },
    users::{
        User,
        UserStatus,
        UserInvitation,
    },
    handlers::{
        add_session_cookie,
        get_auth_state,
    },
};
pub fn users_invite_router_handlers() -> Router {
    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 {
        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("")
        // });
        // 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!"
    }
    route(
        "/signup/:data", get(invite_signup_handler))
        .route("/invite", get(invite_handler))
        .route("/invite", post(post_invite_handler))
}
