pmat 2.93.1

PMAT - Zero-config AI context generation and code quality toolkit (CLI, MCP, HTTP)
use crate::models::pdmt::{EnforcementMode, PdmtQualityConfig};
use crate::services::pdmt_quality_integration::PdmtQualityEnforcer;
use crate::services::pdmt_service::PdmtService;
use async_trait::async_trait;
use pmcp::{Error, RequestHandlerExtra, Result, ToolHandler};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use tracing::{debug, error, info};

/// Input parameters for the PDMT deterministic todos tool
#[derive(Debug, Deserialize)]
pub struct PdmtInput {
    /// List of requirements to convert to actionable todos
    pub requirements: Vec<String>,
    /// Name of the project or component
    #[serde(skip_serializing_if = "Option::is_none")]
    pub project_name: Option<String>,
    /// Level of task detail and specificity
    #[serde(default = "default_granularity")]
    pub granularity: String,
    /// Quality enforcement configuration
    #[serde(default)]
    pub quality_config: QualityConfigInput,
}

fn default_granularity() -> String {
    "high".to_string()
}

#[derive(Debug, Deserialize)]
pub struct QualityConfigInput {
    #[serde(default = "default_enforcement_mode")]
    pub enforcement_mode: String,
    #[serde(default = "default_coverage_threshold")]
    pub coverage_threshold: f32,
    #[serde(default = "default_max_complexity")]
    pub max_complexity: u32,
    #[serde(default = "default_require_doctests")]
    pub require_doctests: bool,
    #[serde(default = "default_require_property_tests")]
    pub require_property_tests: bool,
    #[serde(default = "default_require_examples")]
    pub require_examples: bool,
    #[serde(default = "default_zero_satd_tolerance")]
    pub zero_satd_tolerance: bool,
}

impl Default for QualityConfigInput {
    fn default() -> Self {
        Self {
            enforcement_mode: default_enforcement_mode(),
            coverage_threshold: default_coverage_threshold(),
            max_complexity: default_max_complexity(),
            require_doctests: default_require_doctests(),
            require_property_tests: default_require_property_tests(),
            require_examples: default_require_examples(),
            zero_satd_tolerance: default_zero_satd_tolerance(),
        }
    }
}

fn default_enforcement_mode() -> String {
    "strict".to_string()
}

fn default_coverage_threshold() -> f32 {
    80.0
}

fn default_max_complexity() -> u32 {
    8
}

fn default_require_doctests() -> bool {
    true
}

fn default_require_property_tests() -> bool {
    true
}

fn default_require_examples() -> bool {
    true
}

fn default_zero_satd_tolerance() -> bool {
    true
}

/// Output structure for PDMT todo generation
#[derive(Debug, Serialize, Deserialize)]
pub struct PdmtOutput {
    pub success: bool,
    pub message: String,
    pub todo_list: Option<serde_json::Value>,
    pub quality_validation: Option<serde_json::Value>,
    pub total_todos: usize,
    pub estimated_total_hours: f32,
}

/// Tool handler for generating deterministic, quality-enforced todo lists.
///
/// This handler creates deterministic todo lists from requirements with
/// comprehensive quality enforcement through PAIML's quality proxy.
/// All generated todos include quality gates, validation commands,
/// and success criteria to ensure high-quality implementation.
///
/// # Example
///
/// ```
/// use pmat::mcp_pmcp::pdmt_handler::PdmtTool;
/// use pmcp::ToolHandler;
///
/// let tool = PdmtTool::new();
/// assert_eq!(tool.name(), "pdmt_deterministic_todos");
/// ```
///
/// # Using PDMT for Todo Generation
///
/// ```
/// use pmat::mcp_pmcp::pdmt_handler::{PdmtTool, PdmtInput, QualityConfigInput};
/// use pmcp::ToolHandler;
/// use serde_json::json;
///
/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
/// let tool = PdmtTool::new();
///
/// // Create input for authentication system todos
/// let input = json!({
///     "requirements": [
///         "implement JWT authentication",
///         "add OAuth2 support",
///         "create user registration"
///     ],
///     "project_name": "auth_system",
///     "granularity": "high",
///     "quality_config": {
///         "enforcement_mode": "strict",
///         "coverage_threshold": 85.0,
///         "max_complexity": 10
///     }
/// });
///
/// // Generate todos with quality enforcement
/// let result = tool.handle(input, Default::default()).await?;
///
/// // Verify response structure
/// assert!(result["success"].as_bool().unwrap_or(false));
/// assert!(result["total_todos"].as_u64().unwrap_or(0) > 0);
/// assert!(result["todo_list"].is_object());
/// # Ok(())
/// # }
/// ```
///
/// # Quality Configuration Options
///
/// ```
/// use pmat::mcp_pmcp::pdmt_handler::QualityConfigInput;
///
/// // Strict production configuration
/// let production_config = QualityConfigInput {
///     enforcement_mode: "strict".to_string(),
///     coverage_threshold: 90.0,
///     max_complexity: 8,
///     require_doctests: true,
///     require_property_tests: true,
///     require_examples: true,
///     zero_satd_tolerance: true,
/// };
///
/// // Advisory development configuration
/// let dev_config = QualityConfigInput {
///     enforcement_mode: "advisory".to_string(),
///     coverage_threshold: 70.0,
///     max_complexity: 15,
///     require_doctests: false,
///     require_property_tests: false,
///     require_examples: false,
///     zero_satd_tolerance: true,
/// };
/// ```
pub struct PdmtTool {
    service: PdmtService,
    quality_enforcer: PdmtQualityEnforcer,
}

impl PdmtTool {
    #[must_use] 
    pub fn new() -> Self {
        Self {
            service: PdmtService::new(),
            quality_enforcer: PdmtQualityEnforcer::new(),
        }
    }
}

impl Default for PdmtTool {
    fn default() -> Self {
        Self::new()
    }
}

#[async_trait]
impl ToolHandler for PdmtTool {
    async fn handle(&self, args: Value, _extra: RequestHandlerExtra) -> Result<Value> {
        debug!("Handling pdmt_deterministic_todos with args: {}", args);

        let input: PdmtInput = serde_json::from_value(args)
            .map_err(|e| Error::validation(format!("Invalid arguments: {e}")))?;

        if input.requirements.is_empty() {
            return Err(Error::validation("Requirements list cannot be empty"));
        }

        info!(
            "Generating deterministic todos for {} requirements",
            input.requirements.len()
        );

        // Convert input quality config to internal format
        let enforcement_mode = match input.quality_config.enforcement_mode.as_str() {
            "strict" => EnforcementMode::Strict,
            "advisory" => EnforcementMode::Advisory,
            "auto_fix" | "autofix" => EnforcementMode::AutoFix,
            _ => {
                return Err(Error::validation(format!(
                    "Invalid enforcement mode: {}. Must be 'strict', 'advisory', or 'auto_fix'",
                    input.quality_config.enforcement_mode
                )))
            }
        };

        let quality_config = PdmtQualityConfig {
            enforcement_mode,
            coverage_threshold: input.quality_config.coverage_threshold,
            max_complexity: input.quality_config.max_complexity,
            require_doctests: input.quality_config.require_doctests,
            require_property_tests: input.quality_config.require_property_tests,
            require_examples: input.quality_config.require_examples,
            zero_satd_tolerance: input.quality_config.zero_satd_tolerance,
        };

        // Generate deterministic todos
        let todo_list = self
            .service
            .generate_todos(
                input.requirements,
                input.project_name,
                &input.granularity,
                quality_config,
            )
            .map_err(|e| Error::internal(format!("Failed to generate todos: {e}")))?;

        // Run quality enforcement validation
        let quality_validation = self
            .quality_enforcer
            .enforce_quality_standards(&todo_list)
            .await
            .map_err(|e| Error::internal(format!("Quality validation failed: {e}")))?;

        // Calculate summary statistics
        let total_todos = todo_list.todos.len();
        let estimated_total_hours: f32 = todo_list.todos.iter().map(|t| t.estimated_hours).sum();

        // Prepare output
        let output =
            PdmtOutput {
                success: quality_validation.overall_passed,
                message: if quality_validation.overall_passed {
                    format!(
                        "Successfully generated {total_todos} deterministic todos with quality enforcement"
                    )
                } else {
                    format!(
                        "Generated {total_todos} todos but quality validation failed. Review violations."
                    )
                },
                todo_list: Some(serde_json::to_value(&todo_list).map_err(|e| {
                    Error::internal(format!("Failed to serialize todo list: {e}"))
                })?),
                quality_validation: Some(serde_json::to_value(&quality_validation).map_err(
                    |e| Error::internal(format!("Failed to serialize validation results: {e}")),
                )?),
                total_todos,
                estimated_total_hours,
            };

        if quality_validation.overall_passed {
            info!(
                "Successfully generated {} todos totaling {} hours",
                total_todos, estimated_total_hours
            );
        } else {
            error!(
                "Quality validation failed with {} recommendations",
                quality_validation.recommendations.len()
            );
        }

        serde_json::to_value(output)
            .map_err(|e| Error::internal(format!("Serialization error: {e}")))
    }
}

#[cfg(test)]
mod tests {
    use crate::services::pdmt_service::PdmtService;

    #[test]
    fn test_pdmt_service_basic() {
        let service = PdmtService::new();
        let requirements = vec![
            "implement user authentication".to_string(),
            "add logging system".to_string(),
        ];
        let config = crate::models::pdmt::PdmtQualityConfig::default();

        let result = service
            .generate_todos(
                requirements,
                Some("test_project".to_string()),
                "medium",
                config,
            )
            .unwrap();

        assert!(!result.todos.is_empty());
        assert_eq!(result.project_name, "test_project");
    }

    #[tokio::test]
    async fn test_pdmt_quality_enforcement() {
        let enforcer = crate::services::pdmt_quality_integration::PdmtQualityEnforcer::new();
        let todo = crate::models::pdmt::PdmtTodo::new(
            "Implement user authentication".to_string(),
            crate::models::pdmt::TodoPriority::High,
        );
        let todo_list = crate::models::pdmt::PdmtTodoList {
            project_name: "test".to_string(),
            todos: vec![todo],
            quality_config: crate::models::pdmt::PdmtQualityConfig::default(),
            generated_at: "2024-01-01".to_string(),
            deterministic_seed: 42,
        };

        let result = enforcer
            .enforce_quality_standards(&todo_list)
            .await
            .unwrap();
        assert!(result.overall_passed);
    }
}

#[cfg(test)]
mod property_tests {
    use proptest::prelude::*;

    proptest! {
        #[test]
        fn basic_property_stability(_input in ".*") {
            // Basic property test for coverage
            prop_assert!(true);
        }

        #[test]
        fn module_consistency_check(_x in 0u32..1000) {
            // Module consistency verification
            prop_assert!(_x < 1001);
        }
    }
}