use reqwest::Client;
use serde::{Deserialize, Serialize};
use serde_json::Value;

use crate::error::{CliError, Result};

#[derive(Clone)]
pub struct VaporaClient {
    base_url: String,
    client: Client,
}

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

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

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

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

#[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,
}

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

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

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

impl VaporaClient {
    pub fn new(base_url: &str) -> Self {
        Self {
            base_url: base_url.trim_end_matches('/').to_string(),
            client: Client::new(),
        }
    }

    pub async fn start_workflow(&self, template: String, context: Value) -> Result<String> {
        let url = format!("{}/api/v1/workflow_orchestrator", self.base_url);

        let request = StartWorkflowRequest { template, context };

        let response = self
            .client
            .post(&url)
            .json(&request)
            .send()
            .await
            .map_err(|e| CliError::ApiError(e.to_string()))?;

        if !response.status().is_success() {
            let status = response.status();
            let error_text = response
                .text()
                .await
                .unwrap_or_else(|_| "Unknown error".to_string());
            return Err(CliError::ApiError(format!(
                "HTTP {}: {}",
                status, error_text
            )));
        }

        let result: StartWorkflowResponse = response
            .json()
            .await
            .map_err(|e| CliError::InvalidResponse(e.to_string()))?;

        Ok(result.workflow_id)
    }

    pub async fn list_workflows(&self) -> Result<Vec<WorkflowInstanceResponse>> {
        let url = format!("{}/api/v1/workflow_orchestrator", self.base_url);

        let response = self
            .client
            .get(&url)
            .send()
            .await
            .map_err(|e| CliError::ApiError(e.to_string()))?;

        if !response.status().is_success() {
            return Err(CliError::ApiError(format!("HTTP {}", response.status())));
        }

        let result: WorkflowListResponse = response
            .json()
            .await
            .map_err(|e| CliError::InvalidResponse(e.to_string()))?;

        Ok(result.workflows)
    }

    pub async fn get_workflow(&self, workflow_id: &str) -> Result<WorkflowInstanceResponse> {
        let url = format!(
            "{}/api/v1/workflow_orchestrator/{}",
            self.base_url, workflow_id
        );

        let response = self
            .client
            .get(&url)
            .send()
            .await
            .map_err(|e| CliError::ApiError(e.to_string()))?;

        if response.status().as_u16() == 404 {
            return Err(CliError::NotFound(workflow_id.to_string()));
        }

        if !response.status().is_success() {
            return Err(CliError::ApiError(format!("HTTP {}", response.status())));
        }

        let result: WorkflowInstanceResponse = response
            .json()
            .await
            .map_err(|e| CliError::InvalidResponse(e.to_string()))?;

        Ok(result)
    }

    pub async fn approve_stage(&self, workflow_id: &str, approver: String) -> Result<String> {
        let url = format!(
            "{}/api/v1/workflow_orchestrator/{}/approve",
            self.base_url, workflow_id
        );

        let request = ApproveStageRequest { approver };

        let response = self
            .client
            .post(&url)
            .json(&request)
            .send()
            .await
            .map_err(|e| CliError::ApiError(e.to_string()))?;

        if !response.status().is_success() {
            return Err(CliError::ApiError(format!("HTTP {}", response.status())));
        }

        let result: MessageResponse = response
            .json()
            .await
            .map_err(|e| CliError::InvalidResponse(e.to_string()))?;

        Ok(result.message)
    }

    pub async fn cancel_workflow(&self, workflow_id: &str, reason: String) -> Result<String> {
        let url = format!(
            "{}/api/v1/workflow_orchestrator/{}/cancel",
            self.base_url, workflow_id
        );

        let request = CancelWorkflowRequest { reason };

        let response = self
            .client
            .post(&url)
            .json(&request)
            .send()
            .await
            .map_err(|e| CliError::ApiError(e.to_string()))?;

        if !response.status().is_success() {
            return Err(CliError::ApiError(format!("HTTP {}", response.status())));
        }

        let result: MessageResponse = response
            .json()
            .await
            .map_err(|e| CliError::InvalidResponse(e.to_string()))?;

        Ok(result.message)
    }

    pub async fn list_templates(&self) -> Result<Vec<String>> {
        let url = format!("{}/api/v1/workflow_orchestrator/templates", self.base_url);

        let response = self
            .client
            .get(&url)
            .send()
            .await
            .map_err(|e| CliError::ApiError(e.to_string()))?;

        if !response.status().is_success() {
            return Err(CliError::ApiError(format!("HTTP {}", response.status())));
        }

        let result: TemplatesResponse = response
            .json()
            .await
            .map_err(|e| CliError::InvalidResponse(e.to_string()))?;

        Ok(result.templates)
    }
}
