use axum::{
    extract::{Path, State},
    http::StatusCode,
    response::IntoResponse,
    routing::{get, post},
    Json, Router,
};

use crate::AppState;
use theseus_core::*;

pub(crate) fn router() -> Router<AppState> {
    Router::new()
        .route("/api/projects", post(create_project).get(list_projects))
        .route(
            "/api/projects/:project_id",
            get(get_project).delete(delete_project),
        )
}

async fn create_project(
    State(state): State<AppState>,
    Json(payload): Json<CreateProjectRequest>,
) -> Result<impl IntoResponse, StatusCode> {
    let name = payload.name.trim();
    if name.is_empty() {
        return Ok((
            StatusCode::BAD_REQUEST,
            Json(ApiResponse::<Project>::err(
                "Project name must not be empty",
            )),
        ));
    }
    if name.len() > 200 {
        return Ok((
            StatusCode::BAD_REQUEST,
            Json(ApiResponse::<Project>::err(
                "Project name must not exceed 200 characters",
            )),
        ));
    }
    if name.contains('\n') || name.contains('\r') {
        return Ok((
            StatusCode::BAD_REQUEST,
            Json(ApiResponse::<Project>::err(
                "Project name must not contain newlines",
            )),
        ));
    }
    if state
        .db
        .find_project_by_name(name)
        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
        .is_some()
    {
        return Ok((
            StatusCode::CONFLICT,
            Json(ApiResponse::<Project>::err(&format!(
                "Project name already exists: {name}"
            ))),
        ));
    }
    let project = state
        .db
        .create_project(&CreateProjectRequest {
            name: name.to_string(),
        })
        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
    Ok((StatusCode::CREATED, Json(ApiResponse::ok(project))))
}

async fn list_projects(
    State(state): State<AppState>,
) -> Result<Json<ApiResponse<Vec<Project>>>, StatusCode> {
    let projects = state
        .db
        .list_projects()
        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
    Ok(Json(ApiResponse::ok(projects)))
}

async fn get_project(
    State(state): State<AppState>,
    Path(project_id): Path<String>,
) -> Result<Json<ApiResponse<Project>>, StatusCode> {
    let pid = crate::resolve_project_id(&state.db, &project_id).await?;
    let project = state
        .db
        .get_project(pid)
        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
        .ok_or(StatusCode::NOT_FOUND)?;
    Ok(Json(ApiResponse::ok(project)))
}

async fn delete_project(
    State(state): State<AppState>,
    Path(project_id): Path<String>,
) -> Result<Json<ApiResponse<()>>, StatusCode> {
    let pid = crate::resolve_project_id(&state.db, &project_id).await?;
    let deleted = state
        .db
        .delete_project(pid)
        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
    if deleted {
        Ok(Json(ApiResponse::ok(())))
    } else {
        Err(StatusCode::NOT_FOUND)
    }
}
