//! `GET /api/oidc/auth-query?code=&id=&uuid=` — desktop-client poll loop.
//!
//! Wire shape: return the inner payload as the HTTP body directly. Do NOT
//! wrap in another `{ "body": ... }` envelope — the desktop client's
//! transport (`http_request_sync` in src/common.rs) already wraps every
//! response in `{ status_code, headers, body }` and feeds the inner `body`
//! string to `HbbHttpResponse::parse`. An extra envelope makes the parser
//! see `{"body": "..."}`, fail to deserialize as `AuthBody`, and silently
//! retry until the 180 s client timeout. Spent half a day on this — keep
//! the bare shape.
//!
//! Inner payloads:
//!   - while pending: `{"error":"No authed oidc is found"}` — client keeps polling.
//!   - on success:   `{access_token, type:"access_token", user}` — client stops.
//!   - on error:     `{"error":"<message>"}` — client surfaces and stops polling.

use crate::api::error::ApiError;
use crate::api::state::AppState;
use crate::api::users::UserPayload;
use axum::extract::{Extension, Query};
use axum::Json;
use serde::Deserialize;
use serde_json::{json, Value};
use std::sync::Arc;

#[derive(Debug, Deserialize)]
pub struct PollQuery {
    pub code: String,
    #[serde(default)]
    pub id: String,
    #[serde(default)]
    pub uuid: String,
}

pub async fn auth_query(
    Extension(state): Extension<Arc<AppState>>,
    Query(q): Query<PollQuery>,
) -> Result<Json<Value>, ApiError> {
    let now = chrono::Utc::now().timestamp();
    let session = state
        .db
        .oidc_session_get_by_code(&q.code)
        .await
        .map_err(|e| ApiError::Internal(e.to_string()))?
        .ok_or_else(|| ApiError::BadRequest("unknown oidc session".into()))?;
    hbb_common::log::info!(
        "oidc poll: code={} status={} user_id={:?} elapsed_to_expiry={}",
        q.code,
        session.status,
        session.user_id,
        session.expires_at - now,
    );
    if session.expires_at <= now && session.status == "pending" {
        // The client treats this as an ordinary "still pending" tick and
        // gives up on its own timeout (180 s).
        return Ok(Json(json!({"error": "No authed oidc is found"})));
    }
    match session.status.as_str() {
        "pending" => Ok(Json(json!({"error": "No authed oidc is found"}))),
        "error" => {
            let msg = session
                .error
                .clone()
                .unwrap_or_else(|| "OIDC sign-in failed".to_string());
            Ok(Json(json!({ "error": msg })))
        }
        "success" => {
            let access_token = session
                .access_token
                .clone()
                .ok_or_else(|| ApiError::Internal("success session missing token".into()))?;
            let user_id = session
                .user_id
                .ok_or_else(|| ApiError::Internal("success session missing user_id".into()))?;
            let user = state
                .db
                .user_find_by_id(user_id)
                .await
                .map_err(|e| ApiError::Internal(e.to_string()))?
                .ok_or_else(|| ApiError::Internal("user vanished mid-flow".into()))?;
            Ok(Json(json!({
                "access_token": access_token,
                "type": "access_token",
                "user": UserPayload::from(&user),
            })))
        }
        other => Err(ApiError::Internal(format!(
            "unknown oidc status {:?}",
            other
        ))),
    }
}
