use crate::api::ab::rules::{enforce, Rule};
use crate::api::error::ApiError;
use crate::api::middleware::AuthedUser;
use crate::api::state::AppState;
use axum::extract::{Extension, Path};
use axum::http::StatusCode;
use axum::response::IntoResponse;
use axum::Json;
use serde::{Deserialize, Serialize};
use std::sync::Arc;

/// `POST /api/ab/tags/{guid}` — list tags. Wire shape is a bare JSON array
/// `[{name, color}]`, NOT the `Page<T>` envelope.
#[derive(Debug, Serialize)]
struct TagOut {
    name: String,
    color: i64,
}

pub async fn list(
    Extension(state): Extension<Arc<AppState>>,
    user: AuthedUser,
    Path(guid): Path<String>,
) -> Result<impl IntoResponse, ApiError> {
    enforce(&state, user.user_id, &guid, Rule::Read).await?;
    let rows = state
        .db
        .ab_list_tags(&guid)
        .await
        .map_err(|e| ApiError::Internal(e.to_string()))?;
    let out: Vec<TagOut> = rows
        .into_iter()
        .map(|t| TagOut {
            name: t.name,
            color: t.color,
        })
        .collect();
    Ok((StatusCode::OK, Json(out)))
}

#[derive(Debug, Deserialize)]
pub struct TagAddBody {
    pub name: String,
    pub color: i64,
}

pub async fn add(
    Extension(state): Extension<Arc<AppState>>,
    user: AuthedUser,
    Path(guid): Path<String>,
    Json(body): Json<TagAddBody>,
) -> Result<StatusCode, ApiError> {
    enforce(&state, user.user_id, &guid, Rule::ReadWrite).await?;
    if body.name.is_empty() {
        return Err(ApiError::BadRequest("name required".into()));
    }
    state
        .db
        .ab_tag_insert(&guid, &body.name, body.color)
        .await
        .map_err(|e| ApiError::Internal(e.to_string()))?;
    Ok(StatusCode::OK)
}

#[derive(Debug, Deserialize)]
pub struct TagRenameBody {
    #[serde(rename = "old")]
    pub old_name: String,
    #[serde(rename = "new")]
    pub new_name: String,
}

pub async fn rename(
    Extension(state): Extension<Arc<AppState>>,
    user: AuthedUser,
    Path(guid): Path<String>,
    Json(body): Json<TagRenameBody>,
) -> Result<StatusCode, ApiError> {
    enforce(&state, user.user_id, &guid, Rule::ReadWrite).await?;
    state
        .db
        .ab_tag_rename(&guid, &body.old_name, &body.new_name)
        .await
        .map_err(|e| ApiError::Internal(e.to_string()))?;
    Ok(StatusCode::OK)
}

#[derive(Debug, Deserialize)]
pub struct TagUpdateBody {
    pub name: String,
    pub color: i64,
}

pub async fn update(
    Extension(state): Extension<Arc<AppState>>,
    user: AuthedUser,
    Path(guid): Path<String>,
    Json(body): Json<TagUpdateBody>,
) -> Result<StatusCode, ApiError> {
    enforce(&state, user.user_id, &guid, Rule::ReadWrite).await?;
    state
        .db
        .ab_tag_update_color(&guid, &body.name, body.color)
        .await
        .map_err(|e| ApiError::Internal(e.to_string()))?;
    Ok(StatusCode::OK)
}

pub async fn delete(
    Extension(state): Extension<Arc<AppState>>,
    user: AuthedUser,
    Path(guid): Path<String>,
    Json(names): Json<Vec<String>>,
) -> Result<StatusCode, ApiError> {
    enforce(&state, user.user_id, &guid, Rule::ReadWrite).await?;
    state
        .db
        .ab_tags_delete(&guid, &names)
        .await
        .map_err(|e| ApiError::Internal(e.to_string()))?;
    Ok(StatusCode::OK)
}
