use actix_web::http::header;
use actix_web::{web, HttpRequest, HttpResponse};

use crate::pages::templates::MY;
use crate::pages::{insert_security_headers, naive_to_text, request_to_jar, CommonAuthInfo};
use crate::{DataBaseRo, WebData};

use uuid::Uuid;

#[derive(serde_derive::Serialize)]
struct PageData<'a> {
    common_auth_info: CommonAuthInfo<'a>,
    games: Vec<GameData>,
    contacts: Vec<ContactData>,
    csrf: Uuid,
}

#[derive(serde_derive::Serialize)]
struct GameData {
    gameuid: String,
    gameuidenc: String,
    status: Option<String>,
}

#[derive(serde_derive::Serialize)]
struct ContactData {
    protocol: String,
    address: String,
    is_active: bool,
    create_ts: String,
    delete_ts: Option<String>,
}

pub async fn my(
    request: HttpRequest,
    data: web::Data<WebData<'_>>,
    data_ro: web::Data<DataBaseRo>,
) -> HttpResponse {
    let jar = request_to_jar(request);

    let u = jar
        .private(&data.cookies_key)
        .get("auth")
        .map(|x| x.value().to_string());

    if let Some(user) = u {
        let dbclient = match data_ro.0.get().await {
            Ok(client) => client,
            Err(e) => {
                log::error!("{}", e);
                return HttpResponse::ServiceUnavailable().body(actix_web::body::None::new());
            }
        };

        let stmt = match dbclient
            .prepare("select g.game_uid, g.status::text from games.games g inner join games.players p on g.game_uid = p.game_uid where p.player_name = $1 order by g.game_uid;")
            .await
        {
            Ok(stmt) => stmt,
            Err(e) => {
                log::error!("{}", e);
                return HttpResponse::ServiceUnavailable().body(actix_web::body::None::new());
            }
        };

        let rows = match dbclient.query(&stmt, &[&user]).await {
            Ok(rows) => rows,
            Err(e) => {
                log::error!("{}", e);
                return HttpResponse::ServiceUnavailable().body(actix_web::body::None::new());
            }
        };

        let mut games = Vec::with_capacity(rows.len());

        for row in rows {
            let gameuid = row.get::<_, &str>(0);
            let status = row.get::<_, Option<&str>>(1);
            games.push(GameData {
                gameuid: gameuid.to_string(),
                gameuidenc: pct_str::PctString::encode(gameuid.chars(), pct_str::URIReserved)
                    .into_string(),
                status: status.map(str::to_string),
            });
        }

        let stmt = match dbclient
            .prepare("select protocol::text, address, is_active, create_ts, delete_ts from auth.contacts where player_name = $1;")
            .await
        {
            Ok(stmt) => stmt,
            Err(e) => {
                log::error!("{}", e);
                return HttpResponse::ServiceUnavailable().body(actix_web::body::None::new());
            }
        };

        let rows = match dbclient.query(&stmt, &[&user]).await {
            Ok(rows) => rows,
            Err(e) => {
                log::error!("{}", e);
                return HttpResponse::ServiceUnavailable().body(actix_web::body::None::new());
            }
        };

        let mut contacts = Vec::with_capacity(rows.len());

        for row in rows {
            contacts.push(ContactData {
                protocol: row.get::<_, String>(0),
                address: row.get::<_, String>(1),
                is_active: row.get::<_, bool>(2),
                create_ts: naive_to_text(&row.get::<_, chrono::NaiveDateTime>(3)),
                delete_ts: row
                    .get::<_, Option<chrono::NaiveDateTime>>(4)
                    .as_ref()
                    .map(naive_to_text),
            });
        }

        let csrf = Uuid::new_v4();

        {
            let mut cache = data.cache_reset_game_pwd.lock().await;
            cache.insert(
                csrf,
                (Uuid::nil(), user.to_string(), true),
                std::time::Duration::from_secs(data.cache_duration_sec),
            );
        }

        let body = match data.handlebars.render(
            MY,
            &PageData {
                common_auth_info: CommonAuthInfo {
                    user: Some(user.into()),
                },
                games,
                contacts,
                csrf,
            },
        ) {
            Ok(b) => b,
            Err(e) => {
                log::error!("Render index error: {}", e);
                return HttpResponse::ServiceUnavailable().body(actix_web::body::None::new());
            }
        };
        insert_security_headers(HttpResponse::Ok()).body(body)
    } else {
        HttpResponse::Found()
            .append_header((header::LOCATION, "login.html"))
            .finish()
    }
}