use std::sync::Arc;

use axum::body::Bytes;
use axum::extract::State;
use axum::http::StatusCode;
use axum::response::IntoResponse;
use base64::{Engine as B64Engine, engine::general_purpose::STANDARD as b64};
use hyper::HeaderMap;

pub async fn reset_login_start(
    State(state): State<Arc<crate::server::AppState>>,
    body: Bytes,
) -> impl IntoResponse {
    let span = tracing::info_span!("auth", flv = "wasm");

    span.in_scope(|| match state.auth.reset_password_login_start(body) {
        Ok(v) => (StatusCode::OK, v),
        Err(e) => {
            tracing::error!("{e}");
            (StatusCode::INTERNAL_SERVER_ERROR, Bytes::new())
        }
    })
}

pub async fn reset_login_finish(
    State(state): State<Arc<crate::server::AppState>>,
    body: Bytes,
) -> impl IntoResponse {
    let span = tracing::info_span!("auth", flv = "wasm");

    span.in_scope(|| match state.auth.reset_password_login_finish(body) {
        Ok(v) => (StatusCode::OK, v),
        Err(e) => {
            tracing::error!("{e}");
            (StatusCode::INTERNAL_SERVER_ERROR, Bytes::new())
        }
    })
}

pub async fn reset_registration_start(
    State(state): State<Arc<crate::server::AppState>>,
    headers: HeaderMap,
    body: Bytes,
) -> impl IntoResponse {
    let span = tracing::info_span!("auth", flv = "wasm");

    span.in_scope(|| {
        let mut token: Vec<u8> = vec![];

        if let Some(val) = headers.get("authorization")
            && let Ok(str_val) = val.to_str()
            && let Some(b64_token) = str_val.strip_prefix("Ordinary ")
            && let Ok(t) = b64.decode(b64_token)
        {
            token = t;
        }

        if token.is_empty() {
            return (StatusCode::UNAUTHORIZED, Bytes::new());
        }

        match state.auth.reset_password_registration_start(body, &token) {
            Ok(v) => (StatusCode::OK, v),
            Err(e) => {
                tracing::error!("{e}");
                (StatusCode::INTERNAL_SERVER_ERROR, Bytes::new())
            }
        }
    })
}

pub async fn reset_registration_finish(
    State(state): State<Arc<crate::server::AppState>>,
    headers: HeaderMap,
    body: Bytes,
) -> impl IntoResponse {
    let span = tracing::info_span!("auth", flv = "wasm");

    span.in_scope(|| {
        let mut token: Vec<u8> = vec![];

        if let Some(val) = headers.get("authorization")
            && let Ok(str_val) = val.to_str()
            && let Some(b64_token) = str_val.strip_prefix("Ordinary ")
            && let Ok(t) = b64.decode(b64_token)
        {
            token = t;
        }

        if token.is_empty() {
            return StatusCode::UNAUTHORIZED;
        }

        match state.auth.reset_password_registration_finish(body, &token) {
            Ok(()) => StatusCode::OK,
            Err(e) => {
                tracing::error!("{e}");
                StatusCode::INTERNAL_SERVER_ERROR
            }
        }
    })
}

pub async fn forgot_start(
    State(state): State<Arc<crate::server::AppState>>,
    body: Bytes,
) -> impl IntoResponse {
    let span = tracing::info_span!("auth", flv = "wasm");

    span.in_scope(|| match state.auth.forgot_password_start(body) {
        Ok(v) => (StatusCode::OK, v),
        Err(e) => {
            tracing::error!("{e}");
            (StatusCode::INTERNAL_SERVER_ERROR, Bytes::new())
        }
    })
}

pub async fn forgot_finish(
    State(state): State<Arc<crate::server::AppState>>,
    body: Bytes,
) -> impl IntoResponse {
    let span = tracing::info_span!("auth", flv = "wasm");

    span.in_scope(|| match state.auth.forgot_password_finish(body) {
        Ok(()) => StatusCode::OK,
        Err(e) => {
            tracing::error!("{e}");
            StatusCode::INTERNAL_SERVER_ERROR
        }
    })
}
