use axum::{ Json, extract::{Path, State}, http::StatusCode, }; use common::middleware::Org; use common::models::LessonDependency; use serde::Deserialize; use sqlx::PgPool; use uuid::Uuid; #[derive(Deserialize)] pub struct AssignDependencyPayload { pub prerequisite_lesson_id: Uuid, pub min_score_percentage: Option, } pub async fn assign_dependency( Org(org_ctx): Org, State(pool): State, Path(lesson_id): Path, Json(payload): Json, ) -> Result, StatusCode> { // 1. Validar que ambas lecciones pertenecen a la organización let count: i64 = sqlx::query_scalar( "SELECT COUNT(*) FROM lessons WHERE id IN ($1, $2) AND organization_id = $3", ) .bind(lesson_id) .bind(payload.prerequisite_lesson_id) .bind(org_ctx.id) .fetch_one(&pool) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; if count != 2 { return Err(StatusCode::NOT_FOUND); } // 2. Insertar la dependencia let dependency: LessonDependency = sqlx::query_as( r#" INSERT INTO lesson_dependencies (organization_id, lesson_id, prerequisite_lesson_id, min_score_percentage) VALUES ($1, $2, $3, $4) ON CONFLICT (lesson_id, prerequisite_lesson_id) DO UPDATE SET min_score_percentage = EXCLUDED.min_score_percentage RETURNING id, organization_id, lesson_id, prerequisite_lesson_id, min_score_percentage, created_at "# ) .bind(org_ctx.id) .bind(lesson_id) .bind(payload.prerequisite_lesson_id) .bind(payload.min_score_percentage) .fetch_one(&pool) .await .map_err(|e| { tracing::error!("Failed to assign dependency: {}", e); StatusCode::INTERNAL_SERVER_ERROR })?; Ok(Json(dependency)) } pub async fn remove_dependency( Org(org_ctx): Org, State(pool): State, Path((lesson_id, prerequisite_id)): Path<(Uuid, Uuid)>, ) -> Result { let result = sqlx::query( "DELETE FROM lesson_dependencies WHERE lesson_id = $1 AND prerequisite_lesson_id = $2 AND organization_id = $3" ) .bind(lesson_id) .bind(prerequisite_id) .bind(org_ctx.id) .execute(&pool) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; if result.rows_affected() == 0 { return Err(StatusCode::NOT_FOUND); } Ok(StatusCode::NO_CONTENT) } pub async fn list_lesson_dependencies( Org(org_ctx): Org, State(pool): State, Path(lesson_id): Path, ) -> Result>, StatusCode> { let dependencies: Vec = sqlx::query_as( "SELECT * FROM lesson_dependencies WHERE lesson_id = $1 AND organization_id = $2" ) .bind(lesson_id) .bind(org_ctx.id) .fetch_all(&pool) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; Ok(Json(dependencies)) }