use crate::api::error::ApiError;
use crate::api::middleware::AuthedUser;
use crate::api::pagination::{Page, PageQuery};
use crate::api::state::AppState;
use axum::extract::{Extension, Query};
use axum::http::StatusCode;
use axum::response::IntoResponse;
use axum::Json;
use serde::Serialize;
use serde_json::{json, Value};
use std::sync::Arc;

/// `POST /api/ab/personal` — returns the caller's personal AB GUID, creating
/// it if missing. When `--ab-legacy-mode=on` is configured, returns 404 to
/// signal "this server speaks the legacy single-blob protocol" (the client
/// then falls back to GET/POST /api/ab).
pub async fn personal(
    Extension(state): Extension<Arc<AppState>>,
    user: AuthedUser,
) -> Result<Json<Value>, ApiError> {
    if state.cfg.ab_legacy_mode {
        return Err(ApiError::NotFound);
    }
    let guid = state
        .db
        .ab_get_or_create_personal(user.user_id)
        .await
        .map_err(|e| ApiError::Internal(e.to_string()))?;
    Ok(Json(json!({ "guid": guid })))
}

/// `POST /api/ab/shared/profiles` — paginated list of shared address books
/// the caller can see. Wire shape matches the Flutter `AbProfile` decoder at
/// flutter/lib/common/hbbs/hbbs.dart:258.
#[derive(Debug, Serialize)]
struct AbProfileOut {
    guid: String,
    name: String,
    owner: String,
    note: String,
    rule: i64,
    #[serde(skip_serializing_if = "Option::is_none")]
    info: Option<Value>,
}

pub async fn shared_profiles(
    Extension(state): Extension<Arc<AppState>>,
    user: AuthedUser,
    Query(page): Query<PageQuery>,
) -> Result<impl IntoResponse, ApiError> {
    let (total, rows) = state
        .db
        .ab_list_shared_for_user(user.user_id, page.offset(), page.limit())
        .await
        .map_err(|e| ApiError::Internal(e.to_string()))?;
    let data = rows
        .into_iter()
        .map(|r| AbProfileOut {
            guid: r.guid,
            name: r.name,
            owner: r.owner,
            note: r.note,
            rule: r.rule,
            info: r
                .info_json
                .as_deref()
                .and_then(|s| serde_json::from_str(s).ok()),
        })
        .collect();
    Ok((StatusCode::OK, Json(Page { total, data })))
}
