use matrix_sdk::ruma::events::room::message;

async fn on_room_message(event: message::OriginalSyncRoomMessageEvent, room: matrix_sdk::room::Room, client: matrix_sdk::Client) {
    let cl = client.clone();
    let this_uid = cl.user_id().unwrap();
    if event.clone().sender.matrix_uri(false) == this_uid.matrix_uri(false) {
        return;
    }
    let message::MessageType::Text(text_content) = event.clone().content.msgtype else {
        return;
    };


    let r = client.get_joined_room(room.room_id()).unwrap();

    if text_content.body.contains("!diru") {
        if text_content.body.contains("dpl") {
            deepl_translation(event, r, &text_content.body).await;
        }
    }
}

async fn autojoin_room(room_member: matrix_sdk::ruma::events::room::member::StrippedRoomMemberEvent, client: matrix_sdk::Client, room: matrix_sdk::room::Room) {
    if room_member.state_key != client.user_id().unwrap() {
        return;
    }

    tokio::spawn(async move {
        let rid = room.room_id();
        tracing::info!("Autojoining {}", rid);
        let mut delay = 2;
        let r = client.get_invited_room(rid).unwrap();
        while let Err(err) = r.accept_invitation().await {
            eprintln!("Failed to join room {} ({err:?}), retrying in {delay}s", room.room_id());
            tokio::time::sleep(tokio::time::Duration::from_secs(delay)).await;
            delay *= 2;
            if delay > 3600 {
                eprintln!("Can't join room {} ({err:?})", room.room_id());
                break;
            }
        }
    });
}

async fn deepl_translation(event: message::OriginalSyncRoomMessageEvent, room: matrix_sdk::room::Joined, content: &str) {
    let txn_id = matrix_sdk::ruma::TransactionId::new();
    let msg = message::RoomMessageEventContent::text_plain(format!("{}", content))
        .make_reply_to(&event.clone().into_full_event(room.room_id().into()));
    room.send(msg, Some(&txn_id)).await.unwrap();
}

async fn connect_to_matrix(hs: String, username: String, passwd:String) -> anyhow::Result<()> {
    let client = matrix_sdk::Client::builder().homeserver_url(hs).build().await.unwrap();
    let device_id = std::env::var("DEVICE_ID");

    match device_id {
        Ok(id) => {
            client
                .login_username(&username, &passwd)
                .device_id(&id)
                .send()
                .await?;
        },
        Err(_) => {
            client.login_username(&username, &passwd)
                .initial_device_display_name("Diru Development")
                .send()
                .await?;
        }
    }

    println!("Logged in as {username}");
    let response = client.sync_once(matrix_sdk::config::SyncSettings::default()).await?;
    client.add_event_handler(on_room_message);
    client.add_event_handler(autojoin_room);
    let stg = matrix_sdk::config::SyncSettings::default().token(response.next_batch);
    client.sync(stg).await?;

    Ok(())
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    dotenvy::dotenv().ok();
    tracing_subscriber::fmt::init();

    let (homeserver_url, username, passwd) = match (std::env::var("HOMESERVER_URL"), std::env::var("MATRIX_BOT_NAME"), std::env::var("MATRIX_BOT_PASSWD")) {
        (Ok(a), Ok(b), Ok(c)) => (a, b, c),
        _ => {
            eprintln!("Please ensure the HOMESERVER_URL, MATRIX_BOT_NAME, and MATRIX_BOT_PASSWD environment variables are set.");
            std::process::exit(1)
        }
    };

    connect_to_matrix(homeserver_url, username, passwd).await?;

    Ok(())
}