//! REST API handlers for TypeDialog AI Service

use axum::{
    extract::{Path, State},
    http::StatusCode,
    routing::{delete, get, post},
    Json, Router,
};
use chrono::Utc;

use super::{error::ApiError, types::*};
use crate::api::websocket;

/// Create Axum router with all routes
pub fn create_router(state: AppState) -> Router {
    use crate::web_ui;

    Router::new()
        // Web UI
        .route("/", get(web_ui::index))
        .route("/index.html", get(web_ui::index))
        .route("/styles.css", get(web_ui::styles))
        .route("/app.js", get(web_ui::app))
        // Health check
        .route("/health", get(health_check))
        // Conversation management
        .route("/conversations", post(create_conversation))
        .route("/conversations/{id}", get(get_conversation))
        .route("/conversations/{id}", delete(delete_conversation))
        // Message handling
        .route("/conversations/{id}/messages", post(send_message))
        // Configuration generation
        .route("/conversations/{id}/generate", post(generate_config))
        // Suggestions
        .route(
            "/conversations/{id}/suggestions/{field}",
            get(get_suggestions),
        )
        // WebSocket streaming
        .route("/ws/{id}", axum::routing::get(websocket::handle_websocket))
        .with_state(state)
}

// ============================================================================
// HEALTH CHECK
// ============================================================================

/// Health check endpoint
pub async fn health_check(State(state): State<AppState>) -> Json<HealthResponse> {
    let uptime = state.start_time.elapsed().as_secs();

    Json(HealthResponse {
        status: "healthy".to_string(),
        version: env!("CARGO_PKG_VERSION").to_string(),
        uptime,
    })
}

// ============================================================================
// CONVERSATION MANAGEMENT
// ============================================================================

/// Create a new conversation
///
/// # Request
/// ```json
/// {"schema_id": "my-schema"}
/// ```
///
/// # Response
/// ```json
/// {
///   "conversation_id": "uuid",
///   "schema_id": "my-schema",
///   "message": "Conversation started"
/// }
/// ```
pub async fn create_conversation(
    State(state): State<AppState>,
    Json(req): Json<CreateConversationRequest>,
) -> Result<(StatusCode, Json<CreateConversationResponse>), ApiError> {
    tracing::info!(schema_id = %req.schema_id, "Creating new conversation");

    // Create conversation in database
    let conv_id = state
        .db
        .create_conversation(&req.schema_id)
        .await
        .map_err(|e| ApiError::DatabaseError(e.to_string()))?;

    Ok((
        StatusCode::CREATED,
        Json(CreateConversationResponse {
            conversation_id: conv_id,
            schema_id: req.schema_id,
            message: "Conversation created successfully".to_string(),
        }),
    ))
}

/// Get conversation information
pub async fn get_conversation(
    State(_state): State<AppState>,
    Path(id): Path<String>,
) -> Result<Json<ConversationInfo>, ApiError> {
    tracing::debug!(conversation_id = %id, "Getting conversation info");

    // TODO: Load conversation from database
    Ok(Json(ConversationInfo {
        id: id.clone(),
        schema_id: "schema".to_string(),
        status: "active".to_string(),
        created_at: Utc::now().to_rfc3339(),
        updated_at: Utc::now().to_rfc3339(),
    }))
}

/// Delete a conversation
pub async fn delete_conversation(
    State(_state): State<AppState>,
    Path(id): Path<String>,
) -> Result<StatusCode, ApiError> {
    tracing::info!(conversation_id = %id, "Deleting conversation");

    // TODO: Delete conversation from database
    Ok(StatusCode::NO_CONTENT)
}

// ============================================================================
// MESSAGE HANDLING
// ============================================================================

/// Send a message to the assistant
///
/// # Request
/// ```json
/// {"message": "What port should I use?"}
/// ```
///
/// # Response
/// ```json
/// {
///   "text": "For a web server, port 8080 is commonly used...",
///   "suggestions": [
///     {
///       "field": "port",
///       "value": "8080",
///       "reasoning": "Common web server port",
///       "confidence": 0.92
///     }
///   ],
///   "message_id": "msg-uuid",
///   "rag_context": "..."
/// }
/// ```
pub async fn send_message(
    State(_state): State<AppState>,
    Path(id): Path<String>,
    Json(req): Json<SendMessageRequest>,
) -> Result<Json<SendMessageResponse>, ApiError> {
    tracing::debug!(conversation_id = %id, message_len = req.message.len(), "Processing message");

    if req.message.trim().is_empty() {
        return Err(ApiError::InvalidRequest(
            "Message cannot be empty".to_string(),
        ));
    }

    // TODO: Load assistant from state, call send_message(), return response
    Err(ApiError::InternalError(
        "Assistant not initialized".to_string(),
    ))
}

// ============================================================================
// CONFIGURATION GENERATION
// ============================================================================

/// Generate configuration from conversation
///
/// # Request
/// ```json
/// {"format": "json"}
/// ```
///
/// # Response
/// ```json
/// {
///   "content": "{\"port\": 8080, ...}",
///   "format": "json",
///   "is_valid": true,
///   "errors": []
/// }
/// ```
pub async fn generate_config(
    State(_state): State<AppState>,
    Path(id): Path<String>,
    Json(req): Json<GenerateConfigRequest>,
) -> Result<Json<GenerateConfigResponse>, ApiError> {
    tracing::info!(conversation_id = %id, format = %req.format, "Generating configuration");

    // Validate format
    if !matches!(req.format.as_str(), "json" | "yaml" | "toml") {
        return Err(ApiError::InvalidRequest(format!(
            "Invalid format '{}'. Must be 'json', 'yaml', or 'toml'",
            req.format
        )));
    }

    // TODO: Load assistant from state, call generate_config(), return response
    Err(ApiError::InternalError(
        "Assistant not initialized".to_string(),
    ))
}

// ============================================================================
// SUGGESTIONS
// ============================================================================

/// Get field suggestions
///
/// # Response
/// ```json
/// {
///   "field": "port",
///   "suggestions": [
///     {
///       "field": "port",
///       "value": "8080",
///       "reasoning": "Found in examples",
///       "confidence": 0.85
///     }
///   ]
/// }
/// ```
pub async fn get_suggestions(
    State(_state): State<AppState>,
    Path((id, field)): Path<(String, String)>,
) -> Result<Json<SuggestionsResponse>, ApiError> {
    tracing::debug!(conversation_id = %id, field = %field, "Getting suggestions");

    // TODO: Load assistant from state, call suggest_values(), return response
    Err(ApiError::InternalError(
        "Assistant not initialized".to_string(),
    ))
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::storage::SurrealDbClient;
    use std::sync::{Arc, Mutex};

    async fn create_test_state() -> Result<AppState, Box<dyn std::error::Error>> {
        let db = Arc::new(SurrealDbClient::new("memory://", "default", "test").await?);
        Ok(AppState {
            db,
            assistants: Arc::new(Mutex::new(std::collections::HashMap::new())),
            start_time: std::time::Instant::now(),
        })
    }

    #[tokio::test]
    async fn test_router_creation() {
        let state = create_test_state().await.unwrap();
        let _router = create_router(state);
        // Router created successfully
    }

    #[test]
    fn test_create_conversation_request_validation() {
        let req = CreateConversationRequest {
            schema_id: "test-schema".to_string(),
        };
        assert!(!req.schema_id.is_empty());
    }

    #[test]
    fn test_generate_config_request_format_validation() {
        let valid_formats = vec!["json", "yaml", "toml"];
        for format in valid_formats {
            let req = GenerateConfigRequest {
                format: format.to_string(),
            };
            assert!(matches!(req.format.as_str(), "json" | "yaml" | "toml"));
        }
    }

    #[test]
    fn test_send_message_request_serialization() {
        let req = SendMessageRequest {
            message: "Test message".to_string(),
        };
        let json = serde_json::to_string(&req).unwrap();
        let deserialized: SendMessageRequest = serde_json::from_str(&json).unwrap();
        assert_eq!(deserialized.message, "Test message");
    }
}
