//! HTTP request handlers organized by feature area
//!
//! This module contains all API endpoint handlers, organized into logical groups:
//! - analytics: Burndown charts and progress statistics
//! - assignments: Task assignment operations
//! - attachments: File attachment handling
//! - auth: User authentication and session management
//! - comments: Task comment management
//! - dependencies: Task dependency relationships
//! - milestones: Project milestone tracking
//! - presets: Filter preset management
//! - subtasks: Hierarchical task management
//! - tasks_enhanced: Enhanced task operations (update, delete, assign, deadline)

use crate::error::{ApiError, ApiResult};
use crate::models::*;
use crate::state::AppState;
use axum::{
    extract::{Path, State},
    http::StatusCode,
    Json,
};
use chrono::Utc;
use serde_json::json;
use syntaxis_core::persistence::{
    Database, DbChecklistItem, DbProject, DbSecurityAssessment, DbToolConfiguration,
};
use uuid::Uuid;

// Phase 2 Backend Infrastructure feature modules
pub(crate) mod analytics;
pub(crate) mod assignments;
pub(crate) mod attachments;
pub(crate) mod auth;
pub(crate) mod comments;
pub(crate) mod dependencies;
pub(crate) mod milestones;
pub(crate) mod presets;
pub(crate) mod subtasks;
pub(crate) mod tasks_enhanced;

// Re-export core handlers that were in handlers.rs
// These provide the basic project management operations

/// Create a new project
pub(crate) async fn create_project(
    State(state): State<AppState>,
    Json(req): Json<CreateProjectRequest>,
) -> ApiResult<(StatusCode, Json<ProjectDto>)> {
    let now = Utc::now().to_rfc3339();
    let project = DbProject {
        id: Uuid::new_v4().to_string(),
        name: req.name.clone(),
        version: "0.1.0".to_string(),
        description: req.description.unwrap_or_default(),
        project_type: req.project_type.unwrap_or_else(|| "MultiLang".to_string()),
        current_phase: "Creation".to_string(),
        created_at: now.clone(),
        updated_at: now.clone(),
    };

    let created = state.db.create_project(&project).await?;

    let dto = ProjectDto {
        id: created.id.clone(),
        name: created.name.clone(),
        version: created.version.clone(),
        description: created.description.clone(),
        project_type: created.project_type.clone(),
        current_phase: created.current_phase.clone(),
        created_at: created.created_at.clone(),
        updated_at: created.updated_at.clone(),
    };

    state
        .events
        .broadcast(crate::websocket::ProjectEvent::ProjectCreated {
            project_id: created.id,
            project_name: req.name,
            timestamp: now,
        });

    Ok((StatusCode::CREATED, Json(dto)))
}

/// List all projects
pub(crate) async fn list_projects(
    State(state): State<AppState>,
) -> ApiResult<Json<Vec<ProjectDto>>> {
    let projects = state.db.list_projects().await?;

    let dtos = projects
        .into_iter()
        .map(|p| ProjectDto {
            id: p.id,
            name: p.name,
            version: p.version,
            description: p.description,
            project_type: p.project_type,
            current_phase: p.current_phase,
            created_at: p.created_at,
            updated_at: p.updated_at,
        })
        .collect();

    Ok(Json(dtos))
}

/// Get a specific project
pub(crate) async fn get_project(
    State(state): State<AppState>,
    Path(id): Path<String>,
) -> ApiResult<Json<ProjectDto>> {
    let project = state
        .db
        .get_project(&id)
        .await?
        .ok_or_else(|| ApiError::NotFound(format!("Project {} not found", id)))?;

    let dto = ProjectDto {
        id: project.id,
        name: project.name,
        version: project.version,
        description: project.description,
        project_type: project.project_type,
        current_phase: project.current_phase,
        created_at: project.created_at,
        updated_at: project.updated_at,
    };

    Ok(Json(dto))
}

/// Update a project
pub(crate) async fn update_project(
    State(state): State<AppState>,
    Path(id): Path<String>,
    Json(req): Json<UpdateProjectRequest>,
) -> ApiResult<Json<ProjectDto>> {
    let mut project = state
        .db
        .get_project(&id)
        .await?
        .ok_or_else(|| ApiError::NotFound(format!("Project {} not found", id)))?;

    if let Some(version) = req.version {
        project.version = version;
    }
    if let Some(description) = req.description {
        project.description = description;
    }

    project.updated_at = Utc::now().to_rfc3339();
    let updated = state.db.update_project(&project).await?;

    let dto = ProjectDto {
        id: updated.id,
        name: updated.name,
        version: updated.version,
        description: updated.description,
        project_type: updated.project_type,
        current_phase: updated.current_phase,
        created_at: updated.created_at,
        updated_at: updated.updated_at,
    };

    Ok(Json(dto))
}

/// Delete a project
pub(crate) async fn delete_project(
    State(state): State<AppState>,
    Path(id): Path<String>,
) -> ApiResult<StatusCode> {
    // Verify project exists
    let _ = state
        .db
        .get_project(&id)
        .await?
        .ok_or_else(|| ApiError::NotFound(format!("Project {} not found", id)))?;

    state.db.delete_project(&id).await?;

    state
        .events
        .broadcast(crate::websocket::ProjectEvent::ProjectDeleted {
            project_id: id,
            timestamp: Utc::now().to_rfc3339(),
        });

    Ok(StatusCode::NO_CONTENT)
}

/// Get current phase
pub(crate) async fn get_current_phase(
    State(state): State<AppState>,
    Path(id): Path<String>,
) -> ApiResult<Json<serde_json::Value>> {
    let project = state
        .db
        .get_project(&id)
        .await?
        .ok_or_else(|| ApiError::NotFound(format!("Project {} not found", id)))?;

    Ok(Json(json!({
        "project_id": id,
        "phase": project.current_phase,
        "description": format!("Project in {} phase", project.current_phase)
    })))
}

/// Transition to a new phase
pub(crate) async fn transition_phase(
    State(state): State<AppState>,
    Path(id): Path<String>,
    Json(req): Json<PhaseTransitionRequest>,
) -> ApiResult<Json<serde_json::Value>> {
    let mut project = state
        .db
        .get_project(&id)
        .await?
        .ok_or_else(|| ApiError::NotFound(format!("Project {} not found", id)))?;

    let from_phase = project.current_phase.clone();
    let now = Utc::now().to_rfc3339();

    project.current_phase = req.phase.clone();
    project.updated_at = now.clone();
    state.db.update_project(&project).await?;

    state
        .events
        .broadcast(crate::websocket::ProjectEvent::PhaseTransition {
            project_id: id.clone(),
            from_phase: from_phase.clone(),
            to_phase: req.phase.clone(),
            timestamp: now.clone(),
        });

    Ok(Json(json!({
        "project_id": id,
        "from_phase": from_phase,
        "to_phase": req.phase,
        "timestamp": now,
        "reason": req.reason,
        "success": true
    })))
}

/// Get phase history
pub(crate) async fn get_phase_history(
    State(state): State<AppState>,
    Path(id): Path<String>,
) -> ApiResult<Json<Vec<PhaseHistoryDto>>> {
    // Verify project exists
    let _ = state
        .db
        .get_project(&id)
        .await?
        .ok_or_else(|| ApiError::NotFound(format!("Project {} not found", id)))?;

    let phase_histories = state.db.get_phase_history_records(&id).await?;

    let history = phase_histories
        .into_iter()
        .map(|ph| PhaseHistoryDto {
            from_phase: ph.phase.clone(),
            to_phase: ph.phase,
            timestamp: ph.started_at,
            reason: ph.notes,
        })
        .collect();

    Ok(Json(history))
}

/// Get all checklists
pub(crate) async fn get_checklists(
    State(state): State<AppState>,
    Path(id): Path<String>,
) -> ApiResult<Json<Vec<ChecklistItemDto>>> {
    // Verify project exists
    let _ = state
        .db
        .get_project(&id)
        .await?
        .ok_or_else(|| ApiError::NotFound(format!("Project {} not found", id)))?;

    let items = state.db.get_checklist_items(&id).await?;

    let dtos = items
        .into_iter()
        .map(|item| ChecklistItemDto {
            id: item.id,
            task_id: item.task_id,
            description: item.description,
            completed: item.completed,
            completed_at: item.completed_at,
            created_at: item.created_at,
        })
        .collect();

    Ok(Json(dtos))
}

/// Get checklist for a specific phase
pub(crate) async fn get_checklist_by_phase(
    State(state): State<AppState>,
    Path((id, phase)): Path<(String, String)>,
) -> ApiResult<Json<Vec<ChecklistItemDto>>> {
    // Verify project exists
    let _ = state
        .db
        .get_project(&id)
        .await?
        .ok_or_else(|| ApiError::NotFound(format!("Project {} not found", id)))?;

    let items = state.db.get_checklist_items_by_phase(&id, &phase).await?;

    let dtos = items
        .into_iter()
        .map(|item| ChecklistItemDto {
            id: item.id,
            task_id: item.task_id,
            description: item.description,
            completed: item.completed,
            completed_at: item.completed_at,
            created_at: item.created_at,
        })
        .collect();

    Ok(Json(dtos))
}

/// Add checklist item
pub(crate) async fn add_checklist_item(
    State(state): State<AppState>,
    Path(id): Path<String>,
    Json(req): Json<AddChecklistItemRequest>,
) -> ApiResult<(StatusCode, Json<ChecklistItemDto>)> {
    // Verify project exists
    let _ = state
        .db
        .get_project(&id)
        .await?
        .ok_or_else(|| ApiError::NotFound(format!("Project {} not found", id)))?;

    let now = Utc::now().to_rfc3339();
    let item = DbChecklistItem {
        id: Uuid::new_v4().to_string(),
        project_id: id.clone(),
        phase: "Creation".to_string(),
        task_id: req.task_id.clone(),
        description: req.description.clone(),
        completed: false,
        completed_at: None,
        created_at: now.clone(),
        task_type: "Feature".to_string(),
        task_priority: "Normal".to_string(),
        task_due: None,
        task_estimation: None,
        task_deps: String::new(),
        task_note: None,
        task_name: Some(req.description.clone()),
    };

    let created = state.db.create_checklist_item(&item).await?;

    let dto = ChecklistItemDto {
        id: created.id,
        task_id: created.task_id,
        description: created.description,
        completed: created.completed,
        completed_at: created.completed_at,
        created_at: created.created_at,
    };

    Ok((StatusCode::CREATED, Json(dto)))
}

/// Update checklist item
pub(crate) async fn update_checklist_item(
    State(state): State<AppState>,
    Path((project_id, item_id)): Path<(String, String)>,
    Json(req): Json<UpdateChecklistItemRequest>,
) -> ApiResult<Json<ChecklistItemDto>> {
    // Verify project exists
    let _ = state
        .db
        .get_project(&project_id)
        .await?
        .ok_or_else(|| ApiError::NotFound(format!("Project {} not found", project_id)))?;

    let items = state.db.get_checklist_items(&project_id).await?;

    let mut item = items
        .into_iter()
        .find(|i| i.id == item_id)
        .ok_or_else(|| ApiError::NotFound(format!("Item {} not found", item_id)))?;

    if let Some(completed) = req.completed {
        if completed {
            state.db.complete_checklist_item(&item.id).await?;
            item.completed = true;
            item.completed_at = Some(Utc::now().to_rfc3339());
        } else {
            item.completed = false;
            item.completed_at = None;
            state.db.update_checklist_item(&item).await?;
        }
    }

    let dto = ChecklistItemDto {
        id: item.id,
        task_id: item.task_id,
        description: item.description,
        completed: item.completed,
        completed_at: item.completed_at,
        created_at: item.created_at,
    };

    Ok(Json(dto))
}

/// Get security assessment
pub(crate) async fn get_security_assessment(
    State(state): State<AppState>,
    Path(id): Path<String>,
) -> ApiResult<Json<SecurityAssessmentDto>> {
    // Verify project exists
    let _ = state
        .db
        .get_project(&id)
        .await?
        .ok_or_else(|| ApiError::NotFound(format!("Project {} not found", id)))?;

    let assessment = state.db.get_latest_assessment(&id).await?.ok_or_else(|| {
        ApiError::NotFound(format!("No security assessment found for project {}", id))
    })?;

    let dto = SecurityAssessmentDto {
        id: assessment.id,
        profile: assessment.profile,
        risk_level: assessment.risk_level,
        passed_rules: assessment.passed_rules,
        failed_rules: assessment.failed_rules,
        critical_issues: assessment.critical_issues,
        assessment_date: assessment.assessment_date,
    };

    Ok(Json(dto))
}

/// Run security assessment
pub(crate) async fn run_security_assessment(
    State(state): State<AppState>,
    Path(id): Path<String>,
    Json(req): Json<RunSecurityAssessmentRequest>,
) -> ApiResult<(StatusCode, Json<SecurityAssessmentDto>)> {
    // Verify project exists
    let _ = state
        .db
        .get_project(&id)
        .await?
        .ok_or_else(|| ApiError::NotFound(format!("Project {} not found", id)))?;

    let now = Utc::now().to_rfc3339();
    let assessment = DbSecurityAssessment {
        id: Uuid::new_v4().to_string(),
        project_id: id,
        profile: req.profile.clone(),
        risk_level: "Medium".to_string(),
        passed_rules: 20,
        failed_rules: 3,
        critical_issues: 0,
        assessment_date: now,
    };

    let created = state.db.create_security_assessment(&assessment).await?;

    let dto = SecurityAssessmentDto {
        id: created.id,
        profile: created.profile,
        risk_level: created.risk_level,
        passed_rules: created.passed_rules,
        failed_rules: created.failed_rules,
        critical_issues: created.critical_issues,
        assessment_date: created.assessment_date,
    };

    Ok((StatusCode::CREATED, Json(dto)))
}

/// Get project status
pub(crate) async fn get_project_status(
    State(state): State<AppState>,
    Path(id): Path<String>,
) -> ApiResult<Json<ProjectStatusDto>> {
    let project = state
        .db
        .get_project(&id)
        .await?
        .ok_or_else(|| ApiError::NotFound(format!("Project {} not found", id)))?;

    let items = state.db.get_checklist_items(&id).await?;

    let completed_count = items.iter().filter(|i| i.completed).count();
    let completion_percentage = if items.is_empty() {
        0.0
    } else {
        (completed_count as f64 / items.len() as f64) * 100.0
    };

    let latest_assessment =
        state
            .db
            .get_latest_assessment(&id)
            .await?
            .map(|a| SecurityAssessmentDto {
                id: a.id,
                profile: a.profile,
                risk_level: a.risk_level,
                passed_rules: a.passed_rules,
                failed_rules: a.failed_rules,
                critical_issues: a.critical_issues,
                assessment_date: a.assessment_date,
            });

    Ok(Json(ProjectStatusDto {
        id: project.id,
        current_phase: project.current_phase,
        checklist_completion: completion_percentage,
        active_tools: 2,
        latest_assessment,
    }))
}

/// List tools
pub(crate) async fn list_tools(
    State(state): State<AppState>,
    Path(id): Path<String>,
) -> ApiResult<Json<Vec<ToolDto>>> {
    // Verify project exists
    let _ = state
        .db
        .get_project(&id)
        .await?
        .ok_or_else(|| ApiError::NotFound(format!("Project {} not found", id)))?;

    let tool_configs = state.db.list_tool_configs(&id).await?;

    let tools = tool_configs
        .into_iter()
        .map(|tc| ToolDto {
            name: tc.tool_name.clone(),
            enabled: tc.enabled,
            description: format!("Tool configuration for {}", tc.tool_name),
        })
        .collect();

    Ok(Json(tools))
}

/// Enable a tool
pub(crate) async fn enable_tool(
    State(state): State<AppState>,
    Path((project_id, name)): Path<(String, String)>,
) -> ApiResult<Json<ToolDto>> {
    // Verify project exists
    let _ = state
        .db
        .get_project(&project_id)
        .await?
        .ok_or_else(|| ApiError::NotFound(format!("Project {} not found", project_id)))?;

    let now = Utc::now().to_rfc3339();
    let mut tool = state
        .db
        .find_tool_config(&project_id, &name)
        .await?
        .unwrap_or_else(|| DbToolConfiguration {
            id: Uuid::new_v4().to_string(),
            project_id: project_id.clone(),
            tool_name: name.clone(),
            enabled: false,
            config_json: "{}".to_string(),
            phases: "all".to_string(),
            created_at: now.clone(),
            updated_at: now.clone(),
        });

    tool.enabled = true;
    tool.updated_at = Utc::now().to_rfc3339();

    if state
        .db
        .find_tool_config(&project_id, &name)
        .await?
        .is_some()
    {
        state.db.update_tool_config(&tool).await?;
    } else {
        state.db.create_tool_config(&tool).await?;
    }

    state
        .events
        .broadcast(crate::websocket::ProjectEvent::ToolEnabled {
            project_id: project_id.clone(),
            tool_name: name.clone(),
            timestamp: tool.updated_at.clone(),
        });

    Ok(Json(ToolDto {
        name: tool.tool_name,
        enabled: tool.enabled,
        description: format!("Tool configuration for {}", name),
    }))
}

/// Disable a tool
pub(crate) async fn disable_tool(
    State(state): State<AppState>,
    Path((project_id, name)): Path<(String, String)>,
) -> ApiResult<Json<ToolDto>> {
    // Verify project exists
    let _ = state
        .db
        .get_project(&project_id)
        .await?
        .ok_or_else(|| ApiError::NotFound(format!("Project {} not found", project_id)))?;

    let now = Utc::now().to_rfc3339();
    let mut tool = state
        .db
        .find_tool_config(&project_id, &name)
        .await?
        .unwrap_or_else(|| DbToolConfiguration {
            id: Uuid::new_v4().to_string(),
            project_id: project_id.clone(),
            tool_name: name.clone(),
            enabled: false,
            config_json: "{}".to_string(),
            phases: "all".to_string(),
            created_at: now.clone(),
            updated_at: now.clone(),
        });

    tool.enabled = false;
    tool.updated_at = Utc::now().to_rfc3339();

    if state
        .db
        .find_tool_config(&project_id, &name)
        .await?
        .is_some()
    {
        state.db.update_tool_config(&tool).await?;
    } else {
        state.db.create_tool_config(&tool).await?;
    }

    state
        .events
        .broadcast(crate::websocket::ProjectEvent::ToolDisabled {
            project_id: project_id.clone(),
            tool_name: name.clone(),
            timestamp: tool.updated_at.clone(),
        });

    Ok(Json(ToolDto {
        name: tool.tool_name,
        enabled: tool.enabled,
        description: format!("Tool configuration for {}", name),
    }))
}
