//! Client for interacting with Syntaxis from ecosystem integrations

use crate::ecosystem::EcosystemIntegrationError;
use serde::{Deserialize, Serialize};
use std::time::Duration;

/// Client for communicating with Syntaxis
#[derive(Clone)]
pub struct EcosystemClient {
    base_url: String,
    token: Option<String>,
    timeout: Duration,
}

impl EcosystemClient {
    /// Create a new client
    pub fn new(base_url: String) -> Self {
        Self {
            base_url,
            token: None,
            timeout: Duration::from_secs(30),
        }
    }

    /// Set authentication token
    pub fn with_token(mut self, token: String) -> Self {
        self.token = Some(token);
        self
    }

    /// Set request timeout
    pub fn with_timeout(mut self, timeout: Duration) -> Self {
        self.timeout = timeout;
        self
    }

    /// Create a new task in Syntaxis
    pub async fn create_task(
        &self,
        task: CreateTaskRequest,
    ) -> Result<TaskResponse, EcosystemIntegrationError> {
        let client = reqwest::Client::new();
        let mut request = client
            .post(format!("{}/api/tasks", self.base_url))
            .json(&task);

        if let Some(token) = &self.token {
            request = request.bearer_auth(token);
        }

        request
            .timeout(self.timeout)
            .send()
            .await
            .map_err(|e| EcosystemIntegrationError::IntegrationError(e.to_string()))?
            .json()
            .await
            .map_err(|e| EcosystemIntegrationError::IntegrationError(e.to_string()))
    }

    /// Update a task
    pub async fn update_task(
        &self,
        task_id: &str,
        update: UpdateTaskRequest,
    ) -> Result<TaskResponse, EcosystemIntegrationError> {
        let client = reqwest::Client::new();
        let mut request = client
            .patch(format!("{}/api/tasks/{}", self.base_url, task_id))
            .json(&update);

        if let Some(token) = &self.token {
            request = request.bearer_auth(token);
        }

        request
            .timeout(self.timeout)
            .send()
            .await
            .map_err(|e| EcosystemIntegrationError::IntegrationError(e.to_string()))?
            .json()
            .await
            .map_err(|e| EcosystemIntegrationError::IntegrationError(e.to_string()))
    }

    /// Get a task
    pub async fn get_task(&self, task_id: &str) -> Result<TaskResponse, EcosystemIntegrationError> {
        let client = reqwest::Client::new();
        let mut request = client.get(format!("{}/api/tasks/{}", self.base_url, task_id));

        if let Some(token) = &self.token {
            request = request.bearer_auth(token);
        }

        request
            .timeout(self.timeout)
            .send()
            .await
            .map_err(|e| EcosystemIntegrationError::IntegrationError(e.to_string()))?
            .json()
            .await
            .map_err(|e| EcosystemIntegrationError::IntegrationError(e.to_string()))
    }

    /// Get all tasks for a phase
    pub async fn get_phase_tasks(
        &self,
        phase: &str,
    ) -> Result<Vec<TaskResponse>, EcosystemIntegrationError> {
        let client = reqwest::Client::new();
        let mut request = client.get(format!("{}/api/phases/{}/tasks", self.base_url, phase));

        if let Some(token) = &self.token {
            request = request.bearer_auth(token);
        }

        request
            .timeout(self.timeout)
            .send()
            .await
            .map_err(|e| EcosystemIntegrationError::IntegrationError(e.to_string()))?
            .json()
            .await
            .map_err(|e| EcosystemIntegrationError::IntegrationError(e.to_string()))
    }

    /// Get current phase
    pub async fn get_current_phase(&self) -> Result<String, EcosystemIntegrationError> {
        let client = reqwest::Client::new();
        let mut request = client.get(format!("{}/api/project/phase", self.base_url));

        if let Some(token) = &self.token {
            request = request.bearer_auth(token);
        }

        #[derive(Deserialize)]
        struct PhaseResponse {
            phase: String,
        }

        let response: PhaseResponse = request
            .timeout(self.timeout)
            .send()
            .await
            .map_err(|e| EcosystemIntegrationError::IntegrationError(e.to_string()))?
            .json()
            .await
            .map_err(|e| EcosystemIntegrationError::IntegrationError(e.to_string()))?;

        Ok(response.phase)
    }

    /// Complete a task
    pub async fn complete_task(
        &self,
        task_id: &str,
    ) -> Result<TaskResponse, EcosystemIntegrationError> {
        self.update_task(
            task_id,
            UpdateTaskRequest {
                state: Some("done".to_string()),
                ..Default::default()
            },
        )
        .await
    }

    /// Block a task
    pub async fn block_task(
        &self,
        task_id: &str,
        reason: &str,
    ) -> Result<TaskResponse, EcosystemIntegrationError> {
        self.update_task(
            task_id,
            UpdateTaskRequest {
                state: Some("blocked".to_string()),
                note: Some(reason.to_string()),
                ..Default::default()
            },
        )
        .await
    }
}

/// Request to create a task
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateTaskRequest {
    pub description: String,
    pub task_name: Option<String>,
    pub task_type: String,
    pub task_priority: Option<String>,
    pub phase: Option<String>,
    pub task_deps: Option<Vec<String>>,
    pub note: Option<String>,
}

/// Request to update a task
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct UpdateTaskRequest {
    pub state: Option<String>,
    pub note: Option<String>,
    pub completed: Option<bool>,
    pub task_priority: Option<String>,
}

/// Response from Syntaxis API
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TaskResponse {
    pub id: String,
    pub description: String,
    pub task_name: Option<String>,
    pub task_type: String,
    pub task_state: String,
    pub completed: bool,
    pub phase: String,
}

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

    #[test]
    fn test_client_creation() {
        let client = EcosystemClient::new("http://localhost:3030".to_string());
        assert_eq!(client.base_url, "http://localhost:3030");
        assert!(client.token.is_none());
    }

    #[test]
    fn test_client_with_token() {
        let client = EcosystemClient::new("http://localhost:3030".to_string())
            .with_token("test-token".to_string());

        assert_eq!(client.token, Some("test-token".to_string()));
    }

    #[test]
    fn test_client_with_timeout() {
        let timeout = Duration::from_secs(60);
        let client =
            EcosystemClient::new("http://localhost:3030".to_string()).with_timeout(timeout);

        assert_eq!(client.timeout, timeout);
    }

    #[test]
    fn test_create_task_request() {
        let req = CreateTaskRequest {
            description: "Test task".to_string(),
            task_name: Some("test".to_string()),
            task_type: "feature".to_string(),
            task_priority: Some("high".to_string()),
            phase: Some("devel".to_string()),
            task_deps: None,
            note: None,
        };

        let json = serde_json::to_string(&req).unwrap();
        let deserialized: CreateTaskRequest = serde_json::from_str(&json).unwrap();
        assert_eq!(req.description, deserialized.description);
    }
}
