//! Advanced search and filtering for projects
//!
//! Provides search capabilities with filtering by phase, type, tags, and metadata.

#![allow(dead_code)]

use anyhow::Result;
use serde::{Deserialize, Serialize};

/// Search filter criteria
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct SearchFilter {
    /// Search query string
    #[serde(default)]
    pub query: Option<String>,
    /// Filter by phase
    #[serde(default)]
    pub phase: Option<String>,
    /// Filter by project type
    #[serde(default)]
    pub project_type: Option<String>,
    /// Filter by tags
    #[serde(default)]
    pub tags: Vec<String>,
    /// Filter by status
    #[serde(default)]
    pub status: Option<String>,
    /// Created after date (YYYY-MM-DD)
    #[serde(default)]
    pub created_after: Option<String>,
    /// Created before date (YYYY-MM-DD)
    #[serde(default)]
    pub created_before: Option<String>,
    /// Limit results
    #[serde(default = "default_limit")]
    pub limit: usize,
    /// Offset for pagination
    #[serde(default)]
    pub offset: usize,
    /// Sort field
    #[serde(default = "default_sort")]
    pub sort: String,
    /// Sort order (asc or desc)
    #[serde(default = "default_order")]
    pub order: String,
}

fn default_limit() -> usize {
    50
}

fn default_sort() -> String {
    "name".to_string()
}

fn default_order() -> String {
    "asc".to_string()
}

/// Search result
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SearchResult {
    /// Project identifier
    pub id: String,
    /// Project name
    pub name: String,
    /// Current phase
    pub phase: String,
    /// Project type
    pub project_type: String,
    /// Version
    pub version: String,
    /// Tags
    #[serde(default)]
    pub tags: Vec<String>,
    /// Relevance score (0-100)
    pub relevance: u32,
    /// Matching fields
    #[serde(default)]
    pub matched_fields: Vec<String>,
}

/// Search results with pagination info
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SearchResults {
    /// Search results
    pub results: Vec<SearchResult>,
    /// Total count matching criteria
    pub total: usize,
    /// Number of results returned
    pub returned: usize,
    /// Current offset
    pub offset: usize,
    /// Search filter used
    pub filter: SearchFilter,
}

/// Advanced search handler
pub struct SearchHandler;

impl SearchHandler {
    /// Execute search query
    pub fn search(filter: SearchFilter, projects: &[MockProject]) -> Result<SearchResults> {
        let mut results = Vec::new();

        for project in projects {
            let score = Self::calculate_relevance(project, &filter);
            if score > 0 {
                let result = SearchResult {
                    id: project.id.clone(),
                    name: project.name.clone(),
                    phase: project.phase.clone(),
                    project_type: project.project_type.clone(),
                    version: project.version.clone(),
                    tags: project.tags.clone(),
                    relevance: score,
                    matched_fields: Self::get_matched_fields(project, &filter),
                };
                results.push(result);
            }
        }

        // Sort results
        Self::sort_results(&mut results, &filter);

        // Calculate pagination
        let total = results.len();
        let offset = filter.offset;
        let limit = filter.limit;
        let returned = results.iter().skip(offset).take(limit).count();

        let paginated: Vec<SearchResult> = results.into_iter().skip(offset).take(limit).collect();

        Ok(SearchResults {
            results: paginated,
            total,
            returned,
            offset,
            filter,
        })
    }

    /// Calculate relevance score for a project
    fn calculate_relevance(project: &MockProject, filter: &SearchFilter) -> u32 {
        let mut score: u32 = 0;

        // Query matching
        if let Some(query) = &filter.query {
            let query_lower = query.to_lowercase();
            if project.name.to_lowercase().contains(&query_lower) {
                score += 30;
            }
            if project.description.to_lowercase().contains(&query_lower) {
                score += 20;
            }
        }

        // Phase filter
        if let Some(phase) = &filter.phase {
            if project.phase.eq_ignore_ascii_case(phase) {
                score += 25;
            }
        }

        // Type filter
        if let Some(proj_type) = &filter.project_type {
            if project.project_type.eq_ignore_ascii_case(proj_type) {
                score += 15;
            }
        }

        // Tag matching
        for tag in &filter.tags {
            if project.tags.iter().any(|t| t.eq_ignore_ascii_case(tag)) {
                score += 10;
            }
        }

        // Status filter
        if let Some(status) = &filter.status {
            if project.status.eq_ignore_ascii_case(status) {
                score += 20;
            }
        }

        // Date filters (simplified)
        if filter.created_after.is_some() || filter.created_before.is_some() {
            score += 5;
        }

        score
    }

    /// Get matched fields for a project
    fn get_matched_fields(project: &MockProject, filter: &SearchFilter) -> Vec<String> {
        let mut fields = Vec::new();

        if let Some(query) = &filter.query {
            let query_lower = query.to_lowercase();
            if project.name.to_lowercase().contains(&query_lower) {
                fields.push("name".to_string());
            }
            if project.description.to_lowercase().contains(&query_lower) {
                fields.push("description".to_string());
            }
        }

        if filter.phase.is_some() {
            fields.push("phase".to_string());
        }

        if filter.project_type.is_some() {
            fields.push("project_type".to_string());
        }

        fields
    }

    /// Sort results based on filter
    fn sort_results(results: &mut [SearchResult], filter: &SearchFilter) {
        let sort_field = &filter.sort;
        let is_desc = filter.order.eq_ignore_ascii_case("desc");

        match sort_field.as_str() {
            "relevance" => {
                if is_desc {
                    results.sort_by(|a, b| b.relevance.cmp(&a.relevance));
                } else {
                    results.sort_by(|a, b| a.relevance.cmp(&b.relevance));
                }
            }
            "name" => {
                if is_desc {
                    results.sort_by(|a, b| b.name.cmp(&a.name));
                } else {
                    results.sort_by(|a, b| a.name.cmp(&b.name));
                }
            }
            "phase" => {
                if is_desc {
                    results.sort_by(|a, b| b.phase.cmp(&a.phase));
                } else {
                    results.sort_by(|a, b| a.phase.cmp(&b.phase));
                }
            }
            _ => {
                results.sort_by(|a, b| b.relevance.cmp(&a.relevance));
            }
        }
    }

    /// Parse search query string
    pub fn parse_query(query: &str) -> SearchFilter {
        let mut filter = SearchFilter::default();

        let parts: Vec<&str> = query.split_whitespace().collect();
        let mut remaining = Vec::new();

        for part in parts {
            if let Some(phase) = part.strip_prefix("phase:") {
                filter.phase = Some(phase.to_string());
            } else if let Some(project_type) = part.strip_prefix("type:") {
                filter.project_type = Some(project_type.to_string());
            } else if let Some(tag) = part.strip_prefix("tag:") {
                filter.tags.push(tag.to_string());
            } else if let Some(status) = part.strip_prefix("status:") {
                filter.status = Some(status.to_string());
            } else {
                remaining.push(part);
            }
        }

        if !remaining.is_empty() {
            filter.query = Some(remaining.join(" "));
        }

        filter
    }
}

/// Mock project for testing
#[derive(Debug, Clone)]
pub struct MockProject {
    pub id: String,
    pub name: String,
    pub description: String,
    pub phase: String,
    pub project_type: String,
    pub version: String,
    pub status: String,
    pub tags: Vec<String>,
}

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

    fn create_test_project(name: &str, phase: &str) -> MockProject {
        MockProject {
            id: format!("proj-{}", name),
            name: name.to_string(),
            description: format!("Description for {}", name),
            phase: phase.to_string(),
            project_type: "web".to_string(),
            version: "1.0.0".to_string(),
            status: "active".to_string(),
            tags: vec!["test".to_string()],
        }
    }

    #[test]
    fn test_search_filter_defaults() {
        let filter = SearchFilter {
            query: None,
            phase: None,
            project_type: None,
            tags: Vec::new(),
            status: None,
            created_after: None,
            created_before: None,
            limit: default_limit(),
            offset: 0,
            sort: default_sort(),
            order: default_order(),
        };
        assert_eq!(filter.limit, 50);
        assert_eq!(filter.sort, "name");
        assert_eq!(filter.order, "asc");
    }

    #[test]
    fn test_parse_query_phase() {
        let filter = SearchHandler::parse_query("phase:design");
        assert_eq!(filter.phase, Some("design".to_string()));
    }

    #[test]
    fn test_parse_query_combined() {
        let filter = SearchHandler::parse_query("my project phase:design type:web");
        assert_eq!(filter.phase, Some("design".to_string()));
        assert_eq!(filter.project_type, Some("web".to_string()));
        assert!(filter.query.is_some());
    }

    #[test]
    fn test_relevance_calculation() {
        let project = create_test_project("test-proj", "design");
        let filter = SearchFilter {
            query: Some("test".to_string()),
            phase: Some("design".to_string()),
            ..Default::default()
        };

        let score = SearchHandler::calculate_relevance(&project, &filter);
        assert!(score > 0);
    }

    #[test]
    fn test_search_results_sorting() {
        let filter = SearchFilter {
            query: Some("test".to_string()),
            sort: "name".to_string(),
            order: "asc".to_string(),
            ..Default::default()
        };

        let projects = vec![
            create_test_project("zebra", "design"),
            create_test_project("apple", "design"),
            create_test_project("mango", "design"),
        ];

        let results = SearchHandler::search(filter, &projects);
        assert!(results.is_ok());
    }

    #[test]
    fn test_search_with_limit() {
        let filter = SearchFilter {
            limit: 2,
            ..Default::default()
        };

        let projects = vec![
            create_test_project("proj1", "design"),
            create_test_project("proj2", "design"),
            create_test_project("proj3", "design"),
        ];

        let results = SearchHandler::search(filter, &projects).unwrap();
        assert!(results.returned <= 2);
    }
}
