//! gRPC endpoints for configuration management

use crate::config::ConfigurationManager;
use crate::error::GatewayConfigError;
use futures::Stream;
use std::pin::Pin;
use tonic::{Request, Response, Status};
use tracing::{debug, info, instrument};

use crate::config_backend::{
    config_service_server::{ConfigService, ConfigServiceServer},
    ConfigChangeEvent, ConfigDataType, ConfigurationCategory, ConfigurationSetting,
    DeleteConfigurationRequest, DeleteConfigurationResponse, ExportConfigurationRequest,
    ExportConfigurationResponse, GetConfigSchemaRequest, GetConfigSchemaResponse,
    GetConfigurationHistoryRequest, GetConfigurationHistoryResponse, GetConfigurationRequest,
    GetConfigurationResponse, ImportConfigurationRequest, ImportConfigurationResponse,
    ListCategoriesRequest, ListCategoriesResponse, RollbackConfigurationRequest,
    RollbackConfigurationResponse, StreamConfigChangesRequest, UpdateConfigSchemaRequest,
    UpdateConfigSchemaResponse, UpdateConfigurationRequest, UpdateConfigurationResponse,
    ValidateConfigurationRequest, ValidateConfigurationResponse,
};

/// gRPC service implementation for gateway-local configuration management.
///
/// Implements the `config.ConfigService` trait from config.proto.
/// Handles GetConfiguration, UpdateConfiguration, and ListCategories locally;
/// all other RPCs return `Status::unimplemented`.
pub struct GatewayConfigService {
    manager: std::sync::Arc<tokio::sync::RwLock<ConfigurationManager>>,
}

impl GatewayConfigService {
    /// Creates a new GatewayConfigService
    pub fn new(manager: ConfigurationManager) -> Self {
        Self {
            manager: std::sync::Arc::new(tokio::sync::RwLock::new(manager)),
        }
    }

    /// Returns the gRPC server instance
    pub fn into_server(self) -> ConfigServiceServer<Self> {
        ConfigServiceServer::new(self)
    }
}

#[tonic::async_trait]
impl ConfigService for GatewayConfigService {
    type StreamConfigChangesStream =
        Pin<Box<dyn Stream<Item = Result<ConfigChangeEvent, Status>> + Send>>;

    #[instrument(skip_all)]
    async fn get_configuration(
        &self,
        request: Request<GetConfigurationRequest>,
    ) -> Result<Response<GetConfigurationResponse>, Status> {
        let req = request.into_inner();
        let category = req.category.unwrap_or_default();
        let key = req.key.unwrap_or_default();
        debug!("GetConfiguration: {}/{}", category, key);

        if category.is_empty() && key.is_empty() {
            // List all configs
            let manager = self.manager.read().await;
            let configs = manager
                .list_configs(None)
                .await
                .map_err(|e| Status::internal(format!("{}", e)))?;

            let settings: Result<Vec<ConfigurationSetting>, Status> = configs
                .into_iter()
                .map(|c| config_to_setting(&c))
                .collect();

            return Ok(Response::new(GetConfigurationResponse {
                settings: settings?,
            }));
        }

        let manager = self.manager.read().await;

        if !key.is_empty() {
            // Get specific config
            let config = manager
                .get_config(&category, &key)
                .await
                .map_err(|e| match e {
                    GatewayConfigError::NotFound { .. } => Status::not_found(format!("{}", e)),
                    _ => Status::internal(format!("{}", e)),
                })?;

            Ok(Response::new(GetConfigurationResponse {
                settings: vec![config_to_setting(&config)?],
            }))
        } else {
            // List by category
            let configs = manager
                .list_configs(Some(&category))
                .await
                .map_err(|e| Status::internal(format!("{}", e)))?;

            let settings: Result<Vec<ConfigurationSetting>, Status> = configs
                .into_iter()
                .map(|c| config_to_setting(&c))
                .collect();

            Ok(Response::new(GetConfigurationResponse {
                settings: settings?,
            }))
        }
    }

    #[instrument(skip_all)]
    async fn update_configuration(
        &self,
        request: Request<UpdateConfigurationRequest>,
    ) -> Result<Response<UpdateConfigurationResponse>, Status> {
        let req = request.into_inner();
        info!(
            "UpdateConfiguration: {}/{} by {}",
            req.category, req.key, req.changed_by
        );

        let new_value: serde_json::Value = serde_json::from_str(&req.value)
            .map_err(|e| Status::invalid_argument(format!("Invalid JSON: {}", e)))?;

        let manager = self.manager.read().await;
        manager
            .update_config(&req.category, &req.key, new_value, &req.changed_by)
            .await
            .map_err(|e| match e {
                GatewayConfigError::Validation(msg) => Status::invalid_argument(msg),
                GatewayConfigError::NotFound { .. } => Status::not_found(format!("{}", e)),
                _ => Status::internal(format!("{}", e)),
            })?;

        Ok(Response::new(UpdateConfigurationResponse {
            success: true,
            message: format!("Successfully updated {}/{}", req.category, req.key),
            validation_result: None,
            timestamp: chrono::Utc::now().timestamp(),
        }))
    }

    #[instrument(skip_all)]
    async fn delete_configuration(
        &self,
        _request: Request<DeleteConfigurationRequest>,
    ) -> Result<Response<DeleteConfigurationResponse>, Status> {
        Err(Status::unimplemented(
            "Delete not supported by gateway config",
        ))
    }

    #[instrument(skip_all)]
    async fn list_categories(
        &self,
        request: Request<ListCategoriesRequest>,
    ) -> Result<Response<ListCategoriesResponse>, Status> {
        let req = request.into_inner();
        debug!("ListCategories: {:?}", req.parent_category);

        let manager = self.manager.read().await;
        let configs = manager
            .list_configs(req.parent_category.as_deref())
            .await
            .map_err(|e| Status::internal(format!("{}", e)))?;

        // Extract unique categories from configs
        let mut seen = std::collections::HashSet::new();
        let categories = configs
            .iter()
            .filter_map(|c| {
                if seen.insert(c.service_scope.clone()) {
                    Some(ConfigurationCategory {
                        id: 0,
                        name: c.service_scope.clone(),
                        description: String::new(),
                        parent_id: None,
                        display_order: 0,
                        icon: None,
                        created_at: c.created_at.timestamp(),
                        children: vec![],
                    })
                } else {
                    None
                }
            })
            .collect();

        Ok(Response::new(ListCategoriesResponse { categories }))
    }

    async fn stream_config_changes(
        &self,
        _request: Request<StreamConfigChangesRequest>,
    ) -> Result<Response<Self::StreamConfigChangesStream>, Status> {
        Err(Status::unimplemented(
            "Streaming not supported by gateway config",
        ))
    }

    async fn validate_configuration(
        &self,
        _request: Request<ValidateConfigurationRequest>,
    ) -> Result<Response<ValidateConfigurationResponse>, Status> {
        Err(Status::unimplemented(
            "Validation not supported by gateway config",
        ))
    }

    async fn get_configuration_history(
        &self,
        _request: Request<GetConfigurationHistoryRequest>,
    ) -> Result<Response<GetConfigurationHistoryResponse>, Status> {
        Err(Status::unimplemented(
            "History not supported by gateway config",
        ))
    }

    async fn rollback_configuration(
        &self,
        _request: Request<RollbackConfigurationRequest>,
    ) -> Result<Response<RollbackConfigurationResponse>, Status> {
        Err(Status::unimplemented(
            "Rollback not supported by gateway config",
        ))
    }

    async fn export_configuration(
        &self,
        _request: Request<ExportConfigurationRequest>,
    ) -> Result<Response<ExportConfigurationResponse>, Status> {
        Err(Status::unimplemented(
            "Export not supported by gateway config",
        ))
    }

    async fn import_configuration(
        &self,
        _request: Request<ImportConfigurationRequest>,
    ) -> Result<Response<ImportConfigurationResponse>, Status> {
        Err(Status::unimplemented(
            "Import not supported by gateway config",
        ))
    }

    async fn get_config_schema(
        &self,
        _request: Request<GetConfigSchemaRequest>,
    ) -> Result<Response<GetConfigSchemaResponse>, Status> {
        Err(Status::unimplemented(
            "Schema not supported by gateway config",
        ))
    }

    async fn update_config_schema(
        &self,
        _request: Request<UpdateConfigSchemaRequest>,
    ) -> Result<Response<UpdateConfigSchemaResponse>, Status> {
        Err(Status::unimplemented(
            "Schema update not supported by gateway config",
        ))
    }
}

/// Convert a ConfigItem (from ConfigurationManager) to a ConfigurationSetting proto message
fn config_to_setting(c: &crate::config::ConfigItem) -> Result<ConfigurationSetting, Status> {
    Ok(ConfigurationSetting {
        id: 0,
        category: c.service_scope.clone(),
        key: c.config_key.clone(),
        value: serde_json::to_string(&c.config_value)
            .map_err(|e| Status::internal(format!("Serialization error: {}", e)))?,
        data_type: ConfigDataType::String as i32,
        hot_reload: false,
        description: c.description.clone().unwrap_or_default(),
        default_value: None,
        required: false,
        sensitive: false,
        validation_rule: None,
        environment_override: None,
        min_value: None,
        max_value: None,
        enum_values: None,
        depends_on: vec![],
        tags: vec![],
        display_order: 0,
        created_at: c.created_at.timestamp(),
        modified_at: c.updated_at.timestamp(),
    })
}
