// Example: Simple AI Backend Service (Microservice Pattern)
//
// This demonstrates how to create a standalone RAG service that can be
// integrated into the TypeDialog system or used independently.
//
// This is pseudocode for clarity. To implement:
// 1. Create new crate: cargo new crates/typedialog-ai-service
// 2. Add dependencies: axum, tokio, serde, typedialog-core
// 3. Implement handlers as shown below

#![allow(dead_code)]

// ============================================================================
// SERVICE DEFINITION
// ============================================================================

/// AI Backend Service - Provides RAG and Knowledge Graph capabilities
pub struct AiBackendService {
    /// RAG system for document retrieval
    rag: typedialog_core::ai::rag::RagSystem,

    /// Optional: Knowledge graph for entity relationships
    #[cfg(feature = "ai_backend")]
    kg: typedialog_core::ai::kg::KnowledgeGraph,
}

impl AiBackendService {
    /// Create new service instance
    pub fn new(config: ServiceConfig) -> Result<Self, Box<dyn std::error::Error>> {
        let rag_config = typedialog_core::ai::rag::RagConfig {
            semantic_weight: config.semantic_weight,
            keyword_weight: config.keyword_weight,
            max_results: config.max_results,
            min_score: config.min_score,
        };

        let rag = typedialog_core::ai::rag::RagSystem::new(rag_config)?;

        Ok(AiBackendService {
            rag,
            #[cfg(feature = "ai_backend")]
            kg: typedialog_core::ai::kg::KnowledgeGraph::new(),
        })
    }

    /// Load knowledge base from file or database
    pub async fn load_knowledge_base(&mut self, path: &str) -> Result<usize, Box<dyn std::error::Error>> {
        // In production: Load from database or file system
        // For now: Load from JSON file with documents

        let contents = std::fs::read_to_string(path)?;
        let docs: Vec<(String, String)> = serde_json::from_str(&contents)?;

        let count = docs.len();
        self.rag.add_documents_batch(docs)?;

        Ok(count)
    }

    /// Save service state to disk
    pub fn persist(&self, path: &str) -> Result<(), Box<dyn std::error::Error>> {
        self.rag.save_to_file(path)?;
        Ok(())
    }

    /// Load service state from disk
    pub fn restore(path: &str) -> Result<Self, Box<dyn std::error::Error>> {
        let rag = typedialog_core::ai::rag::RagSystem::load_from_file(path)?;
        Ok(AiBackendService {
            rag,
            #[cfg(feature = "ai_backend")]
            kg: typedialog_core::ai::kg::KnowledgeGraph::new(),
        })
    }
}

// ============================================================================
// SERVICE CONFIGURATION
// ============================================================================

#[derive(serde::Serialize, serde::Deserialize, Clone)]
pub struct ServiceConfig {
    /// Semantic search weight (0.0-1.0)
    pub semantic_weight: f32,

    /// Keyword search weight (0.0-1.0)
    pub keyword_weight: f32,

    /// Maximum results per query
    pub max_results: usize,

    /// Minimum score threshold
    pub min_score: f32,
}

impl Default for ServiceConfig {
    fn default() -> Self {
        ServiceConfig {
            semantic_weight: 0.6,
            keyword_weight: 0.4,
            max_results: 5,
            min_score: 0.0,
        }
    }
}

// ============================================================================
// REST API ENDPOINTS (Pseudo-code with Axum)
// ============================================================================

/*
// In production, implement with Axum web framework:

use axum::{
    extract::{Json, State, Path},
    routing::{get, post, delete},
    Router,
    http::StatusCode,
};
use std::sync::{Arc, Mutex};

pub type SharedService = Arc<Mutex<AiBackendService>>;

/// Create REST router for AI service
pub fn create_routes(service: SharedService) -> Router {
    Router::new()
        // Health check
        .route("/health", get(health_check))

        // Search endpoints
        .route("/search", post(search_documents))
        .route("/search/semantic", post(search_semantic))
        .route("/search/keyword", post(search_keyword))

        // Document management
        .route("/documents", post(add_documents))
        .route("/documents/batch", post(add_documents_batch))
        .route("/documents/:id", delete(remove_document))
        .route("/documents/batch", delete(remove_documents_batch))
        .route("/documents/count", get(document_count))

        // Configuration
        .route("/config", get(get_config))
        .route("/config", post(update_config))

        // Persistence
        .route("/save", post(save_state))
        .route("/load", post(load_state))

        .with_state(service)
}

// ============================================================================
// HANDLER IMPLEMENTATIONS
// ============================================================================

/// GET /health - Service health check
async fn health_check() -> Json<HealthResponse> {
    Json(HealthResponse {
        status: "healthy".into(),
        uptime_seconds: 0, // Calculate in real impl
    })
}

#[derive(serde::Serialize)]
struct HealthResponse {
    status: String,
    uptime_seconds: u64,
}

/// POST /search - Hybrid search (semantic + keyword)
async fn search_documents(
    State(service): State<SharedService>,
    Json(req): Json<SearchRequest>,
) -> Result<Json<Vec<SearchResult>>, ApiError> {
    let mut svc = service.lock().map_err(|_| ApiError::LockPoisoned)?;
    let results = svc.rag.retrieve(&req.query)
        .map_err(|e| ApiError::SearchFailed(e.to_string()))?;

    Ok(Json(
        results.into_iter().map(|r| SearchResult {
            doc_id: r.doc_id,
            content: r.content,
            semantic_score: r.semantic_score,
            keyword_score: r.keyword_score,
            combined_score: r.combined_score,
        }).collect()
    ))
}

#[derive(serde::Deserialize)]
struct SearchRequest {
    query: String,
}

#[derive(serde::Serialize)]
struct SearchResult {
    doc_id: String,
    content: String,
    semantic_score: f32,
    keyword_score: f32,
    combined_score: f32,
}

/// POST /documents - Add single document
async fn add_documents(
    State(service): State<SharedService>,
    Json(req): Json<AddDocumentRequest>,
) -> Result<StatusCode, ApiError> {
    let mut svc = service.lock().map_err(|_| ApiError::LockPoisoned)?;
    svc.rag.add_document(req.id, req.content)
        .map_err(|e| ApiError::AddFailed(e.to_string()))?;
    Ok(StatusCode::CREATED)
}

#[derive(serde::Deserialize)]
struct AddDocumentRequest {
    id: String,
    content: String,
}

/// POST /documents/batch - Add multiple documents efficiently
async fn add_documents_batch(
    State(service): State<SharedService>,
    Json(req): Json<AddDocumentsBatchRequest>,
) -> Result<Json<AddBatchResponse>, ApiError> {
    let mut svc = service.lock().map_err(|_| ApiError::LockPoisoned)?;
    let count_before = svc.rag.doc_count();

    svc.rag.add_documents_batch(req.documents)
        .map_err(|e| ApiError::AddFailed(e.to_string()))?;

    let count_after = svc.rag.doc_count();

    Ok(Json(AddBatchResponse {
        added: count_after - count_before,
        total: count_after,
    }))
}

#[derive(serde::Deserialize)]
struct AddDocumentsBatchRequest {
    documents: Vec<(String, String)>,
}

#[derive(serde::Serialize)]
struct AddBatchResponse {
    added: usize,
    total: usize,
}

/// DELETE /documents/:id - Remove single document
async fn remove_document(
    State(service): State<SharedService>,
    Path(id): Path<String>,
) -> Result<StatusCode, ApiError> {
    let mut svc = service.lock().map_err(|_| ApiError::LockPoisoned)?;
    let removed = svc.rag.remove_document(&id);

    if removed {
        Ok(StatusCode::OK)
    } else {
        Err(ApiError::NotFound)
    }
}

/// DELETE /documents/batch - Remove multiple documents
async fn remove_documents_batch(
    State(service): State<SharedService>,
    Json(req): Json<RemoveBatchRequest>,
) -> Result<Json<RemoveBatchResponse>, ApiError> {
    let mut svc = service.lock().map_err(|_| ApiError::LockPoisoned)?;
    let ids: Vec<&str> = req.ids.iter().map(|s| s.as_str()).collect();
    let removed = svc.rag.remove_documents_batch(&ids);

    Ok(Json(RemoveBatchResponse {
        removed,
        remaining: svc.rag.doc_count(),
    }))
}

#[derive(serde::Deserialize)]
struct RemoveBatchRequest {
    ids: Vec<String>,
}

#[derive(serde::Serialize)]
struct RemoveBatchResponse {
    removed: usize,
    remaining: usize,
}

/// GET /documents/count - Get document count
async fn document_count(
    State(service): State<SharedService>,
) -> Result<Json<CountResponse>, ApiError> {
    let svc = service.lock().map_err(|_| ApiError::LockPoisoned)?;
    Ok(Json(CountResponse {
        count: svc.rag.doc_count(),
    }))
}

#[derive(serde::Serialize)]
struct CountResponse {
    count: usize,
}

/// GET /config - Get current configuration
async fn get_config(
    State(service): State<SharedService>,
) -> Result<Json<ServiceConfig>, ApiError> {
    let svc = service.lock().map_err(|_| ApiError::LockPoisoned)?;
    Ok(Json(ServiceConfig {
        semantic_weight: svc.rag.config().semantic_weight,
        keyword_weight: svc.rag.config().keyword_weight,
        max_results: svc.rag.config().max_results,
        min_score: svc.rag.config().min_score,
    }))
}

/// POST /config - Update configuration
async fn update_config(
    State(service): State<SharedService>,
    Json(config): Json<ServiceConfig>,
) -> Result<StatusCode, ApiError> {
    let mut svc = service.lock().map_err(|_| ApiError::LockPoisoned)?;
    let rag_config = typedialog_core::ai::rag::RagConfig {
        semantic_weight: config.semantic_weight,
        keyword_weight: config.keyword_weight,
        max_results: config.max_results,
        min_score: config.min_score,
    };
    svc.rag.set_config(rag_config);
    Ok(StatusCode::OK)
}

/// POST /save - Persist service state
async fn save_state(
    State(service): State<SharedService>,
    Json(req): Json<SaveRequest>,
) -> Result<StatusCode, ApiError> {
    let svc = service.lock().map_err(|_| ApiError::LockPoisoned)?;
    svc.rag.save_to_file(&req.path)
        .map_err(|e| ApiError::SaveFailed(e.to_string()))?;
    Ok(StatusCode::OK)
}

#[derive(serde::Deserialize)]
struct SaveRequest {
    path: String,
}

/// POST /load - Restore service state
async fn load_state(
    State(service): State<SharedService>,
    Json(req): Json<LoadRequest>,
) -> Result<StatusCode, ApiError> {
    let mut svc = service.lock().map_err(|_| ApiError::LockPoisoned)?;
    let loaded = typedialog_core::ai::rag::RagSystem::load_from_file(&req.path)
        .map_err(|e| ApiError::LoadFailed(e.to_string()))?;
    *svc = AiBackendService { rag: loaded,
        #[cfg(feature = "ai_backend")]
        kg: typedialog_core::ai::kg::KnowledgeGraph::new(),
    };
    Ok(StatusCode::OK)
}

#[derive(serde::Deserialize)]
struct LoadRequest {
    path: String,
}

// ============================================================================
// ERROR HANDLING
// ============================================================================

#[derive(Debug)]
pub enum ApiError {
    LockPoisoned,
    SearchFailed(String),
    AddFailed(String),
    NotFound,
    SaveFailed(String),
    LoadFailed(String),
}

impl axum::response::IntoResponse for ApiError {
    fn into_response(self) -> axum::response::Response {
        use axum::response::IntoResponse;
        match self {
            ApiError::NotFound => StatusCode::NOT_FOUND.into_response(),
            _ => StatusCode::INTERNAL_SERVER_ERROR.into_response(),
        }
    }
}

// ============================================================================
// MAIN APPLICATION
// ============================================================================

/*
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Initialize service
    let config = ServiceConfig::default();
    let mut service = AiBackendService::new(config)?;

    // Load knowledge base
    service.load_knowledge_base("kb.json").await?;
    println!("Loaded {} documents", service.rag.doc_count());

    // Create shared service for handlers
    let shared_service = Arc::new(Mutex::new(service));

    // Create router
    let app = create_routes(shared_service);

    // Start server
    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
    axum::serve(listener, app).await?;

    Ok(())
}
*/

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

    #[test]
    fn test_service_creation() {
        let config = ServiceConfig::default();
        let result = AiBackendService::new(config);
        assert!(result.is_ok());
    }
}
