//! Deployment Plan Management Handlers

use std::sync::Arc;

use axum::{
    extract::{Path, Query, State},
    http::StatusCode,
    Json,
};
use ontoref_derive::onto_api;
use serde::{Deserialize, Serialize};

// Re-export service types to avoid duplication
pub use crate::services::iac_deployment::{DeploymentPlan, DeploymentTask, TaskStatus};
use crate::{AppState, Result};

/// Paginated response
#[derive(Serialize)]
pub struct PagedResponse<T> {
    pub items: Vec<T>,
    pub total: u32,
    pub page: u32,
    pub page_size: u32,
}

/// Query parameters
#[derive(Deserialize)]
pub struct DeploymentQuery {
    pub page: Option<u32>,
    pub page_size: Option<u32>,
    pub status: Option<String>,
    pub organization: Option<String>,
}

/// List deployment plans
#[onto_api(
    method = "GET",
    path = "/deployments",
    description = "List all deployment plans",
    auth = "bearer",
    actors = "developer, agent",
    tags = "deployments",
    feature = ""
)]
pub async fn list_deployments(
    State(state): State<Arc<AppState>>,
    Query(params): Query<DeploymentQuery>,
) -> Result<Json<PagedResponse<DeploymentPlan>>> {
    let page = params.page.unwrap_or(1);
    let page_size = params.page_size.unwrap_or(20);

    let (items, total) = state
        .iac_deployment_service
        .list_deployments(page, page_size, params.status, params.organization)
        .await?;

    Ok(Json(PagedResponse {
        items,
        total,
        page,
        page_size,
    }))
}

/// Get deployment plan
#[onto_api(
    method = "GET",
    path = "/deployments/{id}",
    description = "Get a deployment plan by ID",
    auth = "bearer",
    actors = "developer, agent",
    tags = "deployments",
    feature = ""
)]
pub async fn get_deployment(
    State(state): State<Arc<AppState>>,
    Path(id): Path<String>,
) -> Result<Json<DeploymentPlan>> {
    state
        .iac_deployment_service
        .get_deployment(&id)
        .await
        .map(Json)
}

/// Create deployment plan
#[onto_api(
    method = "POST",
    path = "/deployments",
    description = "Create a new deployment plan",
    auth = "bearer",
    actors = "developer, agent",
    tags = "deployments",
    feature = ""
)]
pub async fn create_deployment(
    State(state): State<Arc<AppState>>,
    Json(plan): Json<DeploymentPlan>,
) -> Result<(StatusCode, Json<DeploymentPlan>)> {
    let created = state.iac_deployment_service.create_deployment(plan).await?;
    Ok((StatusCode::CREATED, Json(created)))
}

/// Update deployment plan
#[onto_api(
    method = "PUT",
    path = "/deployments/{id}",
    description = "Update a deployment plan",
    auth = "bearer",
    actors = "developer, agent",
    tags = "deployments",
    feature = ""
)]
pub async fn update_deployment(
    State(state): State<Arc<AppState>>,
    Path(id): Path<String>,
    Json(plan): Json<DeploymentPlan>,
) -> Result<Json<DeploymentPlan>> {
    let updated = state
        .iac_deployment_service
        .update_deployment(&id, plan)
        .await?;
    Ok(Json(updated))
}

/// Submission result
#[derive(Serialize)]
pub struct SubmissionResult {
    pub workflow_id: String,
    pub status: String,
    pub message: String,
}

/// Submit deployment plan to orchestrator
#[onto_api(
    method = "POST",
    path = "/deployments/{id}/submit",
    description = "Submit a deployment plan to the orchestrator",
    auth = "bearer",
    actors = "developer, agent",
    tags = "deployments",
    feature = ""
)]
pub async fn submit_deployment(
    State(state): State<Arc<AppState>>,
    Path(id): Path<String>,
) -> Result<Json<SubmissionResult>> {
    // Verify plan exists
    let plan = state.iac_deployment_service.get_deployment(&id).await?;

    // Create execution record
    let execution = state.iac_deployment_service.create_execution(&id).await?;

    // Submit to orchestrator
    let orchestrator = crate::services::orchestrator::OrchestratorService::new(None);
    let response = orchestrator.submit_workflow(&plan).await?;

    // Update execution with workflow_id
    state
        .iac_deployment_service
        .update_execution_status(
            &execution.id,
            "submitted",
            0,
            0,
            plan.tasks.len() as u32,
            Some(response.workflow_id.clone()),
        )
        .await?;

    // Publish WebSocket event for deployment submission
    crate::handlers::deployment_events::publish_deployment_event(
        state.websocket_manager.clone(),
        crate::handlers::deployment_events::DeploymentEventType::Submitted,
        id.clone(),
        Some(response.workflow_id.clone()),
        0,
        "Deployment submitted to orchestrator".to_string(),
    );

    // Start background task to poll orchestrator for status updates
    let ws_manager = state.websocket_manager.clone();
    let deployment_service = state.iac_deployment_service.clone();
    let orchestrator_service = Arc::new(crate::services::orchestrator::OrchestratorService::new(
        None,
    ));
    let plan_id = id.clone();
    let workflow_id = response.workflow_id.clone();

    crate::handlers::deployment_events::sync_deployment_status(
        ws_manager,
        deployment_service,
        orchestrator_service,
        plan_id,
        workflow_id,
    )
    .await;

    Ok(Json(SubmissionResult {
        workflow_id: response.workflow_id,
        status: response.status,
        message: "Deployment submitted to orchestrator".to_string(),
    }))
}

/// Deployment execution status
#[derive(Serialize)]
pub struct DeploymentStatus {
    pub plan_id: String,
    pub workflow_id: Option<String>,
    pub status: String,
    pub progress_percentage: u32,
    pub completed_tasks: u32,
    pub total_tasks: u32,
    pub started_at: Option<String>,
    pub completed_at: Option<String>,
}

/// Get deployment execution status
#[onto_api(
    method = "GET",
    path = "/deployments/{id}/status",
    description = "Get deployment execution status",
    auth = "bearer",
    actors = "developer, agent",
    tags = "deployments",
    feature = ""
)]
pub async fn get_deployment_status(
    State(state): State<Arc<AppState>>,
    Path(id): Path<String>,
) -> Result<Json<DeploymentStatus>> {
    // Get the latest execution for this deployment plan
    let execution = state.iac_deployment_service.get_execution(&id).await?;

    Ok(Json(DeploymentStatus {
        plan_id: execution.plan_id,
        workflow_id: execution.workflow_id,
        status: execution.status,
        progress_percentage: execution.progress_percentage,
        completed_tasks: execution.completed_tasks,
        total_tasks: execution.total_tasks,
        started_at: execution.started_at,
        completed_at: execution.completed_at,
    }))
}

#[cfg(test)]
mod tests {
    use chrono::Utc;
    use uuid::Uuid;

    use super::*;

    #[test]
    fn test_deployment_plan_creation() {
        let plan = DeploymentPlan {
            id: Uuid::new_v4().to_string(),
            name: "Test Deployment".to_string(),
            description: "Test deployment plan".to_string(),
            organization: "default".to_string(),
            tasks: vec![],
            created_at: Utc::now().to_rfc3339(),
            scheduled_at: None,
        };

        assert_eq!(plan.name, "Test Deployment");
        assert_eq!(plan.tasks.len(), 0);
    }

    #[test]
    fn test_task_status_default() {
        assert_eq!(TaskStatus::default(), TaskStatus::Pending);
    }
}
