use std::env;

use async_session::{MemoryStore, Session, SessionStore};
use axum::{
    extract::Query,
    http::HeaderMap,
    response::{IntoResponse, Redirect},
    Extension,
};
use oauth2::{
    basic::BasicClient, reqwest::async_http_client, AuthUrl, AuthorizationCode, ClientId,
    ClientSecret, CsrfToken, RedirectUrl, TokenResponse, TokenUrl,
};
use reqwest::header::SET_COOKIE;
use serde::Deserialize;

use crate::{User, COOKIE_NAME};

pub fn oauth_client() -> BasicClient {
    let client_id = env::var("GITEA_CLIENT_ID").expect("Missing GITEA_CLIENT_ID");
    let client_secret = env::var("GITEA_CLIENT_SECRET").expect("Missing GITEA_CLIENT_SECRET");
    let redirect_url = env::var("GITEA_REDIRECT_URL")
        .unwrap_or_else(|_| "http://127.0.0.1:3000/auth/authorized".to_string());

    let auth_url =
        env::var("GITEA_AUTH_URL").unwrap_or_else(|_| "https://git.front.kjuulh.io".to_string());

    let token_url =
        env::var("GITEA_TOKEN_URL").unwrap_or_else(|_| "https://git.front.kjuulh.io".to_string());

    BasicClient::new(
        ClientId::new(client_id),
        Some(ClientSecret::new(client_secret)),
        AuthUrl::new(auth_url).expect("AuthUrl was invalid"),
        Some(TokenUrl::new(token_url).expect("Token url was invalid")),
    )
    .set_redirect_uri(RedirectUrl::new(redirect_url).expect("RedirectUrl was invalid"))
}

pub async fn gitea(Extension(client): Extension<BasicClient>) -> impl IntoResponse {
    let (auth_url, _crsf_token) = client.authorize_url(CsrfToken::new_random).url();

    Redirect::to(&auth_url.to_string())
}

#[derive(Debug, Deserialize)]
pub struct AuthRequest {
    code: String,
    state: String,
}

pub async fn authorized(
    Query(query): Query<AuthRequest>,
    Extension(store): Extension<MemoryStore>,
    Extension(oauth_client): Extension<BasicClient>,
) -> impl IntoResponse {
    let token = oauth_client
        .exchange_code(AuthorizationCode::new(query.code.clone()))
        .request_async(async_http_client)
        .await
        .expect("failed to get http client");

    let client = reqwest::Client::new();
    let user_data_json = client
        .get(get_gitea_user_data_url())
        .bearer_auth(token.access_token().secret())
        .send()
        .await
        .expect("Request did not succeed");
    //      .text()
    //     .await
    //     .unwrap();

    let user_data: User = user_data_json
        .json::<User>()
        .await
        .expect("could not parse user");

    let mut session = Session::new();
    session
        .insert("user", &user_data)
        .expect("could not insert user data");

    let cookie = store
        .store_session(session)
        .await
        .expect("could not insert session")
        .expect("session was not valid");

    let cookie = format!("{}={}; SameSite=Lax; Path=/", COOKIE_NAME, cookie);

    let mut headers = HeaderMap::new();
    headers.insert(SET_COOKIE, cookie.parse().expect("Cookie is not valid"));
    (headers, Redirect::to("/"))
}

fn get_gitea_user_data_url() -> String {
    env::var("GITEA_USER_INFO_URL").expect("Missing GITEA_USER_INFO_URL")
}
