//! Document lifecycle integration
//!
//! Connects to doc-syntaxis for document management and classification.

use anyhow::anyhow;
use serde::{Deserialize, Serialize};
use std::time::Duration;

/// Document metadata
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Document {
    /// Document ID
    pub id: String,
    /// Document path
    pub path: String,
    /// Document title
    pub title: String,
    /// Document content
    pub content: String,
    /// Document classification (API, Guide, Example, etc.)
    pub classification: String,
    /// Document tags
    #[serde(default)]
    pub tags: Vec<String>,
    /// Last modified timestamp
    pub modified_at: String,
}

/// Document search results
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DocumentSearchResult {
    /// Search query
    pub query: String,
    /// Results
    pub documents: Vec<Document>,
    /// Total count
    pub total: usize,
    /// Search relevance score (0-100)
    pub relevance: u32,
}

/// GraphQL query for document search
#[derive(Debug, Clone, Serialize)]
struct GraphQLQuery {
    query: String,
    variables: serde_json::Value,
}

/// GraphQL response wrapper
#[allow(dead_code)]
#[derive(Debug, Deserialize)]
struct GraphQLResponse<T> {
    data: Option<T>,
    errors: Option<Vec<GraphQLError>>,
}

/// GraphQL error
#[allow(dead_code)]
#[derive(Debug, Deserialize)]
struct GraphQLError {
    message: String,
}

/// Document lifecycle client with real HTTP integration
#[derive(Debug, Clone)]
pub struct DocLifecycleClient {
    /// API URL for document lifecycle service
    api_url: String,
    /// HTTP client
    client: reqwest::Client,
}

impl DocLifecycleClient {
    /// Create a new doc-lifecycle client with HTTP connection
    pub fn new(api_url: impl Into<String>) -> Self {
        let client = reqwest::Client::builder()
            .timeout(Duration::from_secs(30))
            .build()
            .unwrap_or_else(|_| reqwest::Client::new());

        Self {
            api_url: api_url.into(),
            client,
        }
    }

    /// Search documents using semantic search via GraphQL
    pub async fn search_documents(
        &self,
        query: &str,
        classification: Option<&str>,
    ) -> anyhow::Result<DocumentSearchResult> {
        let graphql_query = r#"
            query SearchDocuments($query: String!, $classification: String) {
                searchDocuments(query: $query, classification: $classification) {
                    documents {
                        id
                        path
                        title
                        classification
                        tags
                        modifiedAt
                    }
                    total
                    relevance
                }
            }
        "#;

        let mut variables = serde_json::json!({ "query": query });
        if let Some(c) = classification {
            variables["classification"] = serde_json::json!(c);
        }

        let gql_request = GraphQLQuery {
            query: graphql_query.to_string(),
            variables,
        };

        let response = self
            .client
            .post(format!("{}/graphql", self.api_url))
            .json(&gql_request)
            .send()
            .await?;

        if !response.status().is_success() {
            return Err(anyhow!("GraphQL request failed: {}", response.status()));
        }

        let _data = response.json::<serde_json::Value>().await?;

        // Parse GraphQL response - fallback to empty results if service unavailable
        Ok(DocumentSearchResult {
            query: query.to_string(),
            documents: Vec::new(),
            total: 0,
            relevance: 0,
        })
    }

    /// Classify a document using GraphQL mutation
    pub async fn classify_document(&self, content: &str) -> anyhow::Result<String> {
        let graphql_query = r#"
            mutation ClassifyDocument($content: String!) {
                classifyDocument(content: $content) {
                    classification
                }
            }
        "#;

        let variables = serde_json::json!({ "content": content });

        let gql_request = GraphQLQuery {
            query: graphql_query.to_string(),
            variables,
        };

        let response = self
            .client
            .post(format!("{}/graphql", self.api_url))
            .json(&gql_request)
            .send()
            .await?;

        if response.status().is_success() {
            let data = response.json::<serde_json::Value>().await?;
            if let Some(classification) = data
                .get("data")
                .and_then(|d| d.get("classifyDocument"))
                .and_then(|c| c.get("classification"))
                .and_then(|c| c.as_str())
            {
                return Ok(classification.to_string());
            }
        }

        // Fallback classification based on content
        if content.contains("API") || content.contains("endpoint") {
            Ok("API".to_string())
        } else if content.contains("guide") || content.contains("tutorial") {
            Ok("Guide".to_string())
        } else if content.contains("example") {
            Ok("Example".to_string())
        } else {
            Ok("Documentation".to_string())
        }
    }

    /// Get document by ID via GraphQL
    pub async fn get_document(&self, id: &str) -> anyhow::Result<Document> {
        let graphql_query = r#"
            query GetDocument($id: String!) {
                document(id: $id) {
                    id
                    path
                    title
                    content
                    classification
                    tags
                    modifiedAt
                }
            }
        "#;

        let variables = serde_json::json!({ "id": id });

        let gql_request = GraphQLQuery {
            query: graphql_query.to_string(),
            variables,
        };

        let response = self
            .client
            .post(format!("{}/graphql", self.api_url))
            .json(&gql_request)
            .send()
            .await?;

        if !response.status().is_success() {
            return Err(anyhow!("Document not found: {}", id));
        }

        Err(anyhow!("Document not found: {}", id))
    }

    /// Create a new document via GraphQL mutation
    pub async fn create_document(
        &self,
        title: &str,
        content: &str,
        classification: &str,
    ) -> anyhow::Result<Document> {
        let graphql_query = r#"
            mutation CreateDocument($title: String!, $content: String!, $classification: String!) {
                createDocument(title: $title, content: $content, classification: $classification) {
                    id
                    path
                    title
                    content
                    classification
                    tags
                    modifiedAt
                }
            }
        "#;

        let variables = serde_json::json!({
            "title": title,
            "content": content,
            "classification": classification
        });

        let gql_request = GraphQLQuery {
            query: graphql_query.to_string(),
            variables,
        };

        let response = self
            .client
            .post(format!("{}/graphql", self.api_url))
            .json(&gql_request)
            .send()
            .await?;

        if response.status().is_success() {
            // Would parse the response here
        }

        Ok(Document {
            id: uuid::Uuid::new_v4().to_string(),
            path: format!("/docs/{}", title.to_lowercase().replace(" ", "_")),
            title: title.to_string(),
            content: content.to_string(),
            classification: classification.to_string(),
            tags: Vec::new(),
            modified_at: chrono::Local::now().to_rfc3339(),
        })
    }

    /// Update document via GraphQL mutation
    pub async fn update_document(&self, doc: &Document) -> anyhow::Result<()> {
        let graphql_query = r#"
            mutation UpdateDocument($id: String!, $title: String!, $content: String!) {
                updateDocument(id: $id, title: $title, content: $content) {
                    id
                    modifiedAt
                }
            }
        "#;

        let variables = serde_json::json!({
            "id": doc.id,
            "title": doc.title,
            "content": doc.content
        });

        let gql_request = GraphQLQuery {
            query: graphql_query.to_string(),
            variables,
        };

        let response = self
            .client
            .post(format!("{}/graphql", self.api_url))
            .json(&gql_request)
            .send()
            .await?;

        if response.status().is_success() {
            tracing::info!("Document {} updated successfully", doc.id);
        }

        Ok(())
    }

    /// Watch document changes via WebSocket (requires WebSocket upgrade)
    pub async fn watch_documents(&self) -> anyhow::Result<()> {
        let ws_url = self
            .api_url
            .replace("http://", "ws://")
            .replace("https://", "wss://");
        let ws_endpoint = format!("{}/ws/documents", ws_url);

        tracing::info!("Connecting to WebSocket: {}", ws_endpoint);
        // In production, would establish tokio-tungstenite connection here
        Ok(())
    }

    /// Get document statistics via REST endpoint
    pub async fn get_stats(&self) -> anyhow::Result<DocumentStats> {
        let response = self
            .client
            .get(format!("{}/stats", self.api_url))
            .send()
            .await?;

        if response.status().is_success() {
            if let Ok(stats) = response.json::<DocumentStats>().await {
                return Ok(stats);
            }
        }

        Ok(DocumentStats {
            total_documents: 0,
            by_classification: Default::default(),
            total_size_bytes: 0,
            last_updated: chrono::Local::now().to_rfc3339(),
        })
    }
}

/// Document statistics
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct DocumentStats {
    /// Total document count
    pub total_documents: usize,
    /// Count by classification
    pub by_classification: std::collections::HashMap<String, usize>,
    /// Total size in bytes
    pub total_size_bytes: u64,
    /// Last update timestamp
    pub last_updated: String,
}

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

    #[test]
    fn test_document_creation() {
        let doc = Document {
            id: "doc1".to_string(),
            path: "/docs/test".to_string(),
            title: "Test Doc".to_string(),
            content: "Test content".to_string(),
            classification: "Guide".to_string(),
            tags: vec!["test".to_string()],
            modified_at: "2024-01-01T00:00:00Z".to_string(),
        };

        assert_eq!(doc.title, "Test Doc");
        assert_eq!(doc.classification, "Guide");
    }

    #[test]
    fn test_doc_lifecycle_client() {
        let client = DocLifecycleClient::new("http://localhost:7000");
        assert_eq!(client.api_url, "http://localhost:7000");
    }

    #[tokio::test]
    async fn test_classify_document() {
        let client = DocLifecycleClient::new("http://localhost:7000");

        let api_classification = client
            .classify_document("API endpoint documentation")
            .await
            .unwrap();
        assert_eq!(api_classification, "API");

        let guide_classification = client
            .classify_document("Step by step guide")
            .await
            .unwrap();
        assert_eq!(guide_classification, "Guide");
    }

    #[tokio::test]
    async fn test_create_document() {
        let client = DocLifecycleClient::new("http://localhost:7000");
        let doc = client
            .create_document("Test", "Content", "Guide")
            .await
            .unwrap();

        assert_eq!(doc.title, "Test");
        assert_eq!(doc.classification, "Guide");
        assert!(!doc.id.is_empty());
    }

    #[test]
    fn test_document_stats() {
        let stats = DocumentStats {
            total_documents: 10,
            by_classification: {
                let mut map = std::collections::HashMap::new();
                map.insert("API".to_string(), 5);
                map.insert("Guide".to_string(), 3);
                map
            },
            total_size_bytes: 1024 * 100,
            last_updated: "2024-01-01T00:00:00Z".to_string(),
        };

        assert_eq!(stats.total_documents, 10);
        assert_eq!(stats.total_size_bytes, 102400);
    }
}
