//! §7 M3 read-only projects surface: `GET /api/v1/admin/projects` — every
//! project (repo-root) with the count of group nodes it owns. Backs
//! `pic projects ls`. Behind the `/admin` middleware, so any authenticated
//! admin can list them (a project reveals only org structure, like the groups
//! list); ownership WRITES stay in the apply path.

use std::sync::Arc;

use axum::extract::State;
use axum::http::StatusCode;
use axum::response::{IntoResponse, Json, Response};
use axum::routing::get;
use axum::{Extension, Router};
use chrono::{DateTime, Utc};
use picloud_shared::Principal;
use serde::Serialize;
use serde_json::json;

use crate::project_repo::{ProjectRepository, ProjectRepositoryError};

#[derive(Clone)]
pub struct ProjectsState {
    pub projects: Arc<dyn ProjectRepository>,
}

/// Mounted under `/api/v1/admin`.
pub fn projects_router(state: ProjectsState) -> Router {
    Router::new()
        .route("/projects", get(list_projects))
        .with_state(state)
}

#[derive(Serialize)]
struct ProjectListItem {
    slug: String,
    name: String,
    owned_groups: i64,
    created_at: DateTime<Utc>,
}

async fn list_projects(
    State(s): State<ProjectsState>,
    Extension(_principal): Extension<Principal>,
) -> Result<Json<Vec<ProjectListItem>>, ProjectsApiError> {
    let items = s
        .projects
        .list_with_counts()
        .await?
        .into_iter()
        .map(|(p, owned_groups)| ProjectListItem {
            slug: p.slug,
            name: p.name,
            owned_groups,
            created_at: p.created_at,
        })
        .collect();
    Ok(Json(items))
}

#[derive(Debug, thiserror::Error)]
pub enum ProjectsApiError {
    #[error("backend: {0}")]
    Backend(String),
}

impl From<ProjectRepositoryError> for ProjectsApiError {
    fn from(e: ProjectRepositoryError) -> Self {
        Self::Backend(e.to_string())
    }
}

impl IntoResponse for ProjectsApiError {
    fn into_response(self) -> Response {
        tracing::error!(error = %self, "projects api error");
        (
            StatusCode::INTERNAL_SERVER_ERROR,
            Json(json!({ "error": "internal error" })),
        )
            .into_response()
    }
}
