use crate::context::Context;
use crate::events::Event;
use crate::tools::{get_global_registry, Tool, ToolCallback};

/// Import tool that looks up tools by name in the global registry and adds them to the context
pub struct ImportTool;

impl ToolCallback for ImportTool {
    fn handle(&self, event: &Event, context: Context) {
        if let Event::BlockStarted { meta_line } = event {
            let tool_names_text = meta_line.inline_text.content.trim();

            if !tool_names_text.is_empty() {
                // Split by comma and process each tool name
                let tool_names: Vec<&str> = tool_names_text
                    .split(',')
                    .map(|name| name.trim())
                    .filter(|name| !name.is_empty())
                    .collect();

                let global_registry = get_global_registry();
                let registry_guard = global_registry.lock().unwrap();

                for tool_name in tool_names {
                    if let Some(tool) = registry_guard.find_tool_by_name(tool_name) {
                        // Add the tool to the context's tools
                        match context.add_tool(tool.clone()) {
                            Ok(()) => {
                                println!("Imported tool: {tool_name}");
                            }
                            Err(e) => {
                                println!("Import warning: {e}");
                            }
                        }
                    } else {
                        println!("Warning: Tool '{tool_name}' not found in registry");
                    }
                }
            }
        }
    }

    fn as_any(&self) -> &dyn std::any::Any {
        self
    }
}

/// Create the import tool
pub fn create_import_tool() -> Result<Tool, String> {
    Tool::new(
        "import".to_string(),
        vec!["import".to_string()],
        Box::new(ImportTool),
    )
}

/// Build all standard library tools and return them as a vector
pub fn build_all_stdlib_tools() -> Result<Vec<Tool>, String> {
    let tools = vec![create_import_tool()?];
    Ok(tools)
}

/// Register all standard library tools in the global registry
/// Note: This function may create a circular dependency if called during
/// global registry initialization. Use register_stdlib_tools_to_registry instead.
pub fn register_stdlib_tools() -> Result<(), String> {
    let global_registry = get_global_registry();
    let mut registry_guard = global_registry.lock().unwrap();

    // Register the import tool
    let import_tool = create_import_tool()?;
    registry_guard.register(import_tool);

    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::context::Context;
    use crate::events::Event;
    use crate::parsers::wasot::{Id, MetaLine, ParsedArguments, QuotedString};

    #[test]
    fn test_create_import_tool() {
        let tool = create_import_tool().expect("Should create import tool successfully");
        assert_eq!(tool.name, "import");
        assert_eq!(tool.parsed_selectors.len(), 1);
        // Verify it has the import selector
        // Note: We can't easily test the selector matching here without more setup
    }

    #[test]
    fn test_build_all_stdlib_tools() {
        let tools = build_all_stdlib_tools().expect("Should build all stdlib tools successfully");
        assert_eq!(tools.len(), 1, "Should have exactly 1 stdlib tool (import)");
        assert_eq!(tools[0].name, "import");
    }

    #[test]
    fn test_import_tool_callback_creation() {
        let import_tool = ImportTool;
        // Test as_any method
        let any_ref = import_tool.as_any();
        assert!(any_ref.downcast_ref::<ImportTool>().is_some());
    }

    #[test]
    fn test_import_tool_handle_empty_inline_text() {
        let import_tool = ImportTool;
        let context = Context::new();

        // Create event with empty inline text
        let meta_line = MetaLine {
            level: 1,
            type_name: "import".to_string(),
            id: Id::new("".to_string()),
            arguments: ParsedArguments {
                args: vec![],
                kwargs: vec![],
            },
            inline_text: QuotedString::new("".to_string(), false),
            line_index: Some(1),
            is_void: false,
        };
        let event = Event::BlockStarted { meta_line };

        // Should not panic with empty inline text
        import_tool.handle(&event, context.clone());

        // No tools should be added since inline text is empty
        assert_eq!(context.tools().len(), 0);
    }

    #[test]
    fn test_import_tool_handle_whitespace_inline_text() {
        let import_tool = ImportTool;
        let context = Context::new();

        // Create event with whitespace-only inline text
        let meta_line = MetaLine {
            level: 1,
            type_name: "import".to_string(),
            id: Id::new("".to_string()),
            arguments: ParsedArguments {
                args: vec![],
                kwargs: vec![],
            },
            inline_text: QuotedString::new("   \t\n  ".to_string(), false),
            line_index: Some(1),
            is_void: false,
        };
        let event = Event::BlockStarted { meta_line };

        // Should not panic with whitespace
        import_tool.handle(&event, context.clone());

        // No tools should be added since trimmed inline text is empty
        assert_eq!(context.tools().len(), 0);
    }

    #[test]
    fn test_import_tool_handle_nonexistent_tool() {
        let import_tool = ImportTool;
        let context = Context::new();

        // Create event requesting a non-existent tool
        let meta_line = MetaLine {
            level: 1,
            type_name: "import".to_string(),
            id: Id::new("".to_string()),
            arguments: ParsedArguments {
                args: vec![],
                kwargs: vec![],
            },
            inline_text: QuotedString::new("nonexistent_tool".to_string(), false),
            line_index: Some(1),
            is_void: false,
        };
        let event = Event::BlockStarted { meta_line };

        // Should not panic when tool doesn't exist
        import_tool.handle(&event, context.clone());

        // No tools should be added since the requested tool doesn't exist
        assert_eq!(context.tools().len(), 0);
    }

    #[test]
    fn test_import_tool_handle_non_block_started_event() {
        let import_tool = ImportTool;
        let context = Context::new();

        // Test with BlockFinished event (should be ignored)
        let event = Event::BlockFinished;

        // Should not panic with non-BlockStarted events
        import_tool.handle(&event, context.clone());

        // No changes should occur
        assert_eq!(context.tools().len(), 0);
    }

    #[test]
    fn test_import_tool_handle_with_existing_tool_in_global_registry() {
        let import_tool = ImportTool;
        let context = Context::new();

        // First, add a tool to the global registry
        let global_registry = get_global_registry();
        {
            let mut registry_guard = global_registry.lock().unwrap();
            let test_tool = Tool::new(
                "test_tool".to_string(),
                vec!["test".to_string()],
                Box::new(TestToolCallback),
            )
            .expect("Should create test tool");
            registry_guard.register(test_tool);
        }

        // Create event requesting the tool we just registered
        let meta_line = MetaLine {
            level: 1,
            type_name: "import".to_string(),
            id: Id::new("".to_string()),
            arguments: ParsedArguments {
                args: vec![],
                kwargs: vec![],
            },
            inline_text: QuotedString::new("test_tool".to_string(), false),
            line_index: Some(1),
            is_void: false,
        };
        let event = Event::BlockStarted { meta_line };

        // Import the tool
        import_tool.handle(&event, context.clone());

        // The tool should now be in the context
        assert_eq!(context.tools().len(), 1);
        assert_eq!(context.tools()[0].name, "test_tool");
    }

    #[test]
    fn test_import_tool_handle_comma_separated_tools() {
        let import_tool = ImportTool;
        let context = Context::new();

        // First, add multiple tools to the global registry
        let global_registry = get_global_registry();
        {
            let mut registry_guard = global_registry.lock().unwrap();
            let test_tool1 = Tool::new(
                "tool1".to_string(),
                vec!["test1".to_string()],
                Box::new(TestToolCallback),
            )
            .expect("Should create test tool 1");
            let test_tool2 = Tool::new(
                "tool2".to_string(),
                vec!["test2".to_string()],
                Box::new(TestToolCallback),
            )
            .expect("Should create test tool 2");
            registry_guard.register(test_tool1);
            registry_guard.register(test_tool2);
        }

        // Create event requesting multiple tools with comma separation
        let meta_line = MetaLine {
            level: 1,
            type_name: "import".to_string(),
            id: Id::new("".to_string()),
            arguments: ParsedArguments {
                args: vec![],
                kwargs: vec![],
            },
            inline_text: QuotedString::new("tool1, tool2".to_string(), false),
            line_index: Some(1),
            is_void: false,
        };
        let event = Event::BlockStarted { meta_line };

        // Import the tools
        import_tool.handle(&event, context.clone());

        // Both tools should now be in the context
        assert_eq!(context.tools().len(), 2);
        let tool_names: Vec<String> = context.tools().iter().map(|t| t.name.clone()).collect();
        assert!(tool_names.contains(&"tool1".to_string()));
        assert!(tool_names.contains(&"tool2".to_string()));
    }

    #[test]
    fn test_import_tool_handle_comma_separated_with_spaces() {
        let import_tool = ImportTool;
        let context = Context::new();

        // Add test tools to the global registry
        let global_registry = get_global_registry();
        {
            let mut registry_guard = global_registry.lock().unwrap();
            let tool_a = Tool::new(
                "tool_a".to_string(),
                vec!["testa".to_string()],
                Box::new(TestToolCallback),
            )
            .expect("Should create test tool a");
            let tool_b = Tool::new(
                "tool_b".to_string(),
                vec!["testb".to_string()],
                Box::new(TestToolCallback),
            )
            .expect("Should create test tool b");
            registry_guard.register(tool_a);
            registry_guard.register(tool_b);
        }

        // Create event with various spacing patterns
        let meta_line = MetaLine {
            level: 1,
            type_name: "import".to_string(),
            id: Id::new("".to_string()),
            arguments: ParsedArguments {
                args: vec![],
                kwargs: vec![],
            },
            inline_text: QuotedString::new("  tool_a ,  tool_b  ".to_string(), false),
            line_index: Some(1),
            is_void: false,
        };
        let event = Event::BlockStarted { meta_line };

        // Import the tools
        import_tool.handle(&event, context.clone());

        // Both tools should be imported despite irregular spacing
        assert_eq!(context.tools().len(), 2);
        let tool_names: Vec<String> = context.tools().iter().map(|t| t.name.clone()).collect();
        assert!(tool_names.contains(&"tool_a".to_string()));
        assert!(tool_names.contains(&"tool_b".to_string()));
    }

    #[test]
    fn test_import_tool_handle_comma_separated_mixed_valid_invalid() {
        let import_tool = ImportTool;
        let context = Context::new();

        // Add only one tool to the global registry
        let global_registry = get_global_registry();
        {
            let mut registry_guard = global_registry.lock().unwrap();
            let valid_tool = Tool::new(
                "valid_tool".to_string(),
                vec!["valid".to_string()],
                Box::new(TestToolCallback),
            )
            .expect("Should create valid tool");
            registry_guard.register(valid_tool);
        }

        // Create event requesting both valid and invalid tools
        let meta_line = MetaLine {
            level: 1,
            type_name: "import".to_string(),
            id: Id::new("".to_string()),
            arguments: ParsedArguments {
                args: vec![],
                kwargs: vec![],
            },
            inline_text: QuotedString::new("valid_tool, nonexistent_tool".to_string(), false),
            line_index: Some(1),
            is_void: false,
        };
        let event = Event::BlockStarted { meta_line };

        // Import the tools
        import_tool.handle(&event, context.clone());

        // Only the valid tool should be imported
        assert_eq!(context.tools().len(), 1);
        assert_eq!(context.tools()[0].name, "valid_tool");
    }

    #[test]
    fn test_import_tool_handle_comma_separated_empty_items() {
        let import_tool = ImportTool;
        let context = Context::new();

        // Add test tool to the global registry
        let global_registry = get_global_registry();
        {
            let mut registry_guard = global_registry.lock().unwrap();
            let test_tool = Tool::new(
                "test_empty_tool".to_string(),
                vec!["test".to_string()],
                Box::new(TestToolCallback),
            )
            .expect("Should create test tool");
            registry_guard.register(test_tool);
        }

        // Create event with empty items in comma-separated list
        let meta_line = MetaLine {
            level: 1,
            type_name: "import".to_string(),
            id: Id::new("".to_string()),
            arguments: ParsedArguments {
                args: vec![],
                kwargs: vec![],
            },
            inline_text: QuotedString::new("test_empty_tool, , ,".to_string(), false),
            line_index: Some(1),
            is_void: false,
        };
        let event = Event::BlockStarted { meta_line };

        // Import the tools (should ignore empty items)
        import_tool.handle(&event, context.clone());

        // Only the valid tool should be imported (empty items filtered out)
        assert_eq!(context.tools().len(), 1);
        assert_eq!(context.tools()[0].name, "test_empty_tool");
    }

    #[test]
    fn test_import_tool_handle_duplicate_import() {
        let import_tool = ImportTool;
        let context = Context::new();

        // First, add a tool to the global registry
        let global_registry = get_global_registry();
        {
            let mut registry_guard = global_registry.lock().unwrap();
            let test_tool = Tool::new(
                "duplicate_test_tool".to_string(),
                vec!["test".to_string()],
                Box::new(TestToolCallback),
            )
            .expect("Should create test tool");
            registry_guard.register(test_tool);
        }

        // Create event requesting the tool
        let meta_line = MetaLine {
            level: 1,
            type_name: "import".to_string(),
            id: Id::new("".to_string()),
            arguments: ParsedArguments {
                args: vec![],
                kwargs: vec![],
            },
            inline_text: QuotedString::new("duplicate_test_tool".to_string(), false),
            line_index: Some(1),
            is_void: false,
        };
        let event = Event::BlockStarted {
            meta_line: meta_line.clone(),
        };

        // Import the tool twice
        import_tool.handle(&event, context.clone());
        let first_import_count = context.tools().len();

        let event2 = Event::BlockStarted { meta_line };
        import_tool.handle(&event2, context.clone());

        // Should still have only one instance (duplicate should be rejected)
        assert_eq!(context.tools().len(), first_import_count);
        assert_eq!(context.tools().len(), 1);
        assert_eq!(context.tools()[0].name, "duplicate_test_tool");
    }

    #[test]
    fn test_stdlib_tools_integration() {
        // Test that all stdlib tools can be built and have expected properties
        let tools = build_all_stdlib_tools().expect("Should build stdlib tools");

        // Verify import tool is present and configured correctly
        let import_tool = &tools[0];
        assert_eq!(import_tool.name, "import");

        // Test that import tool matches "import" blocks
        use crate::StackFrame;
        let stack = vec![StackFrame {
            metaline: MetaLine {
                level: 1,
                type_name: "import".to_string(),
                id: Id::new("".to_string()),
                arguments: ParsedArguments {
                    args: vec![],
                    kwargs: vec![],
                },
                inline_text: QuotedString::new("some_tool".to_string(), false),
                line_index: Some(1),
                is_void: false,
            },
            child_index: 0,
        }];

        assert!(
            import_tool.matches(&stack),
            "Import tool should match import blocks"
        );
    }

    #[test]
    fn test_register_stdlib_tools() {
        // This is harder to test since it modifies global state
        // But we can at least verify it doesn't panic
        let result = register_stdlib_tools();
        assert!(result.is_ok(), "Should register stdlib tools without error");

        // Verify the import tool is in the global registry
        let global_registry = get_global_registry();
        let registry_guard = global_registry.lock().unwrap();
        let all_tools = registry_guard.get_all_tools();

        let has_import_tool = all_tools.iter().any(|tool| tool.name == "import");
        assert!(
            has_import_tool,
            "Global registry should contain import tool after registration"
        );
    }

    // Helper test tool for testing import functionality
    struct TestToolCallback;

    impl ToolCallback for TestToolCallback {
        fn handle(&self, _event: &Event, _context: Context) {
            // Do nothing for test
        }

        fn as_any(&self) -> &dyn std::any::Any {
            self
        }
    }
}
