#![allow(clippy::unwrap_used)]
//! M0–M10 review pass 2, zone C regression: a creator SEARCH with a
//! CategoryEq/FormatEq filter must NOT be intersected against the ITEM-scoped
//! bitmap indexes.
//!
//! Before the fix, `apply_metadata_filter` (Stage 2) ran a `FilterEvaluator` over
//! the item category/format indexes and `candidates.retain(|id| bitmap.contains(id))`
//! against CREATOR ids — the wrong namespace. In a mixed DB only creators whose
//! id coincidentally aliased an item in that category survived; in a creator-only
//! DB every creator was dropped. The Stage 2b creator-metadata post-filter (an
//! AND-style retain) cannot resurrect candidates Stage 2 already removed, so a
//! documented feature ("filter creators by category") returned zero/arbitrary
//! results with no error. This test builds a DB with BOTH items (in category
//! "jazz") and creators (with category "news") and proves the matching creators
//! are returned.

use std::{collections::HashMap, time::Duration};

use tidaldb::{
    TidalDb,
    query::search::Search,
    schema::{DecaySpec, EntityId, EntityKind, SchemaBuilder, TextFieldType, Window},
    storage::indexes::filter::FilterExpr,
};

fn schema() -> tidaldb::schema::Schema {
    let mut builder = SchemaBuilder::new();
    let _ = builder
        .signal(
            "view",
            EntityKind::Item,
            DecaySpec::Exponential {
                half_life: Duration::from_secs(7 * 24 * 3600),
            },
        )
        .windows(&[Window::AllTime])
        .add();
    let _ = builder
        .signal(
            "follow",
            EntityKind::Creator,
            DecaySpec::Exponential {
                half_life: Duration::from_secs(30 * 24 * 3600),
            },
        )
        .windows(&[Window::AllTime])
        .add();
    // Creators are retrieved by BM25 over their "name" text field.
    builder.creator_text_field("name", TextFieldType::Text);
    builder.build().unwrap()
}

#[test]
fn creator_search_by_category_returns_matching_creators() {
    let db = TidalDb::builder()
        .ephemeral()
        .with_schema(schema())
        .open()
        .unwrap();

    // ITEMS in category "jazz" — these populate the item-scoped category index.
    // Their ids deliberately overlap the creator id range so a (buggy) intersection
    // against the item index would alias creator ids onto these item slots.
    for id in 1..=3u64 {
        let mut item_meta = HashMap::new();
        item_meta.insert("title".to_string(), format!("jazz track {id}"));
        item_meta.insert("category".to_string(), "jazz".to_string());
        db.write_item_with_metadata(EntityId::new(id), &item_meta)
            .unwrap();
    }

    // CREATORS with category metadata "news". Their names all contain "news" so
    // a BM25 query on "news" retrieves them.
    for id in 1..=3u64 {
        let mut creator_meta = HashMap::new();
        creator_meta.insert("name".to_string(), format!("news anchor {id}"));
        creator_meta.insert("category".to_string(), "news".to_string());
        db.write_creator(EntityId::new(id), &creator_meta).unwrap();
    }
    // One creator in a different category — must be excluded by the filter.
    let mut sports_meta = HashMap::new();
    sports_meta.insert("name".to_string(), "news roundup 99".to_string());
    sports_meta.insert("category".to_string(), "sports".to_string());
    db.write_creator(EntityId::new(99), &sports_meta).unwrap();

    db.flush_creator_text_index().unwrap();

    // SEARCH creators for "news" filtered to category "news". The matching news
    // creators (1, 2, 3) must be returned — NOT dropped by an item-index
    // intersection (the item category index holds only "jazz" item ids).
    let query = Search::builder()
        .entity_kind(EntityKind::Creator)
        .query("news")
        .filter(FilterExpr::eq("category", "news"))
        .limit(20)
        .build()
        .unwrap();

    let results = db.search(&query).unwrap();
    let mut ids: Vec<u64> = results.items.iter().map(|r| r.entity_id.as_u64()).collect();
    ids.sort_unstable();
    assert_eq!(
        ids,
        vec![1, 2, 3],
        "creators in category 'news' must be returned (not dropped by the item-index \
         intersection); the 'sports' creator 99 is correctly excluded by the metadata filter. \
         Got {ids:?}, warnings: {:?}",
        results.warnings
    );

    db.close().unwrap();
}
