use crate::terminal::sec_edgar::earnings::facts::{classify_fact_unit_family, FilingXbrlDocument};
use crate::terminal::types::AvailableXbrlConcept;
use std::collections::HashSet;

/// Extracts available XBRL concepts from a filing document.
///
/// Deduplicates concepts by (taxonomy, concept, unit_family) to avoid showing
/// the same concept multiple times in the dropdown when it appears with different
/// dimensions, segments, or contexts in the filing.
pub(crate) fn extract_available_concepts(
    document: &FilingXbrlDocument,
    period_id: &str,
) -> Vec<AvailableXbrlConcept> {
    // Use a set to deduplicate by (taxonomy, concept, unit_family)
    let mut unique_concepts: HashSet<(String, String, String)> = HashSet::new();
    let mut concepts = Vec::new();

    for fact in &document.facts {
        // Skip nil facts and text facts
        if fact.is_nil {
            continue;
        }
        if !matches!(
            fact.value,
            crate::terminal::sec_edgar::earnings::facts::FilingFactValue::Numeric(_)
        ) {
            continue;
        }

        // Extract taxonomy and concept name
        let (taxonomy, concept_name) = match (&fact.prefix, &fact.local_name) {
            (Some(prefix), local_name) => (prefix.clone(), local_name.clone()),
            (None, _) => continue,
        };

        // Classify unit family
        let unit_family = match classify_fact_unit_family(document, fact) {
            Some(uf) => uf.as_str(),
            None => continue,
        };

        // Create deduplication key
        let key = (
            taxonomy.clone(),
            concept_name.clone(),
            unit_family.to_string(),
        );

        // Only add if we haven't seen this combination before
        if unique_concepts.insert(key) {
            concepts.push(AvailableXbrlConcept {
                taxonomy,
                concept: concept_name,
                unit_family: unit_family.to_string(),
                label: None,
                period_id: period_id.to_string(),
                value: fact.numeric_value(),
            });
        }
    }

    concepts
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::terminal::sec_edgar::earnings::facts::{
        FilingContext, FilingFact, FilingFactValue, FilingUnit, FilingXbrlDocument,
    };
    use std::collections::HashMap;

    fn create_test_fact(prefix: &str, local_name: &str, value: f64, unit: &str) -> FilingFact {
        FilingFact {
            prefix: Some(prefix.to_string()),
            local_name: local_name.to_string(),
            value: FilingFactValue::Numeric(value),
            unit_ref: Some(unit.to_string()),
            is_nil: false,
            context_ref: Some("context".to_string()),
        }
    }

    fn create_test_document(facts: Vec<FilingFact>) -> FilingXbrlDocument {
        let mut contexts = HashMap::new();
        contexts.insert(
            "context".to_string(),
            FilingContext {
                entity_identifier: Some("test-entity".to_string()),
                period_start: Some("2023-01-01".to_string()),
                period_end: Some("2023-12-31".to_string()),
                instant: None,
                has_dimensions: false,
            },
        );

        let mut units = HashMap::new();
        units.insert(
            "USD".to_string(),
            FilingUnit {
                measures: vec!["iso4217:USD".to_string()],
            },
        );
        units.insert(
            "EUR".to_string(),
            FilingUnit {
                measures: vec!["iso4217:EUR".to_string()],
            },
        );
        units.insert(
            "shares".to_string(),
            FilingUnit {
                measures: vec!["xbrli:shares".to_string()],
            },
        );
        units.insert(
            "pure".to_string(),
            FilingUnit {
                measures: vec!["xbrli:pure".to_string()],
            },
        );

        FilingXbrlDocument {
            contexts,
            units,
            facts,
        }
    }

    #[test]
    fn test_deduplication_removes_duplicate_concepts() {
        let document = create_test_document(vec![
            // Same concept appearing multiple times
            create_test_fact("us-gaap", "Revenue", 1000.0, "USD"),
            create_test_fact("us-gaap", "Revenue", 2000.0, "USD"), // Duplicate
            create_test_fact("us-gaap", "Revenue", 3000.0, "USD"), // Duplicate
            create_test_fact("us-gaap", "NetIncome", 500.0, "USD"),
        ]);

        let concepts = extract_available_concepts(&document, "test-period");

        // Should only have 2 unique concepts (Revenue and NetIncome)
        assert_eq!(concepts.len(), 2);
        assert_eq!(concepts[0].concept, "Revenue");
        assert_eq!(concepts[1].concept, "NetIncome");
    }

    #[test]
    fn test_different_concepts_with_same_taxonomy_are_kept() {
        let document = create_test_document(vec![
            create_test_fact("us-gaap", "Revenue", 1000.0, "USD"),
            create_test_fact("us-gaap", "CostOfGoodsSold", 500.0, "USD"),
            create_test_fact("us-gaap", "GrossProfit", 500.0, "USD"),
        ]);

        let concepts = extract_available_concepts(&document, "test-period");

        // Should have all 3 concepts
        assert_eq!(concepts.len(), 3);
        assert_eq!(concepts[0].concept, "Revenue");
        assert_eq!(concepts[1].concept, "CostOfGoodsSold");
        assert_eq!(concepts[2].concept, "GrossProfit");
    }

    #[test]
    fn test_different_unit_families_create_separate_entries() {
        let document = create_test_document(vec![
            // Revenue with currency unit
            create_test_fact("us-gaap", "Revenue", 1000.0, "USD"),
            // Revenue with shares unit (different unit family)
            create_test_fact("us-gaap", "Revenue", 500.0, "shares"),
            // Revenue with pure unit (different unit family)
            create_test_fact("us-gaap", "Revenue", 1.5, "pure"),
        ]);

        let concepts = extract_available_concepts(&document, "test-period");

        // Should have 3 separate entries (one per unit family)
        assert_eq!(concepts.len(), 3);
        assert_eq!(concepts[0].unit_family, "currency");
        assert_eq!(concepts[1].unit_family, "shares");
        assert_eq!(concepts[2].unit_family, "pure");
    }

    #[test]
    fn test_empty_filing_returns_empty_list() {
        let document = create_test_document(vec![]);

        let concepts = extract_available_concepts(&document, "test-period");

        assert_eq!(concepts.len(), 0);
        assert!(concepts.is_empty());
    }

    #[test]
    fn test_nil_facts_are_skipped() {
        let document = create_test_document(vec![FilingFact {
            prefix: Some("us-gaap".to_string()),
            local_name: "Revenue".to_string(),
            value: FilingFactValue::Numeric(1000.0),
            unit_ref: Some("USD".to_string()),
            is_nil: true, // This should be skipped
            context_ref: Some("context".to_string()),
        }]);

        let concepts = extract_available_concepts(&document, "test-period");

        assert_eq!(concepts.len(), 0);
    }

    #[test]
    fn test_different_taxonomies_create_separate_entries() {
        let document = create_test_document(vec![
            create_test_fact("us-gaap", "Revenue", 1000.0, "USD"),
            create_test_fact("ifrs-full", "Revenue", 2000.0, "EUR"),
        ]);

        let concepts = extract_available_concepts(&document, "test-period");

        // Should have 2 separate entries (different taxonomies)
        assert_eq!(concepts.len(), 2);
        assert_eq!(concepts[0].taxonomy, "us-gaap");
        assert_eq!(concepts[1].taxonomy, "ifrs-full");
    }

    #[test]
    fn test_facts_without_prefix_are_skipped() {
        let document = create_test_document(vec![FilingFact {
            prefix: None, // No prefix, should be skipped
            local_name: "Revenue".to_string(),
            value: FilingFactValue::Numeric(1000.0),
            unit_ref: Some("USD".to_string()),
            is_nil: false,
            context_ref: Some("context".to_string()),
        }]);

        let concepts = extract_available_concepts(&document, "test-period");

        assert_eq!(concepts.len(), 0);
    }
}
