//! HTTP API client for remote syntaxis-api communication
//!
//! This module provides a client for communicating with the syntaxis-api server.
//! The CLI can operate in two modes:
//! - Local mode: Direct database access (default)
//! - Remote mode: Via API endpoints when configured

use anyhow::{Context, Result};
use reqwest::Client;
use serde::{Deserialize, Serialize};
use std::time::Duration;

/// API client configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApiConfig {
    /// Enable remote API mode (false = local mode)
    pub enabled: bool,
    /// API server base URL
    pub base_url: String,
    /// JWT authentication token (optional)
    pub auth_token: Option<String>,
    /// Request timeout in seconds
    pub timeout_secs: u64,
}

impl Default for ApiConfig {
    fn default() -> Self {
        Self {
            enabled: false,
            base_url: "http://localhost:3000".to_string(),
            auth_token: None,
            timeout_secs: 30,
        }
    }
}

/// HTTP API client for syntaxis-api communication
#[derive(Debug, Clone)]
pub struct ApiClient {
    client: Client,
    config: ApiConfig,
}

impl ApiClient {
    /// Create a new API client with the given configuration
    pub fn new(config: ApiConfig) -> Result<Self> {
        let timeout = Duration::from_secs(config.timeout_secs);
        let client = Client::builder()
            .timeout(timeout)
            .build()
            .context("Failed to create HTTP client")?;

        Ok(Self { client, config })
    }

    /// Check if API mode is enabled
    #[allow(dead_code)]
    pub fn is_enabled(&self) -> bool {
        self.config.enabled
    }

    /// Get the API base URL
    #[allow(dead_code)]
    pub fn base_url(&self) -> &str {
        &self.config.base_url
    }

    /// Check server health
    pub async fn health_check(&self) -> Result<HealthStatus> {
        let url = format!("{}/api/health", self.config.base_url);
        let response = self
            .client
            .get(&url)
            .send()
            .await
            .context("Failed to connect to health endpoint")?;

        response
            .json::<HealthStatus>()
            .await
            .context("Failed to parse health response")
    }

    /// Get a project by ID
    #[allow(dead_code)]
    pub async fn get_project(&self, id: &str) -> Result<ProjectResponse> {
        let url = format!("{}/api/projects/{}", self.config.base_url, id);
        let response = self
            .client
            .get(&url)
            .send()
            .await
            .context("Failed to get project")?;

        response
            .json::<ProjectResponse>()
            .await
            .context("Failed to parse project response")
    }

    /// List all projects
    pub async fn list_projects(&self) -> Result<Vec<ProjectResponse>> {
        let url = format!("{}/api/projects", self.config.base_url);
        let response = self
            .client
            .get(&url)
            .send()
            .await
            .context("Failed to list projects")?;

        response
            .json::<Vec<ProjectResponse>>()
            .await
            .context("Failed to parse projects list")
    }

    /// Get current project phase
    pub async fn get_current_phase(&self, project_id: &str) -> Result<PhaseResponse> {
        let url = format!(
            "{}/api/projects/{}/phases",
            self.config.base_url, project_id
        );
        let response = self
            .client
            .get(&url)
            .send()
            .await
            .context("Failed to get current phase")?;

        response
            .json::<PhaseResponse>()
            .await
            .context("Failed to parse phase response")
    }

    /// Transition project to a new phase
    pub async fn transition_phase(
        &self,
        project_id: &str,
        target_phase: &str,
    ) -> Result<PhaseResponse> {
        let url = format!(
            "{}/api/projects/{}/phases/transition",
            self.config.base_url, project_id
        );

        #[derive(Serialize)]
        struct TransitionRequest {
            target_phase: String,
        }

        let response = self
            .client
            .post(&url)
            .json(&TransitionRequest {
                target_phase: target_phase.to_string(),
            })
            .send()
            .await
            .context("Failed to transition phase")?;

        response
            .json::<PhaseResponse>()
            .await
            .context("Failed to parse transition response")
    }

    /// Get project status
    pub async fn get_project_status(&self, project_id: &str) -> Result<StatusResponse> {
        let url = format!(
            "{}/api/projects/{}/status",
            self.config.base_url, project_id
        );
        let response = self
            .client
            .get(&url)
            .send()
            .await
            .context("Failed to get project status")?;

        response
            .json::<StatusResponse>()
            .await
            .context("Failed to parse status response")
    }

    /// Run security assessment
    pub async fn run_security_assessment(&self, project_id: &str) -> Result<SecurityResponse> {
        let url = format!(
            "{}/api/projects/{}/security/assess",
            self.config.base_url, project_id
        );
        let response = self
            .client
            .post(&url)
            .send()
            .await
            .context("Failed to run security assessment")?;

        response
            .json::<SecurityResponse>()
            .await
            .context("Failed to parse security response")
    }
}

// Response types from API

/// Health status response
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HealthStatus {
    pub status: String,
    pub service: String,
    pub version: String,
    pub uptime: UptimeInfo,
    pub database: DatabaseInfo,
    pub timestamp: String,
}

/// Uptime information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UptimeInfo {
    pub seconds: u64,
    pub formatted: String,
}

/// Database information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DatabaseInfo {
    pub connected: bool,
    #[serde(rename = "type")]
    pub db_type: String,
}

/// Project response from API
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProjectResponse {
    pub id: String,
    pub name: String,
    pub description: Option<String>,
    pub phase: String,
    pub status: String,
    pub created_at: String,
    pub updated_at: String,
}

/// Phase response from API
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PhaseResponse {
    pub project_id: String,
    pub phase: String,
    pub transitioned_at: String,
}

/// Status response from API
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StatusResponse {
    pub project_id: String,
    pub phase: String,
    pub completion_percentage: f64,
    pub checklist_items: ChecklistStats,
    pub tools_enabled: u32,
    pub tools_disabled: u32,
}

/// Checklist statistics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChecklistStats {
    pub total: u32,
    pub completed: u32,
    pub pending: u32,
}

/// Security assessment response
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SecurityResponse {
    pub project_id: String,
    pub severity: String,
    pub assessment_date: String,
    pub findings: Vec<SecurityFinding>,
}

/// Security finding
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SecurityFinding {
    pub id: String,
    pub title: String,
    pub severity: String,
    pub description: String,
}

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

    #[test]
    fn test_api_config_default() {
        let config = ApiConfig::default();
        assert!(!config.enabled);
        assert_eq!(config.base_url, "http://localhost:3000");
        assert_eq!(config.timeout_secs, 30);
    }

    #[test]
    fn test_api_client_creation() {
        let config = ApiConfig::default();
        let client = ApiClient::new(config);
        assert!(client.is_ok());
    }

    #[test]
    fn test_api_client_is_enabled() {
        let mut config = ApiConfig::default();
        assert!(!config.enabled);

        config.enabled = true;
        let client = ApiClient::new(config).unwrap();
        assert!(client.is_enabled());
    }

    #[test]
    fn test_api_client_base_url() {
        let config = ApiConfig {
            base_url: "https://api.example.com".to_string(),
            ..Default::default()
        };
        let client = ApiClient::new(config).unwrap();
        assert_eq!(client.base_url(), "https://api.example.com");
    }

    #[test]
    fn test_health_status_serialization() {
        let json = r#"{
            "status": "healthy",
            "service": "syntaxis-api",
            "version": "1.0.0",
            "uptime": { "seconds": 3600, "formatted": "1h 0m" },
            "database": { "connected": true, "type": "sqlite" },
            "timestamp": "2025-11-13T00:00:00Z"
        }"#;

        let status: HealthStatus = serde_json::from_str(json).unwrap();
        assert_eq!(status.status, "healthy");
        assert_eq!(status.version, "1.0.0");
        assert!(status.database.connected);
    }

    #[test]
    fn test_project_response_serialization() {
        let json = r#"{
            "id": "proj-1",
            "name": "Test Project",
            "description": "A test project",
            "phase": "development",
            "status": "active",
            "created_at": "2025-01-01T00:00:00Z",
            "updated_at": "2025-11-13T00:00:00Z"
        }"#;

        let project: ProjectResponse = serde_json::from_str(json).unwrap();
        assert_eq!(project.id, "proj-1");
        assert_eq!(project.name, "Test Project");
        assert_eq!(project.phase, "development");
    }

    #[test]
    fn test_status_response_serialization() {
        let json = r#"{
            "project_id": "proj-1",
            "phase": "development",
            "completion_percentage": 75.5,
            "checklist_items": { "total": 10, "completed": 7, "pending": 3 },
            "tools_enabled": 5,
            "tools_disabled": 2
        }"#;

        let status: StatusResponse = serde_json::from_str(json).unwrap();
        assert_eq!(status.project_id, "proj-1");
        assert_eq!(status.completion_percentage, 75.5);
        assert_eq!(status.checklist_items.completed, 7);
    }
}
