//! Conflict Resolution API endpoints. use axum::{ extract::{Path, State}, http::StatusCode, routing::{get, post}, Json, Router, }; use serde::Deserialize; use serde_json::{json, Value}; use sqlx::PgPool; use uuid::Uuid; use crate::auth::AuthUser; use crate::plugins::builtin::conflict_resolution::{ConflictCase, ConflictService}; // ============================================================================ // Request Types // ============================================================================ #[derive(Debug, Deserialize)] pub struct ReportConflictRequest { pub conflict_type: String, pub title: String, pub description: String, pub party_a_id: Uuid, pub party_b_id: Option, pub anonymous: bool, pub severity: i32, } #[derive(Debug, Deserialize)] pub struct TransitionStatusRequest { pub new_status: String, pub notes: Option, } #[derive(Debug, Deserialize)] pub struct ProposeCompromiseRequest { pub title: String, pub description: String, pub proposed_actions: Value, pub proposed_by_role: String, } #[derive(Debug, Deserialize)] pub struct RespondToCompromiseRequest { pub party: String, pub response: String, pub feedback: Option, } #[derive(Debug, Deserialize)] pub struct ScheduleSessionRequest { pub scheduled_for: chrono::DateTime, pub duration_minutes: i32, pub agenda: Option, } #[derive(Debug, Deserialize)] pub struct AddNoteRequest { pub content: String, pub note_type: String, pub session_id: Option, pub is_confidential: bool, } #[derive(Debug, Deserialize)] pub struct AddMediatorRequest { pub user_id: Uuid, pub certification_level: Option, } // ============================================================================ // Handlers // ============================================================================ /// Get active conflicts in a community async fn get_active_conflicts( _auth: AuthUser, Path(community_id): Path, State(pool): State, ) -> Result>, (StatusCode, String)> { let conflicts = ConflictService::get_active_conflicts(&pool, community_id) .await .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?; Ok(Json(conflicts)) } /// Get a specific conflict async fn get_conflict( _auth: AuthUser, Path(conflict_id): Path, State(pool): State, ) -> Result>, (StatusCode, String)> { let conflict = ConflictService::get_conflict(&pool, conflict_id) .await .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?; Ok(Json(conflict)) } /// Report a new conflict async fn report_conflict( auth: AuthUser, Path(community_id): Path, State(pool): State, Json(req): Json, ) -> Result, (StatusCode, String)> { let conflict_id = ConflictService::report_conflict( &pool, community_id, &req.title, &req.description, &req.conflict_type, req.party_a_id, req.party_b_id, Some(auth.user_id), req.anonymous, req.severity, ) .await .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?; Ok(Json(json!({"id": conflict_id}))) } /// Transition conflict status async fn transition_status( auth: AuthUser, Path(conflict_id): Path, State(pool): State, Json(req): Json, ) -> Result, (StatusCode, String)> { ConflictService::transition_status( &pool, conflict_id, &req.new_status, auth.user_id, req.notes.as_deref(), ) .await .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?; Ok(Json(json!({"success": true}))) } /// Propose a compromise async fn propose_compromise( auth: AuthUser, Path(conflict_id): Path, State(pool): State, Json(req): Json, ) -> Result, (StatusCode, String)> { let proposal_id = ConflictService::propose_compromise( &pool, conflict_id, &req.title, &req.description, req.proposed_actions, auth.user_id, &req.proposed_by_role, ) .await .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?; Ok(Json(json!({"id": proposal_id}))) } /// Respond to a compromise proposal async fn respond_to_compromise( _auth: AuthUser, Path(proposal_id): Path, State(pool): State, Json(req): Json, ) -> Result, (StatusCode, String)> { ConflictService::respond_to_compromise( &pool, proposal_id, &req.party, &req.response, req.feedback.as_deref(), ) .await .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?; Ok(Json(json!({"success": true}))) } /// Schedule a mediation session async fn schedule_session( _auth: AuthUser, Path(conflict_id): Path, State(pool): State, Json(req): Json, ) -> Result, (StatusCode, String)> { let session_id = ConflictService::schedule_session( &pool, conflict_id, req.scheduled_for, req.duration_minutes, req.agenda.as_deref(), ) .await .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?; Ok(Json(json!({"id": session_id}))) } /// Add a note to a conflict async fn add_note( auth: AuthUser, Path(conflict_id): Path, State(pool): State, Json(req): Json, ) -> Result, (StatusCode, String)> { let note_id = ConflictService::add_note( &pool, conflict_id, req.session_id, auth.user_id, &req.content, &req.note_type, req.is_confidential, ) .await .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?; Ok(Json(json!({"id": note_id}))) } /// Get conflict statistics for a community async fn get_statistics( _auth: AuthUser, Path(community_id): Path, State(pool): State, ) -> Result, (StatusCode, String)> { let stats = ConflictService::get_statistics(&pool, community_id) .await .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?; Ok(Json(stats)) } /// Add user to mediator pool async fn add_to_mediator_pool( _auth: AuthUser, Path(community_id): Path, State(pool): State, Json(req): Json, ) -> Result, (StatusCode, String)> { let mediator_id = ConflictService::add_to_mediator_pool( &pool, community_id, req.user_id, req.certification_level.as_deref(), ) .await .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?; Ok(Json(json!({"id": mediator_id}))) } // ============================================================================ // Router // ============================================================================ pub fn router(pool: PgPool) -> Router { Router::new() // Community conflicts .route( "/api/communities/{community_id}/conflicts", get(get_active_conflicts).post(report_conflict), ) .route( "/api/communities/{community_id}/conflicts/stats", get(get_statistics), ) .route( "/api/communities/{community_id}/mediators", post(add_to_mediator_pool), ) // Individual conflict operations .route("/api/conflicts/{conflict_id}", get(get_conflict)) .route( "/api/conflicts/{conflict_id}/status", post(transition_status), ) .route( "/api/conflicts/{conflict_id}/compromise", post(propose_compromise), ) .route( "/api/conflicts/{conflict_id}/session", post(schedule_session), ) .route("/api/conflicts/{conflict_id}/note", post(add_note)) // Compromise responses .route( "/api/compromises/{proposal_id}/respond", post(respond_to_compromise), ) .with_state(pool) }