use axum::{
    response::IntoResponse,
    extract::{Json, Path},
    Extension,
};
use axum_extra::extract::CookieJar;
use crate::{
    get_user_from_token,
    routes::api::v1::{UpdateJournalPrivacy, CreateJournal, UpdateJournalTitle},
    State,
};
use tetratto_core::model::{
    journals::{Journal, JournalPrivacyPermission},
    oauth,
    permissions::FinePermission,
    ApiReturn, Error,
};

pub async fn get_request(
    jar: CookieJar,
    Path(id): Path<usize>,
    Extension(data): Extension<State>,
) -> impl IntoResponse {
    let data = &(data.read().await).0;
    let user = match get_user_from_token!(jar, data, oauth::AppScope::UserReadJournals) {
        Some(ua) => ua,
        None => return Json(Error::NotAllowed.into()),
    };

    let journal = match data.get_journal_by_id(id).await {
        Ok(x) => x,
        Err(e) => return Json(e.into()),
    };

    if journal.privacy == JournalPrivacyPermission::Private
        && user.id != journal.owner
        && !user.permissions.contains(FinePermission::MANAGE_JOURNALS)
    {
        return Json(Error::NotAllowed.into());
    }

    Json(ApiReturn {
        ok: true,
        message: "Success".to_string(),
        payload: Some(journal),
    })
}

pub async fn list_request(jar: CookieJar, Extension(data): Extension<State>) -> impl IntoResponse {
    let data = &(data.read().await).0;
    let user = match get_user_from_token!(jar, data, oauth::AppScope::UserReadJournals) {
        Some(ua) => ua,
        None => return Json(Error::NotAllowed.into()),
    };

    match data.get_journals_by_user(user.id).await {
        Ok(x) => Json(ApiReturn {
            ok: true,
            message: "Success".to_string(),
            payload: Some(x),
        }),
        Err(e) => Json(e.into()),
    }
}

pub async fn create_request(
    jar: CookieJar,
    Extension(data): Extension<State>,
    Json(props): Json<CreateJournal>,
) -> impl IntoResponse {
    let data = &(data.read().await).0;
    let user = match get_user_from_token!(jar, data, oauth::AppScope::UserCreateJournals) {
        Some(ua) => ua,
        None => return Json(Error::NotAllowed.into()),
    };

    match data
        .create_journal(Journal::new(user.id, props.title))
        .await
    {
        Ok(x) => Json(ApiReturn {
            ok: true,
            message: "Journal created".to_string(),
            payload: Some(x.id.to_string()),
        }),
        Err(e) => Json(e.into()),
    }
}

pub async fn update_title_request(
    jar: CookieJar,
    Path(id): Path<usize>,
    Extension(data): Extension<State>,
    Json(mut props): Json<UpdateJournalTitle>,
) -> impl IntoResponse {
    let data = &(data.read().await).0;
    let user = match get_user_from_token!(jar, data, oauth::AppScope::UserManageJournals) {
        Some(ua) => ua,
        None => return Json(Error::NotAllowed.into()),
    };

    props.title = props.title.replace(" ", "_");

    // make sure this title isn't already in use
    if data
        .get_journal_by_owner_title(user.id, &props.title)
        .await
        .is_ok()
    {
        return Json(Error::TitleInUse.into());
    }

    // ...
    match data.update_journal_title(id, &user, &props.title).await {
        Ok(_) => Json(ApiReturn {
            ok: true,
            message: "Journal updated".to_string(),
            payload: (),
        }),
        Err(e) => Json(e.into()),
    }
}

pub async fn update_privacy_request(
    jar: CookieJar,
    Path(id): Path<usize>,
    Extension(data): Extension<State>,
    Json(props): Json<UpdateJournalPrivacy>,
) -> impl IntoResponse {
    let data = &(data.read().await).0;
    let user = match get_user_from_token!(jar, data, oauth::AppScope::UserManageJournals) {
        Some(ua) => ua,
        None => return Json(Error::NotAllowed.into()),
    };

    match data.update_journal_privacy(id, &user, props.privacy).await {
        Ok(_) => Json(ApiReturn {
            ok: true,
            message: "Journal updated".to_string(),
            payload: (),
        }),
        Err(e) => Json(e.into()),
    }
}

pub async fn delete_request(
    jar: CookieJar,
    Path(id): Path<usize>,
    Extension(data): Extension<State>,
) -> impl IntoResponse {
    let data = &(data.read().await).0;
    let user = match get_user_from_token!(jar, data, oauth::AppScope::UserManageJournals) {
        Some(ua) => ua,
        None => return Json(Error::NotAllowed.into()),
    };

    match data.delete_journal(id, &user).await {
        Ok(_) => Json(ApiReturn {
            ok: true,
            message: "Journal deleted".to_string(),
            payload: (),
        }),
        Err(e) => Json(e.into()),
    }
}