// vapora-backend: Workflow API endpoints
// Phase 3: REST API for workflow management

use crate::api::error::ApiError;
use crate::api::state::AppState;
use crate::audit::AuditEntry;
use crate::workflow::{parser::WorkflowParser, Workflow};
use axum::{
    extract::{Path, State},
    http::StatusCode,
    routing::{get, post},
    Json, Router,
};
use serde::{Deserialize, Serialize};
use tracing::error;
use vapora_shared::VaporaError;

#[derive(Debug, Serialize, Deserialize)]
pub struct CreateWorkflowRequest {
    pub yaml: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct WorkflowResponse {
    pub workflow: Workflow,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct WorkflowListResponse {
    pub workflows: Vec<Workflow>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct MessageResponse {
    pub success: bool,
    pub message: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct AuditResponse {
    pub entries: Vec<AuditEntry>,
}

/// Create workflow router
pub fn workflow_routes() -> Router<AppState> {
    Router::new()
        .route("/", get(list_workflows).post(create_workflow))
        .route("/:id", get(get_workflow))
        .route("/:id/execute", post(execute_workflow))
        .route("/:id/rollback", post(rollback_workflow))
        .route("/:id/audit", get(get_workflow_audit))
}

/// List all workflows
async fn list_workflows(
    State(state): State<AppState>,
) -> Result<Json<WorkflowListResponse>, ApiError> {
    let workflow_service = state
        .workflow_service
        .as_ref()
        .ok_or_else(|| ApiError(VaporaError::InternalError("Workflow service not available".to_string())))?;

    let workflows = workflow_service.list_workflows().await;

    Ok(Json(WorkflowListResponse { workflows }))
}

/// Create new workflow from YAML
async fn create_workflow(
    State(state): State<AppState>,
    Json(req): Json<CreateWorkflowRequest>,
) -> Result<(StatusCode, Json<WorkflowResponse>), ApiError> {
    let workflow_service = state
        .workflow_service
        .as_ref()
        .ok_or_else(|| ApiError(VaporaError::InternalError("Workflow service not available".to_string())))?;

    // Parse YAML
    let workflow = WorkflowParser::parse_string(&req.yaml)
        .map_err(|e| ApiError(VaporaError::InvalidInput(format!("Invalid workflow YAML: {}", e))))?;

    // Create workflow
    let created = workflow_service
        .create_workflow(workflow)
        .await
        .map_err(|e| {
            error!("Failed to create workflow: {}", e);
            ApiError(VaporaError::InternalError(e.to_string()))
        })?;

    Ok((
        StatusCode::CREATED,
        Json(WorkflowResponse { workflow: created }),
    ))
}

/// Get workflow by ID
async fn get_workflow(
    State(state): State<AppState>,
    Path(id): Path<String>,
) -> Result<Json<WorkflowResponse>, ApiError> {
    let workflow_service = state
        .workflow_service
        .as_ref()
        .ok_or_else(|| ApiError(VaporaError::InternalError("Workflow service not available".to_string())))?;

    let workflow = workflow_service.get_workflow(&id).await.map_err(|e| {
        error!("Failed to get workflow {}: {}", id, e);
        ApiError(VaporaError::NotFound(format!("Workflow {} not found", id)))
    })?;

    Ok(Json(WorkflowResponse { workflow }))
}

/// Execute workflow
async fn execute_workflow(
    State(state): State<AppState>,
    Path(id): Path<String>,
) -> Result<Json<WorkflowResponse>, ApiError> {
    let workflow_service = state
        .workflow_service
        .as_ref()
        .ok_or_else(|| ApiError(VaporaError::InternalError("Workflow service not available".to_string())))?;

    let workflow = workflow_service.execute_workflow(&id).await.map_err(|e| {
        error!("Failed to execute workflow {}: {}", id, e);
        ApiError(VaporaError::InternalError(e.to_string()))
    })?;

    Ok(Json(WorkflowResponse { workflow }))
}

/// Rollback failed workflow
async fn rollback_workflow(
    State(state): State<AppState>,
    Path(id): Path<String>,
) -> Result<Json<MessageResponse>, ApiError> {
    let workflow_service = state
        .workflow_service
        .as_ref()
        .ok_or_else(|| ApiError(VaporaError::InternalError("Workflow service not available".to_string())))?;

    workflow_service.rollback_workflow(&id).await.map_err(|e| {
        error!("Failed to rollback workflow {}: {}", id, e);
        ApiError(VaporaError::InternalError(e.to_string()))
    })?;

    Ok(Json(MessageResponse {
        success: true,
        message: format!("Workflow {} rolled back successfully", id),
    }))
}

/// Get audit trail for workflow
async fn get_workflow_audit(
    State(state): State<AppState>,
    Path(id): Path<String>,
) -> Result<Json<AuditResponse>, ApiError> {
    let workflow_service = state
        .workflow_service
        .as_ref()
        .ok_or_else(|| ApiError(VaporaError::InternalError("Workflow service not available".to_string())))?;

    let entries = workflow_service.get_audit_trail(&id).await;

    Ok(Json(AuditResponse { entries }))
}

// Note: WebSocket endpoint would be added separately to main router
// pub async fn workflow_websocket_handler(
//     ws: WebSocketUpgrade,
//     State(state): State<AppState>,
// ) -> axum::response::Response {
//     if let Some(workflow_service) = state.workflow_service.as_ref() {
//         let broadcaster = workflow_service.broadcaster();
//         workflow_ws_handler(ws, broadcaster).await.into_response()
//     } else {
//         (
//             StatusCode::SERVICE_UNAVAILABLE,
//             Json(serde_json::json!({"error": "Workflow service not available"})),
//         )
//             .into_response()
//     }
// }

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_create_workflow_request_serialization() {
        let req = CreateWorkflowRequest {
            yaml: "workflow:\n  id: test".to_string(),
        };

        let json = serde_json::to_string(&req).unwrap();
        let deserialized: CreateWorkflowRequest = serde_json::from_str(&json).unwrap();

        assert_eq!(deserialized.yaml, req.yaml);
    }

    #[test]
    fn test_message_response() {
        let resp = MessageResponse {
            success: true,
            message: "Test message".to_string(),
        };

        let json = serde_json::to_string(&resp).unwrap();
        assert!(json.contains("success"));
        assert!(json.contains("Test message"));
    }
}
