use actix_web::{web, HttpResponse};

use crate::pages::templates::ATOM_GAMES;
use crate::{DataBaseRo, WebData};

#[derive(serde_derive::Serialize)]
struct FeedData {
    upddatetime: String,
    entries: Vec<EntriesData>,
    selflink: String,
}

#[derive(serde_derive::Serialize)]
struct EntriesData {
    gameuid: String,
    gameuidenc: String,
    start_ts: i64,
    create_ts_str: String,
    fo_forum_url: String,
    notes_summ: String,
    notes: String,
}

#[actix_web::get("/atoms/new-games.xml")]
pub async fn atom_games(
    data: web::Data<WebData<'_>>,
    data_ro: web::Data<DataBaseRo>,
) -> HttpResponse {
    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.start_ts, g.fo_forum_url, g.create_ts, g.notes_html from games.games g where g.create_ts IS NOT NULL and g.fo_forum_url IS NOT NULL AND g.notes_html IS NOT NULL order by g.create_ts desc limit 50;").await {
        Ok(stmt) => stmt,
        Err(e) => {
            log::error!("{}", e);
            return HttpResponse::ServiceUnavailable().body(actix_web::body::None::new());
        }
    };

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

    let mut upd_date_time = None;

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

    for row in rows {
        let gameuid = row.get::<_, &str>(0);
        use chrono::TimeZone;
        let start_ts =
            chrono::Utc::from_utc_datetime(&chrono::Utc, &row.get::<_, chrono::NaiveDateTime>(1));
        let fo_forum_url = row
            .get::<_, &str>(2)
            .replace('&', "&amp;")
            .replace('<', "&lt;");
        let create_ts =
            chrono::Utc::from_utc_datetime(&chrono::Utc, &row.get::<_, chrono::NaiveDateTime>(3));
        let notes = row.get::<_, String>(4);

        use base64::Engine;
        use sha2::Digest;
        let mut hasher = sha2::Sha224::new();
        hasher.update(notes.as_bytes());
        let notes_summ = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(hasher.finalize());

        entries.insert(
            0,
            EntriesData {
                gameuid: gameuid.to_string(),
                gameuidenc: pct_str::PctString::encode(gameuid.chars(), pct_str::URIReserved)
                    .into_string(),
                start_ts: start_ts.timestamp(),
                create_ts_str: create_ts.to_rfc3339_opts(chrono::SecondsFormat::Secs, true),
                fo_forum_url: fo_forum_url.to_string(),
                notes,
                notes_summ,
            },
        );

        upd_date_time.get_or_insert(create_ts);
    }

    let upd_date_time = upd_date_time.unwrap_or_else(chrono::Utc::now);

    let body = match data.handlebars_xml.render(
        ATOM_GAMES,
        &FeedData {
            entries,
            upddatetime: upd_date_time.to_rfc3339_opts(chrono::SecondsFormat::Secs, true),
            selflink: format!("{}://{}/", data.base_proto, data.base_domain),
        },
    ) {
        Ok(b) => b,
        Err(e) => {
            log::error!("{}", e);
            return HttpResponse::ServiceUnavailable().body(actix_web::body::None::new());
        }
    };
    HttpResponse::Ok()
        .insert_header(("Content-Type", "application/atom+xml"))
        .insert_header((
            "Date",
            upd_date_time
                .format("%a, %d %b %Y %H:%M:%S GMT")
                .to_string(),
        ))
        .body(body)
}