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

use uuid::Uuid;

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

#[derive(serde_derive::Serialize)]
struct PendingDelegateInfo {
    csrf: Uuid,
    name: String,
    accept: bool,
}

#[derive(serde_derive::Serialize)]
struct PlayerInfo {
    player_name: String,
    team_id: i32,
    create_ts: Option<String>,
    delegate_name: Option<String>,
    player_itself: bool,
    delegate_itself: bool,
    revoke_delegate_data: Option<JoinLeaveData>,
    query_delegate_data: Option<JoinLeaveData>,
    pending_delegates: Option<Vec<PendingDelegateInfo>>,
}

#[derive(serde_derive::Deserialize, serde_derive::Serialize)]
pub struct JoinLeaveData {
    csrf: Uuid,
}

#[derive(serde_derive::Deserialize)]
pub struct QueryDelegationData {
    csrf: Uuid,
    delegate_name: String,
}

#[derive(serde_derive::Deserialize)]
pub struct DelegationData {
    csrf: Uuid,
    submit: String,
}

#[derive(serde_derive::Serialize)]
struct GameData<'a> {
    common_auth_info: CommonAuthInfo<'a>,
    gameuid: &'a str,
    gameuidenc: String,
    status: Option<&'a str>,
    notes_html: Option<&'a str>,
    fo_forum_url: Option<&'a str>,
    players: i64,
    min_turn_ts: Option<String>,
    max_turn_ts: Option<String>,
    turns: i64,
    player_list: Option<Vec<PlayerInfo>>,
    join_data: Option<JoinLeaveData>,
    leave_data: Option<JoinLeaveData>,
    is_teamed: bool,
}

fn naive_to_diff_utc(
    naive: &chrono::NaiveDateTime,
    now: chrono::DateTime<chrono::Utc>,
) -> (chrono::DateTime<chrono::Utc>, chrono::Duration) {
    use chrono::TimeZone;
    let ts = chrono::Utc::from_utc_datetime(&chrono::Utc, naive);
    (ts, now - ts)
}

fn diff_utc_to_string((ts, diff): (chrono::DateTime<chrono::Utc>, chrono::Duration)) -> String {
    let (part, part_diff) = if diff.num_weeks() != 0 {
        ("weeks", diff.num_weeks())
    } else if diff.num_days() != 0 {
        ("days", diff.num_days())
    } else if diff.num_hours() != 0 {
        ("hours", diff.num_hours())
    } else if diff.num_minutes() != 0 {
        ("minutes", diff.num_minutes())
    } else {
        ("seconds", diff.num_seconds())
    };
    format!(
        "{} ({} {})",
        ts.format("%Y %b %d %H:%M UTC"),
        part_diff,
        part
    )
}

#[actix_web::get("slow-game-{path}.html")]
pub async fn slow_game(
    request: HttpRequest,
    path: web::Path<String>,
    data: web::Data<WebData<'_>>,
    data_ro: web::Data<DataBaseRo>,
) -> HttpResponse {
    let gameuid = path.into_inner();
    let gameuid = match gameuid.char_indices().nth(128) {
        None => gameuid,
        Some((idx, _)) => gameuid[..idx].to_string(),
    };

    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.status::text, g.notes_html, g.fo_forum_url, g.is_teamed, (select count(*) from games.players p where p.game_uid = g.game_uid and p.client_type = 'p'), MIN(t.turn_ts), MAX(t.turn_ts), COUNT(t.turn) from games.games g left join games.turns t on t.game_uid = g.game_uid where g.game_uid = $1 group 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_opt(&stmt, &[&gameuid]).await {
        Ok(rows) => rows,
        Err(e) => {
            log::error!("Pool RO query error {}", e);
            return HttpResponse::ServiceUnavailable().body(actix_web::body::None::new());
        }
    };
    let row = match rows {
        Some(r) => r,
        None => {
            return HttpResponse::NotFound().body("Not found");
        }
    };

    let jar = request_to_jar(request);

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

    let now = chrono::Utc::now();
    let min_turn_ts = row
        .get::<_, Option<chrono::NaiveDateTime>>(5)
        .map(|x| naive_to_diff_utc(&x, now));
    let max_turn_ts = row
        .get::<_, Option<chrono::NaiveDateTime>>(6)
        .map(|x| naive_to_diff_utc(&x, now));

    let status = row.get::<_, Option<&str>>(0);

    let (player_list, join_data, leave_data) = if let Some(ref user) = user {
        let stmt = match dbclient.prepare("select q.player_name, q.player_name = $2, q.delegate_name, q.delegate_name = $2 from games.query_delegation q where (q.player_name = $2 or q.delegate_name = $2) and q.game_uid = $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, &[&gameuid, user]).await {
            Ok(rows) => rows,
            Err(e) => {
                log::error!("{}", e);
                return HttpResponse::ServiceUnavailable().body(actix_web::body::None::new());
            }
        };
        // player_name is user
        let mut delegations_from = std::collections::HashSet::new();
        // delegate_name is user
        let mut delegations_to = std::collections::HashSet::new();
        for row in rows {
            if row.get::<_, bool>(1) {
                delegations_from.insert(row.get::<_, String>(2));
            } else if row.get::<_, bool>(3) {
                delegations_to.insert(row.get::<_, &str>(0).to_lowercase());
            }
        }

        let stmt = match dbclient.prepare("select p.player_name, p.team_id, p.create_ts, p.delegate_name, p.player_name = $2, p.delegate_name = $2 from games.players p where p.game_uid = $1 and p.client_type = 'p';").await {
            Ok(stmt) => stmt,
            Err(e) => {
                log::error!("{}", e);
                return HttpResponse::ServiceUnavailable().body(actix_web::body::None::new());
            }
        };
        let rows = match dbclient.query(&stmt, &[&gameuid, user]).await {
            Ok(rows) => rows,
            Err(e) => {
                log::error!("{}", e);
                return HttpResponse::ServiceUnavailable().body(actix_web::body::None::new());
            }
        };
        let mut users = Vec::with_capacity(rows.len());
        let mut has_itself = false;
        for row in rows {
            let player_name = row.get::<_, String>(0);
            let team_id = row.get::<_, i32>(1);
            let create_ts = row
                .get::<_, Option<chrono::NaiveDateTime>>(2)
                .as_ref()
                .map(naive_to_text);
            let delegate_name = row.get::<_, Option<String>>(3);
            let player_itself = row.get::<_, bool>(4);
            let delegate_itself = row.get::<_, Option<bool>>(5).unwrap_or(false);
            let pending_delegates = if status.map_or(true, |x| x == "started")
                && player_itself
                && !delegations_from.is_empty()
            {
                let mut cache = data.cache_delegation_game.lock().await;
                Some(
                    delegations_from
                        .iter()
                        .map(|x| {
                            let csrf = Uuid::new_v4();
                            cache.insert(
                                csrf,
                                (gameuid.clone(), user.to_lowercase(), x.to_lowercase()),
                                std::time::Duration::from_secs(data.cache_duration_sec),
                            );
                            PendingDelegateInfo {
                                csrf,
                                name: x.clone(),
                                accept: false,
                            }
                        })
                        .collect(),
                )
            } else if status.map_or(true, |x| x == "started")
                && delegations_to.contains(&player_name.to_lowercase())
            {
                let csrf = Uuid::new_v4();
                let mut cache = data.cache_delegation_game.lock().await;
                cache.insert(
                    csrf,
                    (
                        gameuid.clone(),
                        player_name.to_lowercase(),
                        user.to_lowercase(),
                    ),
                    std::time::Duration::from_secs(data.cache_duration_sec),
                );
                Some(
                    [PendingDelegateInfo {
                        csrf,
                        name: user.clone(),
                        accept: true,
                    }]
                    .into_iter()
                    .collect(),
                )
            } else {
                None
            };
            has_itself = has_itself || player_itself;
            let revoke_delegate_data = if status.map_or(true, |x| x == "started") && delegate_itself
            {
                let csrf = Uuid::new_v4();
                let mut cache = data.cache_revoke_delegation_game.lock().await;
                cache.insert(
                    csrf,
                    (
                        gameuid.clone(),
                        player_name.to_lowercase(),
                        user.to_lowercase(),
                    ),
                    std::time::Duration::from_secs(data.cache_duration_sec),
                );
                Some(JoinLeaveData { csrf })
            } else if status.map_or(true, |x| x == "started") && player_itself {
                if let Some(ref delegate_name) = delegate_name {
                    let csrf = Uuid::new_v4();
                    let mut cache = data.cache_revoke_delegation_game.lock().await;
                    cache.insert(
                        csrf,
                        (
                            gameuid.clone(),
                            user.to_lowercase(),
                            delegate_name.to_lowercase(),
                        ),
                        std::time::Duration::from_secs(data.cache_duration_sec),
                    );
                    Some(JoinLeaveData { csrf })
                } else {
                    None
                }
            } else {
                None
            };
            let query_delegate_data = if status.map_or(true, |x| x == "started")
                && player_itself
                && delegate_name.is_none()
            {
                let csrf = Uuid::new_v4();
                let mut cache = data.cache_query_delegation_game.lock().await;
                cache.insert(
                    csrf,
                    (gameuid.clone(), user.to_lowercase()),
                    std::time::Duration::from_secs(data.cache_duration_sec),
                );
                Some(JoinLeaveData { csrf })
            } else {
                None
            };
            users.push(PlayerInfo {
                player_name,
                team_id,
                delegate_name,
                player_itself,
                delegate_itself,
                revoke_delegate_data,
                query_delegate_data,
                pending_delegates,
                create_ts,
            });
        }

        let (join_data, leave_data) = if !has_itself && status.is_none() {
            let csrf = Uuid::new_v4();
            let mut cache = data.cache_join_game.lock().await;
            cache.insert(
                csrf,
                (gameuid.clone(), user.to_lowercase()),
                std::time::Duration::from_secs(data.cache_duration_sec),
            );
            (Some(JoinLeaveData { csrf }), None)
        } else if has_itself && status.is_none() {
            let csrf = Uuid::new_v4();
            let mut cache = data.cache_leave_game.lock().await;
            cache.insert(
                csrf,
                (gameuid.clone(), user.to_lowercase()),
                std::time::Duration::from_secs(data.cache_duration_sec),
            );
            (None, Some(JoinLeaveData { csrf }))
        } else {
            (None, None)
        };

        (Some(users), join_data, leave_data)
    } else if status.is_none() {
        let stmt = match dbclient.prepare("select p.player_name, p.team_id from games.players p where p.game_uid = $1 and p.client_type = 'p';").await {
            Ok(stmt) => stmt,
            Err(e) => {
                log::error!("{}", e);
                return HttpResponse::ServiceUnavailable().body(actix_web::body::None::new());
            }
        };
        let rows = match dbclient.query(&stmt, &[&gameuid]).await {
            Ok(rows) => rows,
            Err(e) => {
                log::error!("{}", e);
                return HttpResponse::ServiceUnavailable().body(actix_web::body::None::new());
            }
        };
        let mut users = Vec::with_capacity(rows.len());
        for row in rows {
            users.push(PlayerInfo {
                player_name: row.get::<_, String>(0),
                team_id: row.get::<_, i32>(1),
                delegate_name: None,
                player_itself: false,
                delegate_itself: false,
                revoke_delegate_data: None,
                query_delegate_data: None,
                pending_delegates: None,
                create_ts: None,
            });
        }
        (Some(users), None, None)
    } else {
        (None, None, None)
    };

    let body = match data.handlebars_xml.render(
        SLOW_GAME,
        &GameData {
            common_auth_info: CommonAuthInfo {
                user: user.map(Into::into),
            },
            gameuid: &gameuid,
            gameuidenc: pct_str::PctString::encode(gameuid.chars(), pct_str::URIReserved)
                .into_string(),
            status,
            notes_html: row.get::<_, Option<&str>>(1),
            fo_forum_url: row.get::<_, Option<&str>>(2),
            is_teamed: row.get::<_, bool>(3),
            players: row.get::<_, i64>(4),
            min_turn_ts: min_turn_ts.map(diff_utc_to_string),
            max_turn_ts: max_turn_ts.map(diff_utc_to_string),
            turns: row.get::<_, i64>(7),
            player_list,
            join_data,
            leave_data,
        },
    ) {
        Ok(b) => b,
        Err(e) => {
            log::error!("{}", e);
            return HttpResponse::ServiceUnavailable().body(actix_web::body::None::new());
        }
    };
    insert_security_headers(HttpResponse::Ok()).body(body)
}

pub async fn post_join_game(
    request: HttpRequest,
    form: web::Form<JoinLeaveData>,
    data: web::Data<WebData<'_>>,
    data_rw: web::Data<DataBaseRw>,
) -> HttpResponse {
    let cached_data = {
        let mut cache = data.cache_join_game.lock().await;
        cache.remove(&form.csrf)
    };
    let cached_data = if let Some(cd) = cached_data {
        cd
    } else {
        log::warn!("Unknown data for CSRF: {}", form.csrf);
        return HttpResponse::BadRequest().body("Incorrect");
    };

    let jar = request_to_jar(request);

    if jar
        .private(&data.cookies_key)
        .get("auth")
        .map_or(true, |x| x.value().to_lowercase() != cached_data.1)
    {
        log::warn!("Incorrect user");
        return HttpResponse::BadRequest().body("Incorrect");
    }

    let dbclient_rw = match data_rw.0.get().await {
        Ok(c) => c,
        Err(e) => {
            log::error!("Pool RW error {}", e);
            return HttpResponse::ServiceUnavailable().body(actix_web::body::None::new());
        }
    };
    let stmt = match dbclient_rw
        .prepare("insert into games.players (game_uid, player_name, is_confirmed, species, client_type, create_ts) values ($1, $2, true, 'RANDOM', 'p', NOW());")
        .await
    {
        Ok(stmt) => stmt,
        Err(e) => {
            log::error!("Pool RW statement error {}", e);
            return HttpResponse::ServiceUnavailable().body(actix_web::body::None::new());
        }
    };
    let inserted = match dbclient_rw
        .execute(&stmt, &[&cached_data.0, &cached_data.1])
        .await
    {
        Ok(c) => c,
        Err(e) => {
            log::error!("Pool RW execute insert error {}", e);
            return HttpResponse::ServiceUnavailable().body(actix_web::body::None::new());
        }
    };
    if inserted == 0 {
        log::error!("Pool RW execute insert row error");
        return HttpResponse::ServiceUnavailable().body(actix_web::body::None::new());
    }

    HttpResponse::Found()
        .append_header((
            header::LOCATION,
            format!("slow-game-{}.html", cached_data.0),
        ))
        .finish()
}

pub async fn post_leave_game(
    request: HttpRequest,
    form: web::Form<JoinLeaveData>,
    data: web::Data<WebData<'_>>,
    data_rw: web::Data<DataBaseRw>,
) -> HttpResponse {
    let cached_data = {
        let mut cache = data.cache_leave_game.lock().await;
        cache.remove(&form.csrf)
    };
    let cached_data = if let Some(cd) = cached_data {
        cd
    } else {
        log::warn!("Unknown data for CSRF: {}", form.csrf);
        return HttpResponse::BadRequest().body("Incorrect");
    };

    let jar = request_to_jar(request);

    if jar
        .private(&data.cookies_key)
        .get("auth")
        .map_or(true, |x| x.value().to_lowercase() != cached_data.1)
    {
        log::warn!("Incorrect user");
        return HttpResponse::BadRequest().body("Incorrect");
    }

    let dbclient_rw = match data_rw.0.get().await {
        Ok(c) => c,
        Err(e) => {
            log::error!("Pool RW error {}", e);
            return HttpResponse::ServiceUnavailable().body(actix_web::body::None::new());
        }
    };
    let stmt = match dbclient_rw
        .prepare("delete from games.players where game_uid = $1 and player_name = $2;")
        .await
    {
        Ok(stmt) => stmt,
        Err(e) => {
            log::error!("Pool RW statement error {}", e);
            return HttpResponse::ServiceUnavailable().body(actix_web::body::None::new());
        }
    };
    let inserted = match dbclient_rw
        .execute(&stmt, &[&cached_data.0, &cached_data.1])
        .await
    {
        Ok(c) => c,
        Err(e) => {
            log::error!("Pool RW execute insert error {}", e);
            return HttpResponse::ServiceUnavailable().body(actix_web::body::None::new());
        }
    };
    if inserted == 0 {
        log::error!("Pool RW execute insert row error");
        return HttpResponse::ServiceUnavailable().body(actix_web::body::None::new());
    }

    HttpResponse::Found()
        .append_header((
            header::LOCATION,
            format!("slow-game-{}.html", cached_data.0),
        ))
        .finish()
}

pub async fn post_revoke_delegate(
    request: HttpRequest,
    form: web::Form<JoinLeaveData>,
    data: web::Data<WebData<'_>>,
    data_rw: web::Data<DataBaseRw>,
) -> HttpResponse {
    let cached_data = {
        let mut cache = data.cache_revoke_delegation_game.lock().await;
        cache.remove(&form.csrf)
    };
    let cached_data = if let Some(cd) = cached_data {
        cd
    } else {
        log::warn!("Unknown data for CSRF: {}", form.csrf);
        return HttpResponse::BadRequest().body("Incorrect");
    };

    let jar = request_to_jar(request);

    if jar
        .private(&data.cookies_key)
        .get("auth")
        .map(|x| x.value().to_lowercase())
        .map_or(true, |x| x != cached_data.1 && x != cached_data.2)
    {
        log::warn!("Incorrect user");
        return HttpResponse::BadRequest().body("Incorrect");
    }

    let dbclient_rw = match data_rw.0.get().await {
        Ok(c) => c,
        Err(e) => {
            log::error!("Pool RW error {}", e);
            return HttpResponse::ServiceUnavailable().body(actix_web::body::None::new());
        }
    };
    let stmt = match dbclient_rw
        .prepare("update games.players set delegate_name = null where game_uid = $1 and player_name = $2 and delegate_name = $3;")
        .await
    {
        Ok(stmt) => stmt,
        Err(e) => {
            log::error!("Pool RW statement error {}", e);
            return HttpResponse::ServiceUnavailable().body(actix_web::body::None::new());
        }
    };
    let inserted = match dbclient_rw
        .execute(&stmt, &[&cached_data.0, &cached_data.1, &cached_data.2])
        .await
    {
        Ok(c) => c,
        Err(e) => {
            log::error!("Pool RW execute insert error {}", e);
            return HttpResponse::ServiceUnavailable().body(actix_web::body::None::new());
        }
    };
    if inserted == 0 {
        log::error!("Pool RW execute insert row error");
        return HttpResponse::ServiceUnavailable().body(actix_web::body::None::new());
    }

    HttpResponse::Found()
        .append_header((
            header::LOCATION,
            format!("slow-game-{}.html", cached_data.0),
        ))
        .finish()
}

pub async fn post_query_delegate(
    request: HttpRequest,
    form: web::Form<QueryDelegationData>,
    data: web::Data<WebData<'_>>,
    data_rw: web::Data<DataBaseRw>,
) -> HttpResponse {
    let cached_data = {
        let mut cache = data.cache_query_delegation_game.lock().await;
        cache.remove(&form.csrf)
    };
    let cached_data = if let Some(cd) = cached_data {
        cd
    } else {
        log::warn!("Unknown data for CSRF: {}", form.csrf);
        return HttpResponse::BadRequest().body("Incorrect");
    };

    let jar = request_to_jar(request);

    if jar
        .private(&data.cookies_key)
        .get("auth")
        .map_or(true, |x| x.value().to_lowercase() != cached_data.1)
    {
        log::warn!("Incorrect user");
        return HttpResponse::BadRequest().body("Incorrect");
    }

    let dbclient_rw = match data_rw.0.get().await {
        Ok(c) => c,
        Err(e) => {
            log::error!("Pool RW error {}", e);
            return HttpResponse::ServiceUnavailable().body(actix_web::body::None::new());
        }
    };
    let stmt = match dbclient_rw
        .prepare("insert into games.query_delegation (game_uid, player_name, delegate_name) values ($1, $2, $3) on conflict do nothing;")
        .await
    {
        Ok(stmt) => stmt,
        Err(e) => {
            log::error!("Pool RW statement error {}", e);
            return HttpResponse::ServiceUnavailable().body(actix_web::body::None::new());
        }
    };
    match dbclient_rw
        .execute(
            &stmt,
            &[&cached_data.0, &cached_data.1, &form.delegate_name],
        )
        .await
    {
        Ok(_) => (),
        Err(e) => {
            log::error!("Pool RW execute insert error {}", e);
            return HttpResponse::ServiceUnavailable().body(actix_web::body::None::new());
        }
    };

    HttpResponse::Found()
        .append_header((
            header::LOCATION,
            format!("slow-game-{}.html", cached_data.0),
        ))
        .finish()
}

pub async fn post_delegate(
    request: HttpRequest,
    form: web::Form<DelegationData>,
    data: web::Data<WebData<'_>>,
    data_rw: web::Data<DataBaseRw>,
) -> HttpResponse {
    let cached_data = {
        let mut cache = data.cache_delegation_game.lock().await;
        cache.remove(&form.csrf)
    };
    let cached_data = if let Some(cd) = cached_data {
        cd
    } else {
        log::warn!("Unknown data for CSRF: {}", form.csrf);
        return HttpResponse::BadRequest().body("Incorrect");
    };

    let jar = request_to_jar(request);

    let user = if let Some(u) = jar
        .private(&data.cookies_key)
        .get("auth")
        .map(|x| x.value().to_lowercase())
    {
        u
    } else {
        log::warn!("Incorrect user");
        return HttpResponse::BadRequest().body("Incorrect");
    };

    if form.submit == "Accept Delegate" && user == cached_data.2 {
        let dbclient_rw = match data_rw.0.get().await {
            Ok(c) => c,
            Err(e) => {
                log::error!("Pool RW error {}", e);
                return HttpResponse::ServiceUnavailable().body(actix_web::body::None::new());
            }
        };
        match dbclient_rw
            .execute(
                "delete from games.query_delegation where game_uid = $1 and player_name = $2;",
                &[&cached_data.0, &cached_data.1],
            )
            .await
        {
            Ok(_) => (),
            Err(e) => {
                log::error!("Pool RW statement error {}", e);
                return HttpResponse::ServiceUnavailable().body(actix_web::body::None::new());
            }
        };
        match dbclient_rw
            .execute("update games.players set delegate_name = $3 where game_uid = $1 and player_name = $2;", &[&cached_data.0, &cached_data.1, &cached_data.2])
            .await
        {
            Ok(_) => (),
            Err(e) => {
                log::error!("Pool RW statement error {}", e);
                return HttpResponse::ServiceUnavailable().body(actix_web::body::None::new());
            }
        };
    } else if form.submit == "Reject Delegate" && (user == cached_data.1 || user == cached_data.2) {
        let dbclient_rw = match data_rw.0.get().await {
            Ok(c) => c,
            Err(e) => {
                log::error!("Pool RW error {}", e);
                return HttpResponse::ServiceUnavailable().body(actix_web::body::None::new());
            }
        };
        let stmt = match dbclient_rw
            .prepare("delete from games.query_delegation where game_uid = $1 and player_name = $2 and delegate_name = $3;")
            .await
        {
            Ok(stmt) => stmt,
            Err(e) => {
                log::error!("Pool RW statement error {}", e);
                return HttpResponse::ServiceUnavailable().body(actix_web::body::None::new());
            }
        };
        match dbclient_rw
            .execute(&stmt, &[&cached_data.0, &cached_data.1, &cached_data.2])
            .await
        {
            Ok(_) => (),
            Err(e) => {
                log::error!("Pool RW execute insert error {}", e);
                return HttpResponse::ServiceUnavailable().body(actix_web::body::None::new());
            }
        };
    } else {
        log::warn!("Incorrect user submit {}", form.submit);
        return HttpResponse::BadRequest().body("Incorrect");
    }

    HttpResponse::Found()
        .append_header((
            header::LOCATION,
            format!("slow-game-{}.html", cached_data.0),
        ))
        .finish()
}