// Workflow Orchestrator API endpoints
// Provides REST API for multi-stage workflow orchestration

use axum::{
    extract::{Path, State},
    http::StatusCode,
    routing::{get, post},
    Json, Router,
};
use ontoref_derive::onto_api;
use serde::{Deserialize, Serialize};
use tracing::{error, info};
use vapora_shared::VaporaError;
use vapora_workflow_engine::{WorkflowInstance, WorkflowStatus};

use crate::api::error::ApiError;
use crate::api::state::AppState;

#[allow(dead_code)]
#[derive(Debug, Serialize, Deserialize)]
pub struct StartWorkflowRequest {
    pub template: String,
    #[serde(default)]
    pub context: serde_json::Value,
}

#[allow(dead_code)]
#[derive(Debug, Serialize, Deserialize)]
pub struct StartWorkflowResponse {
    pub workflow_id: String,
}

#[allow(dead_code)]
#[derive(Debug, Serialize, Deserialize)]
pub struct ApproveStageRequest {
    pub approver: String,
}

#[allow(dead_code)]
#[derive(Debug, Serialize, Deserialize)]
pub struct CancelWorkflowRequest {
    pub reason: String,
}

#[allow(dead_code)]
#[derive(Debug, Serialize, Deserialize)]
pub struct WorkflowInstanceResponse {
    pub id: String,
    pub template_name: String,
    pub status: String,
    pub current_stage: usize,
    pub total_stages: usize,
    pub created_at: String,
    pub updated_at: String,
}

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

#[allow(dead_code)]
#[derive(Debug, Serialize, Deserialize)]
pub struct TemplatesResponse {
    pub templates: Vec<String>,
}

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

impl From<WorkflowInstance> for WorkflowInstanceResponse {
    fn from(instance: WorkflowInstance) -> Self {
        Self {
            id: instance.id,
            template_name: instance.template_name,
            status: status_to_string(&instance.status),
            current_stage: instance.current_stage_idx,
            total_stages: instance.stages.len(),
            created_at: instance.created_at.to_rfc3339(),
            updated_at: instance.updated_at.to_rfc3339(),
        }
    }
}

#[allow(dead_code)]
fn status_to_string(status: &WorkflowStatus) -> String {
    match status {
        WorkflowStatus::Running => "running".to_string(),
        WorkflowStatus::WaitingApproval(idx) => format!("waiting_approval:{}", idx),
        WorkflowStatus::Completed => "completed".to_string(),
        WorkflowStatus::Failed(err) => format!("failed:{}", err),
        WorkflowStatus::Cancelled => "cancelled".to_string(),
    }
}

#[allow(dead_code)]
pub fn orchestrator_routes() -> Router<AppState> {
    Router::new()
        .route("/", post(start_workflow))
        .route("/", get(list_workflows))
        .route("/:id", get(get_workflow))
        .route("/:id/approve", post(approve_stage))
        .route("/:id/cancel", post(cancel_workflow))
        .route("/templates", get(list_templates))
}

#[onto_api(
    method = "POST",
    path = "/api/v1/workflow_orchestrator",
    description = "Starts a new multi-stage orchestrated workflow from a named template.",
    auth = "bearer",
    actors = "developer, agent, admin",
    tags = "workflow_orchestrator, write"
)]
#[allow(dead_code)]
async fn start_workflow(
    State(state): State<AppState>,
    Json(req): Json<StartWorkflowRequest>,
) -> Result<(StatusCode, Json<StartWorkflowResponse>), ApiError> {
    let orchestrator = state.workflow_orchestrator.as_ref().ok_or_else(|| {
        ApiError(VaporaError::InternalError(
            "Workflow orchestrator not available".to_string(),
        ))
    })?;

    let workflow_id = orchestrator
        .start_workflow(&req.template, req.context)
        .await
        .map_err(|e| {
            error!("Failed to start workflow: {}", e);
            ApiError(VaporaError::InternalError(e.to_string()))
        })?;

    info!(
        workflow_id = %workflow_id,
        template = %req.template,
        "Workflow started via API"
    );

    Ok((
        StatusCode::CREATED,
        Json(StartWorkflowResponse { workflow_id }),
    ))
}

#[onto_api(
    method = "GET",
    path = "/api/v1/workflow_orchestrator",
    description = "Returns all active and completed workflow instances managed by the \
                   orchestrator.",
    auth = "bearer",
    actors = "developer, agent, ci, admin",
    tags = "workflow_orchestrator, read"
)]
#[allow(dead_code)]
async fn list_workflows(
    State(state): State<AppState>,
) -> Result<Json<WorkflowListResponse>, ApiError> {
    let orchestrator = state.workflow_orchestrator.as_ref().ok_or_else(|| {
        ApiError(VaporaError::InternalError(
            "Workflow orchestrator not available".to_string(),
        ))
    })?;

    let workflows = orchestrator
        .list_workflows()
        .into_iter()
        .map(WorkflowInstanceResponse::from)
        .collect();

    Ok(Json(WorkflowListResponse { workflows }))
}

#[onto_api(
    method = "GET",
    path = "/api/v1/workflow_orchestrator/:id",
    description = "Returns the current state and stage of a specific orchestrated workflow \
                   instance.",
    auth = "bearer",
    actors = "developer, agent, ci, admin",
    params = "id:string:required:Workflow instance identifier",
    tags = "workflow_orchestrator, read"
)]
#[allow(dead_code)]
async fn get_workflow(
    State(state): State<AppState>,
    Path(id): Path<String>,
) -> Result<Json<WorkflowInstanceResponse>, ApiError> {
    let orchestrator = state.workflow_orchestrator.as_ref().ok_or_else(|| {
        ApiError(VaporaError::InternalError(
            "Workflow orchestrator not available".to_string(),
        ))
    })?;

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

    Ok(Json(WorkflowInstanceResponse::from(workflow)))
}

#[onto_api(
    method = "POST",
    path = "/api/v1/workflow_orchestrator/:id/approve",
    description = "Approves the current pending stage of an orchestrated workflow, unblocking \
                   execution.",
    auth = "bearer",
    actors = "developer, admin",
    params = "id:string:required:Workflow instance identifier",
    tags = "workflow_orchestrator, write"
)]
#[allow(dead_code)]
async fn approve_stage(
    State(state): State<AppState>,
    Path(id): Path<String>,
    Json(req): Json<ApproveStageRequest>,
) -> Result<Json<MessageResponse>, ApiError> {
    let orchestrator = state.workflow_orchestrator.as_ref().ok_or_else(|| {
        ApiError(VaporaError::InternalError(
            "Workflow orchestrator not available".to_string(),
        ))
    })?;

    orchestrator
        .approve_stage(&id, &req.approver)
        .await
        .map_err(|e| {
            error!("Failed to approve workflow {}: {}", id, e);
            ApiError(VaporaError::InternalError(e.to_string()))
        })?;

    info!(
        workflow_id = %id,
        approver = %req.approver,
        "Workflow stage approved via API"
    );

    Ok(Json(MessageResponse {
        success: true,
        message: format!("Workflow {} stage approved", id),
    }))
}

#[onto_api(
    method = "POST",
    path = "/api/v1/workflow_orchestrator/:id/cancel",
    description = "Cancels a running or approval-pending orchestrated workflow with a reason.",
    auth = "bearer",
    actors = "developer, admin",
    params = "id:string:required:Workflow instance identifier",
    tags = "workflow_orchestrator, write"
)]
#[allow(dead_code)]
async fn cancel_workflow(
    State(state): State<AppState>,
    Path(id): Path<String>,
    Json(req): Json<CancelWorkflowRequest>,
) -> Result<Json<MessageResponse>, ApiError> {
    let orchestrator = state.workflow_orchestrator.as_ref().ok_or_else(|| {
        ApiError(VaporaError::InternalError(
            "Workflow orchestrator not available".to_string(),
        ))
    })?;

    orchestrator
        .cancel_workflow(&id, req.reason.clone())
        .await
        .map_err(|e| {
            error!("Failed to cancel workflow {}: {}", id, e);
            ApiError(VaporaError::InternalError(e.to_string()))
        })?;

    info!(
        workflow_id = %id,
        reason = %req.reason,
        "Workflow cancelled via API"
    );

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

#[onto_api(
    method = "GET",
    path = "/api/v1/workflow_orchestrator/templates",
    description = "Returns the names of all registered workflow templates available for \
                   instantiation.",
    auth = "bearer",
    actors = "developer, agent, ci, admin",
    tags = "workflow_orchestrator, read"
)]
#[allow(dead_code)]
async fn list_templates(
    State(state): State<AppState>,
) -> Result<Json<TemplatesResponse>, ApiError> {
    let orchestrator = state.workflow_orchestrator.as_ref().ok_or_else(|| {
        ApiError(VaporaError::InternalError(
            "Workflow orchestrator not available".to_string(),
        ))
    })?;

    let templates = orchestrator.list_templates();

    Ok(Json(TemplatesResponse { templates }))
}

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

    #[test]
    fn test_start_workflow_request_serialization() {
        let req = StartWorkflowRequest {
            template: "feature_development".to_string(),
            context: serde_json::json!({
                "task": "Add authentication"
            }),
        };

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

        assert_eq!(deserialized.template, "feature_development");
    }

    #[test]
    fn test_workflow_instance_response_conversion() {
        use vapora_workflow_engine::config::{StageConfig, WorkflowConfig};

        let config = WorkflowConfig {
            name: "test".to_string(),
            trigger: "manual".to_string(),
            stages: vec![StageConfig {
                name: "stage1".to_string(),
                agents: vec!["agent1".to_string()],
                parallel: false,
                max_parallel: None,
                approval_required: false,
                compensation_agents: None,
            }],
            schedule: None,
            notifications: Default::default(),
        };

        let instance = WorkflowInstance::new(&config, serde_json::json!({}));
        let response = WorkflowInstanceResponse::from(instance);

        assert_eq!(response.template_name, "test");
        assert_eq!(response.status, "running");
        assert_eq!(response.total_stages, 1);
    }
}
