//! Edge-case integration tests — exercises parser, RefResolver, Store, and element logic
//! against purpose-built fixture files that stress corner cases.

use chrono::NaiveDate;
use indexmap::IndexMap;
use mps::{elements::*, parser, ref_resolver::RefResolver, store::Store};
use std::collections::HashMap;
use std::path::{Path, PathBuf};

// ── Helpers ──────────────────────────────────────────────────────────────────

fn fixtures_dir() -> PathBuf {
    Path::new(env!("CARGO_MANIFEST_DIR"))
        .join("tests")
        .join("fixtures")
}

fn fixture_path(name: &str) -> PathBuf {
    fixtures_dir().join(name)
}

fn parse_fixture(name: &str) -> IndexMap<String, Element> {
    parser::parse_file(&fixture_path(name)).expect("fixture parse failed")
}

fn non_root(els: &IndexMap<String, Element>) -> Vec<&Element> {
    els.values()
        .filter(|e| !e.is_mps_group() && !e.is_unknown())
        .collect()
}

fn fixture_store() -> (tempfile::TempDir, Store) {
    let dir = tempfile::tempdir().unwrap();
    for entry in std::fs::read_dir(fixtures_dir()).unwrap() {
        let entry = entry.unwrap();
        let dst = dir.path().join(entry.file_name());
        std::fs::copy(entry.path(), dst).unwrap();
    }
    let store = Store::new(dir.path());
    (dir, store)
}

fn make_date(y: i32, m: u32, d: u32) -> NaiveDate {
    NaiveDate::from_ymd_opt(y, m, d).unwrap()
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 1: Empty bodies and empty-bracket elements
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_empty_body_task_is_parsed() {
    // @task[]{} — empty body, empty brackets
    let els = parse_fixture("20260301.1000000100.mps");
    assert!(
        els.values().any(|e| e.kind() == ElementKind::Task),
        "should parse task with empty body"
    );
}

#[test]
fn test_empty_body_body_str_is_empty_or_whitespace() {
    let els = parse_fixture("20260301.1000000100.mps");
    let task = els
        .values()
        .find(|e| e.kind() == ElementKind::Task)
        .unwrap();
    assert!(
        task.body_str().trim().is_empty(),
        "empty-body task body should be empty"
    );
}

#[test]
fn test_empty_body_log_has_duration() {
    // @log[start: 09:00, end: 10:00]{} — duration should still be computed
    let els = parse_fixture("20260301.1000000100.mps");
    let log = els.values().find(|e| e.kind() == ElementKind::Log).unwrap();
    if let Element::Log { data, .. } = log {
        assert_eq!(data.duration_minutes(), Some(60), "09:00–10:00 = 60 min");
        assert_eq!(data.duration_str(), Some("1h".to_string()));
    } else {
        panic!("expected Log");
    }
}

#[test]
fn test_empty_body_reminder_at_noon() {
    let els = parse_fixture("20260301.1000000100.mps");
    let rem = els
        .values()
        .find(|e| e.kind() == ElementKind::Reminder)
        .unwrap();
    if let Element::Reminder { data, .. } = rem {
        assert_eq!(data.at.as_deref(), Some("noon"));
    } else {
        panic!("expected Reminder");
    }
}

#[test]
fn test_empty_body_note_has_no_tags() {
    let els = parse_fixture("20260301.1000000100.mps");
    let note = els
        .values()
        .find(|e| e.kind() == ElementKind::Note)
        .unwrap();
    assert!(note.tags().is_empty(), "bare @note has no tags");
}

#[test]
fn test_empty_bracket_task_raw_args_is_empty() {
    // @task[]{} — empty bracket → raw_args should be ""
    let els = parse_fixture("20260301.1000000100.mps");
    let task = els
        .values()
        .find(|e| e.kind() == ElementKind::Task)
        .unwrap();
    assert_eq!(
        task.raw_args(),
        "",
        "empty bracket → raw_args is empty string"
    );
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 2: Deep nesting — 4 levels
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_deep_nesting_4_levels_parsed() {
    let els = parse_fixture("20260302.1000000101.mps");
    // Structure: root_mps → mps.1 → mps.1.1 → mps.1.1.1 → task.1.1.1.1
    // base_ref = 20260302
    assert!(els.contains_key("20260302.1"), "level-1 mps block");
    assert!(els.contains_key("20260302.1.1"), "level-2 mps block");
    assert!(els.contains_key("20260302.1.1.1"), "level-3 mps block");
    assert!(els.contains_key("20260302.1.1.1.1"), "level-4 task");
    assert!(els.contains_key("20260302.1.1.1.2"), "level-4 note");
    assert!(els.contains_key("20260302.1.1.2"), "level-3 task");
    assert!(els.contains_key("20260302.1.2"), "level-2 task");
}

#[test]
fn test_deep_nesting_top_level_tasks() {
    let els = parse_fixture("20260302.1000000101.mps");
    let top_task = els.get("20260302.2").expect("top-level task at .2");
    assert_eq!(top_task.kind(), ElementKind::Task);
    assert!(top_task.body_str().contains("Top level outer"));
}

#[test]
fn test_deep_nesting_ref_resolver_4_levels() {
    let els = parse_fixture("20260302.1000000101.mps");
    let r = RefResolver::new(&els);
    // mps-1 is the outermost @mps block
    assert_eq!(r.to_human("20260302.1"), Some("mps-1"));
    // task inside mps-1 at position 2 = mps-1.2
    assert_eq!(r.to_human("20260302.1.2"), Some("mps-1.2"));
    // mps block at position 1 inside mps-1 = mps-1.1 (mps-2 if top-level counting, but it's nested)
    // Actually mps blocks are counted as "mps-N" at top level but nested as parent.N
    assert_eq!(r.to_human("20260302.1.1"), Some("mps-1.1"));
    assert_eq!(r.to_human("20260302.1.1.1"), Some("mps-1.1.1"));
    assert_eq!(r.to_human("20260302.1.1.1.1"), Some("mps-1.1.1.1"));
}

#[test]
fn test_deep_nesting_5_levels() {
    let els = parse_fixture("20260313.1000000112.mps");
    // root → mps.1 → mps.1.1 → mps.1.1.1 → mps.1.1.1.1 → task.1.1.1.1.1
    assert!(els.contains_key("20260313.1.1.1.1.1"), "5-level task ref");
    let deep = els.get("20260313.1.1.1.1.1").unwrap();
    assert!(deep.body_str().contains("Five levels deep"));
}

#[test]
fn test_deep_nesting_ref_resolver_5_levels() {
    let els = parse_fixture("20260313.1000000112.mps");
    let r = RefResolver::new(&els);
    assert_eq!(r.to_human("20260313.1.1.1.1.1"), Some("mps-1.1.1.1.1"));
    // Roundtrip
    assert_eq!(r.to_epoch("mps-1.1.1.1.1"), Some("20260313.1.1.1.1.1"));
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 3: Many same-type elements — counter correctness
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_many_same_type_5_tasks() {
    let els = parse_fixture("20260303.1000000102.mps");
    let task_count = els
        .values()
        .filter(|e| e.kind() == ElementKind::Task)
        .count();
    assert_eq!(task_count, 5, "should have exactly 5 tasks");
}

#[test]
fn test_many_same_type_3_notes() {
    let els = parse_fixture("20260303.1000000102.mps");
    let note_count = els
        .values()
        .filter(|e| e.kind() == ElementKind::Note)
        .count();
    assert_eq!(note_count, 3);
}

#[test]
fn test_many_same_type_ref_resolver_counters() {
    let els = parse_fixture("20260303.1000000102.mps");
    let r = RefResolver::new(&els);
    assert_eq!(
        r.to_epoch("task-5"),
        Some("20260303.5"),
        "5th task maps to .5"
    );
    assert_eq!(
        r.to_epoch("note-3"),
        Some("20260303.8"),
        "3rd note maps to .8 (after 5 tasks)"
    );
    assert_eq!(
        r.to_epoch("log-2"),
        Some("20260303.10"),
        "2nd log maps to .10"
    );
}

#[test]
fn test_many_same_type_done_count() {
    let els = parse_fixture("20260303.1000000102.mps");
    let done = els
        .values()
        .filter_map(|e| {
            if let Element::Task { data, .. } = e {
                Some(data)
            } else {
                None
            }
        })
        .filter(|d| d.is_done())
        .count();
    assert_eq!(done, 2, "tasks 4 and 5 are done");
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 4: Tags with hyphens, underscores, and numbers
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_hyphen_tags_parsed_correctly() {
    let els = parse_fixture("20260304.1000000103.mps");
    let task = els
        .values()
        .find(|e| e.kind() == ElementKind::Task)
        .unwrap();
    let tags = task.tags();
    assert!(
        tags.contains(&"mps-dev".to_string()),
        "hyphenated tag mps-dev"
    );
    assert!(
        tags.contains(&"work_item".to_string()),
        "underscore tag work_item"
    );
    assert!(tags.contains(&"v2".to_string()), "alphanumeric tag v2");
}

#[test]
fn test_hyphen_tag_in_log() {
    let els = parse_fixture("20260304.1000000103.mps");
    let log = els.values().find(|e| e.kind() == ElementKind::Log).unwrap();
    assert!(
        log.tags().contains(&"feature-123".to_string()),
        "feature-123 tag"
    );
    if let Element::Log { data, .. } = log {
        assert_eq!(
            data.duration_str(),
            Some("2h30m".to_string()),
            "09:00–11:30 = 2h30m"
        );
    }
}

#[test]
fn test_hyphen_tag_in_reminder() {
    let els = parse_fixture("20260304.1000000103.mps");
    let rem = els
        .values()
        .find(|e| e.kind() == ElementKind::Reminder)
        .unwrap();
    assert!(
        rem.tags().contains(&"daily-standup".to_string()),
        "hyphenated reminder tag"
    );
    if let Element::Reminder { data, .. } = rem {
        assert_eq!(data.at.as_deref(), Some("3pm"));
    }
}

#[test]
fn test_hyphen_tags_in_store_search() {
    let (_dir, store) = fixture_store();
    let results = store.search("", None, Some("mps-dev"), None).unwrap();
    assert!(
        !results.is_empty(),
        "search by hyphenated tag should find results"
    );
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 5: Log duration edge cases
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_log_near_full_day_duration() {
    let els = parse_fixture("20260305.1000000104.mps");
    // 00:00 to 23:59 = 1439 min = 23h59m
    let log = els
        .values()
        .filter_map(|e| {
            if let Element::Log { data, .. } = e {
                Some(data)
            } else {
                None
            }
        })
        .find(|d| d.start.as_deref() == Some("00:00"))
        .unwrap();
    assert_eq!(log.duration_minutes(), Some(1439));
    assert_eq!(log.duration_str(), Some("23h59m".to_string()));
}

#[test]
fn test_log_zero_duration() {
    let els = parse_fixture("20260305.1000000104.mps");
    let log = els
        .values()
        .filter_map(|e| {
            if let Element::Log { data, .. } = e {
                Some(data)
            } else {
                None
            }
        })
        .find(|d| d.start.as_deref() == Some("08:00") && d.end.as_deref() == Some("08:00"))
        .unwrap();
    assert_eq!(log.duration_minutes(), Some(0));
    assert_eq!(log.duration_str(), None, "zero duration → no duration_str");
}

#[test]
fn test_log_only_start_no_duration() {
    let els = parse_fixture("20260305.1000000104.mps");
    let log = els
        .values()
        .filter_map(|e| {
            if let Element::Log { data, .. } = e {
                Some(data)
            } else {
                None
            }
        })
        .find(|d| d.start.is_some() && d.end.is_none())
        .unwrap();
    assert_eq!(log.duration_minutes(), None);
    assert_eq!(log.duration_str(), None);
}

#[test]
fn test_log_only_end_no_duration() {
    let els = parse_fixture("20260305.1000000104.mps");
    let log = els
        .values()
        .filter_map(|e| {
            if let Element::Log { data, .. } = e {
                Some(data)
            } else {
                None
            }
        })
        .find(|d| d.start.is_none() && d.end.is_some())
        .unwrap();
    assert_eq!(log.duration_minutes(), None);
}

#[test]
fn test_log_45_minutes() {
    let els = parse_fixture("20260305.1000000104.mps");
    // 09:30 to 10:15 = 45 min
    let log = els
        .values()
        .filter_map(|e| {
            if let Element::Log { data, .. } = e {
                Some(data)
            } else {
                None
            }
        })
        .find(|d| d.start.as_deref() == Some("09:30"))
        .unwrap();
    assert_eq!(log.duration_minutes(), Some(45));
    assert_eq!(log.duration_str(), Some("0h45m".to_string()));
}

#[test]
fn test_log_no_times_no_duration() {
    let els = parse_fixture("20260305.1000000104.mps");
    let log = els
        .values()
        .filter_map(|e| {
            if let Element::Log { data, .. } = e {
                Some(data)
            } else {
                None
            }
        })
        .find(|d| d.start.is_none() && d.end.is_none() && d.tags.contains(&"work".to_string()))
        .unwrap();
    assert_eq!(log.duration_minutes(), None);
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 6: Mixed unknown elements
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_unknown_elements_are_parsed_as_unknown() {
    let els = parse_fixture("20260306.1000000105.mps");
    let unknowns: Vec<_> = els.values().filter(|e| e.is_unknown()).collect();
    assert_eq!(
        unknowns.len(),
        4,
        "widget, gadget, sprocket, thingamajig — 4 unknowns"
    );
}

#[test]
fn test_unknown_elements_counted_correctly() {
    let els = parse_fixture("20260306.1000000105.mps");
    let unknowns: Vec<_> = els.values().filter(|e| e.is_unknown()).collect();
    // widget, gadget, sprocket, thingamajig
    assert_eq!(unknowns.len(), 4);
    let signs: Vec<&str> = unknowns.iter().map(|e| e.sign()).collect();
    assert!(signs.contains(&"widget"));
    assert!(signs.contains(&"gadget"));
    assert!(signs.contains(&"thingamajig"));
}

#[test]
fn test_unknown_elements_excluded_from_non_root() {
    let els = parse_fixture("20260306.1000000105.mps");
    let visible = non_root(&els);
    // Only task, note, log are visible — 3 known elements
    assert_eq!(visible.len(), 3);
    assert!(visible.iter().all(|e| !e.is_unknown()));
}

#[test]
fn test_unknown_elements_ref_resolver_skips_them() {
    let els = parse_fixture("20260306.1000000105.mps");
    let r = RefResolver::new(&els);
    // widget is at .1, task at .2 → task should be task-1 (not task-2)
    assert!(r.to_human("20260306.1").is_none(), "widget (.1) skipped");
    assert_eq!(
        r.to_human("20260306.2"),
        Some("task-1"),
        "task at .2 becomes task-1"
    );
    assert!(r.to_human("20260306.3").is_none(), "gadget (.3) skipped");
    assert_eq!(
        r.to_human("20260306.4"),
        Some("note-1"),
        "note at .4 becomes note-1"
    );
    assert!(r.to_human("20260306.5").is_none(), "sprocket (.5) skipped");
    assert_eq!(
        r.to_human("20260306.6"),
        Some("log-1"),
        "log at .6 becomes log-1"
    );
    assert!(
        r.to_human("20260306.7").is_none(),
        "thingamajig (.7) skipped"
    );
}

#[test]
fn test_unknown_elements_excluded_from_store_search() {
    let (_dir, store) = fixture_store();
    // Search for "Unknown" which appears in the unknown elements' body
    let results = store.search("Unknown element", None, None, None).unwrap();
    assert!(
        results.iter().all(|r| !r.element.is_unknown()),
        "search should never return unknown elements"
    );
}

#[test]
fn test_sprocket_has_own_raw_args() {
    let els = parse_fixture("20260306.1000000105.mps");
    let sprocket = els.values().find(|e| e.sign() == "sprocket").unwrap();
    assert_eq!(sprocket.raw_args(), "cog, status: spinning");
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 7: Unicode and special chars in body
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_unicode_body_preserved() {
    let els = parse_fixture("20260307.1000000106.mps");
    let note = els
        .values()
        .find(|e| e.kind() == ElementKind::Note)
        .unwrap();
    assert!(
        note.body_str().contains("日本語テキスト"),
        "CJK text preserved"
    );
    assert!(note.body_str().contains("🎯"), "emoji preserved");
    assert!(note.body_str().contains("مرحبا"), "Arabic text preserved");
}

#[test]
fn test_at_sign_in_body_not_parsed_as_element() {
    let els = parse_fixture("20260307.1000000106.mps");
    // @john.doe and @example.com in body should NOT create unknown elements
    // (because they lack the { that at_regexp requires)
    let unknowns: Vec<_> = els.values().filter(|e| e.is_unknown()).collect();
    assert!(
        unknowns.is_empty(),
        "@ without {{ should not create elements"
    );
    // Total: root mps + note + task + log = 4 elements
    assert_eq!(els.len(), 4);
}

#[test]
fn test_dollar_in_body_does_not_break_parsing() {
    let els = parse_fixture("20260307.1000000106.mps");
    let log = els.values().find(|e| e.kind() == ElementKind::Log).unwrap();
    assert!(
        log.body_str().contains("$50.00"),
        "dollar sign preserved in body"
    );
    assert!(
        log.body_str().contains("$1,200"),
        "dollar sign with comma preserved"
    );
}

#[test]
fn test_unicode_search() {
    let (_dir, store) = fixture_store();
    let results = store.search("日本語", None, None, None).unwrap();
    assert!(!results.is_empty(), "should find CJK query");
    assert!(results[0].element.body_str().contains("日本語テキスト"));
}

#[test]
fn test_special_chars_in_body_dont_affect_count() {
    let els = parse_fixture("20260307.1000000106.mps");
    // root mps + note + task + log = 4 elements
    assert_eq!(
        els.len(),
        4,
        "special body chars should not create extra elements"
    );
}

#[test]
fn test_rewrite_element_with_dollar_in_body() {
    // Ensure rewrite_element works when the log element has $ in its body
    let (dir, store) = fixture_store();
    let date = make_date(2026, 3, 7);
    let mut new_attrs = HashMap::new();
    new_attrs.insert("start".to_string(), "10:00".to_string());
    // The log at 20260307 already has start:10:00 end:11:00 — update end time
    new_attrs.insert("end".to_string(), "12:00".to_string());
    // Should not panic even though body has $ signs
    let result = store.rewrite_element("log-1", &new_attrs, date);
    assert!(result.is_ok(), "rewrite with $ in body should not error");
    drop(dir);
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 8: Duplicate raw_args — which element gets rewritten?
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_duplicate_raw_args_file_parses_all_tasks() {
    let els = parse_fixture("20260308.1000000107.mps");
    let tasks: Vec<_> = els
        .values()
        .filter(|e| e.kind() == ElementKind::Task)
        .collect();
    assert_eq!(tasks.len(), 5, "5 tasks total");
}

#[test]
fn test_duplicate_raw_args_ref_resolver_assigns_unique_refs() {
    let els = parse_fixture("20260308.1000000107.mps");
    let r = RefResolver::new(&els);
    // All 5 tasks should get unique human refs
    assert_eq!(r.to_epoch("task-1"), Some("20260308.1"));
    assert_eq!(r.to_epoch("task-2"), Some("20260308.2"));
    assert_eq!(r.to_epoch("task-3"), Some("20260308.3"));
    assert_eq!(r.to_epoch("task-4"), Some("20260308.4"));
    assert_eq!(r.to_epoch("task-5"), Some("20260308.5"));
}

#[test]
fn test_rewrite_first_of_duplicate_raw_args() {
    // task-1 and task-2 both have raw_args "work, status: open".
    // Rewriting task-2 (epoch 20260308.2) by epoch ref should update the SECOND occurrence.
    let (dir, store) = fixture_store();
    let date = make_date(2026, 3, 8);
    let mut new_attrs = HashMap::new();
    new_attrs.insert("status".to_string(), "done".to_string());

    // Update by epoch ref (unambiguous)
    let ok = store
        .rewrite_element("20260308.1", &new_attrs, date)
        .unwrap();
    assert!(ok, "rewrite by epoch ref should succeed");

    // Read back and verify: task-1 body should now show status:done
    let file = store.find_file(date).unwrap();
    let content = std::fs::read_to_string(&file).unwrap();
    // The first task body is "First open work task"
    // After rewrite, "First open work task" should be in a done task context
    assert!(content.contains("status: done"), "first task now done");
    drop(dir);
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 9: Complex nested structures and RefResolver
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_complex_nested_all_elements_parsed() {
    let els = parse_fixture("20260309.1000000108.mps");
    // root mps + mps[project-alpha] + 2 tasks in mps + mps[sub-milestone] + task in sub + note in sub
    // + note[retrospective] + log = 9 elements
    assert!(
        els.len() >= 8,
        "should have at least 8 elements, got {}",
        els.len()
    );
}

#[test]
fn test_complex_nested_ref_resolver_3_levels() {
    let els = parse_fixture("20260309.1000000108.mps");
    let r = RefResolver::new(&els);
    // project-alpha mps block = mps-1
    assert_eq!(r.to_human("20260309.1"), Some("mps-1"));
    // First task inside mps-1 = mps-1.1
    assert_eq!(r.to_human("20260309.1.1"), Some("mps-1.1"));
    // sub-milestone mps block = mps-1.3
    assert_eq!(r.to_human("20260309.1.3"), Some("mps-1.3"));
    // task inside sub-milestone = mps-1.3.1
    assert_eq!(r.to_human("20260309.1.3.1"), Some("mps-1.3.1"));
    // note inside sub-milestone = mps-1.3.2
    assert_eq!(r.to_human("20260309.1.3.2"), Some("mps-1.3.2"));
}

#[test]
fn test_complex_nested_top_level_elements() {
    let els = parse_fixture("20260309.1000000108.mps");
    let r = RefResolver::new(&els);
    // note[retrospective] is after the mps block → note-1
    assert_eq!(r.to_human("20260309.2"), Some("note-1"));
    // log is after note → log-1
    assert_eq!(r.to_human("20260309.3"), Some("log-1"));
}

#[test]
fn test_complex_nested_log_full_day() {
    let els = parse_fixture("20260309.1000000108.mps");
    let log = els.values().find(|e| e.kind() == ElementKind::Log).unwrap();
    if let Element::Log { data, .. } = log {
        // 09:00 to 17:30 = 510 minutes = 8h30m
        assert_eq!(data.duration_minutes(), Some(510));
        assert_eq!(data.duration_str(), Some("8h30m".to_string()));
    }
}

#[test]
fn test_complex_nested_rewrite_nested_task_by_human_ref() {
    let (dir, store) = fixture_store();
    let date = make_date(2026, 3, 9);
    let mut new_attrs = HashMap::new();
    new_attrs.insert("status".to_string(), "done".to_string());
    // mps-1.3.1 is the task inside the sub-milestone
    let ok = store
        .rewrite_element("mps-1.3.1", &new_attrs, date)
        .unwrap();
    assert!(ok, "should rewrite a 3-level-deep task by human ref");
    let file = store.find_file(date).unwrap();
    let content = std::fs::read_to_string(&file).unwrap();
    assert!(
        content.contains("Write API integration tests"),
        "body preserved"
    );
    drop(dir);
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 10: Epoch-less filenames
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_epoch_less_file_included_in_all_files() {
    let (_dir, store) = fixture_store();
    let files = store.all_files().unwrap();
    let has_epoch_less = files
        .iter()
        .any(|p| p.file_name().and_then(|n| n.to_str()) == Some("20260310.mps"));
    assert!(has_epoch_less, "epoch-less file should be in all_files()");
}

#[test]
fn test_epoch_less_file_found_by_find_file() {
    let (_dir, store) = fixture_store();
    let date = make_date(2026, 3, 10);
    let found = store.find_file(date);
    assert!(found.is_some(), "find_file should find epoch-less file");
    assert!(found.unwrap().to_str().unwrap().ends_with("20260310.mps"));
}

#[test]
fn test_epoch_less_file_parses_elements() {
    let els = parse_fixture("20260310.mps");
    // base_ref extracted from basename: "20260310.mps"[..8] = "20260310" → 20260310
    assert!(els.values().any(|e| e.kind() == ElementKind::Task));
    assert!(els.values().any(|e| e.kind() == ElementKind::Note));
}

#[test]
fn test_epoch_less_file_date_in_all_file_dates() {
    let (_dir, store) = fixture_store();
    let dates = store.all_file_dates().unwrap();
    let d = make_date(2026, 3, 10);
    assert!(
        dates.contains(&d),
        "epoch-less file date appears in all_file_dates"
    );
}

#[test]
fn test_epoch_less_file_parse_date() {
    let (_dir, store) = fixture_store();
    let date = make_date(2026, 3, 10);
    let els = store.parse_date(date).unwrap();
    assert!(els.values().any(|e| e.kind() == ElementKind::Task));
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 11: Whitespace in args — trimming robustness
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_whitespace_args_task_tag_trimmed() {
    let els = parse_fixture("20260311.1000000110.mps");
    // @task[  work  ,   status: open  ]
    let tasks: Vec<_> = els
        .values()
        .filter(|e| e.kind() == ElementKind::Task)
        .collect();
    assert!(
        tasks.iter().any(|t| t.tags().contains(&"work".to_string())),
        "tag 'work' should be trimmed of spaces"
    );
}

#[test]
fn test_whitespace_args_task_status_trimmed() {
    let els = parse_fixture("20260311.1000000110.mps");
    // @task[  work  ,   status: open  ] — status should be parsed as "open"
    let tasks: Vec<_> = els
        .values()
        .filter_map(|e| {
            if let Element::Task { data, .. } = e {
                Some(data)
            } else {
                None
            }
        })
        .collect();
    assert!(
        tasks.iter().any(|d| d.is_open()),
        "status: open with spaces should parse correctly"
    );
}

#[test]
fn test_whitespace_only_args_treated_as_no_args() {
    let els = parse_fixture("20260311.1000000110.mps");
    // @note[   ] — whitespace-only args should yield no tags, no attrs
    let note = els
        .values()
        .find(|e| e.kind() == ElementKind::Note)
        .unwrap();
    assert!(note.tags().is_empty(), "whitespace-only args → no tags");
}

#[test]
fn test_whitespace_log_times_trimmed() {
    let els = parse_fixture("20260311.1000000110.mps");
    // @log[ start: 09:00 , end: 10:00 ] — times should still parse
    let log = els.values().find(|e| e.kind() == ElementKind::Log).unwrap();
    if let Element::Log { data, .. } = log {
        assert_eq!(
            data.duration_minutes(),
            Some(60),
            "09:00–10:00 = 60 min even with spaces"
        );
    }
}

#[test]
fn test_whitespace_args_reversed_order() {
    let els = parse_fixture("20260311.1000000110.mps");
    // @task[ status: done , work ] — status first, then tag
    let done_task = els
        .values()
        .filter_map(|e| {
            if let Element::Task { data, .. } = e {
                Some(data)
            } else {
                None
            }
        })
        .find(|d| d.is_done());
    assert!(
        done_task.is_some(),
        "status: done before tag should parse correctly"
    );
    let done_data = done_task.unwrap();
    assert!(
        done_data.tags.contains(&"work".to_string()),
        "tag after status attr"
    );
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 12: @ signs in body without { — not parsed as elements
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_at_in_body_without_brace_not_parsed() {
    let els = parse_fixture("20260312.1000000111.mps");
    // Body contains @john.doe, @example.com, @deprecated, etc. — none should become elements
    let unknowns: Vec<_> = els.values().filter(|e| e.is_unknown()).collect();
    assert!(
        unknowns.is_empty(),
        "@ without {{ must not create Unknown elements"
    );
    // root mps + note + task + log = 4 total
    assert_eq!(els.len(), 4, "only 3 real elements + root mps");
}

#[test]
fn test_at_in_body_body_preserved() {
    let els = parse_fixture("20260312.1000000111.mps");
    let note = els
        .values()
        .find(|e| e.kind() == ElementKind::Note)
        .unwrap();
    assert!(
        note.body_str().contains("@john.doe"),
        "@john.doe preserved in body"
    );
    assert!(
        note.body_str().contains("@deprecated"),
        "@deprecated preserved"
    );
}

#[test]
fn test_at_in_body_url_not_parsed_as_element() {
    let els = parse_fixture("20260312.1000000111.mps");
    let note = els
        .values()
        .find(|e| e.kind() == ElementKind::Note)
        .unwrap();
    assert!(
        note.body_str().contains("https://github.com"),
        "URL preserved in body"
    );
}

// ═══════════════════════════════════════════════════════════════════════════
// Cross-fixture store integration
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_store_all_files_includes_new_fixtures() {
    let (_dir, store) = fixture_store();
    let files = store.all_files().unwrap();
    // Original 8 + new 13 fixtures = 21 total (some may be epoch-less)
    assert!(
        files.len() >= 18,
        "all fixture files should be found, got {}",
        files.len()
    );
}

#[test]
fn test_store_all_file_dates_includes_new_dates() {
    let (_dir, store) = fixture_store();
    let dates = store.all_file_dates().unwrap();
    let new_dates = [
        make_date(2026, 3, 1),
        make_date(2026, 3, 2),
        make_date(2026, 3, 10), // epoch-less
        make_date(2026, 3, 12),
    ];
    for d in &new_dates {
        assert!(dates.contains(d), "date {} should be in all_file_dates", d);
    }
}

#[test]
fn test_store_search_across_all_fixtures_unicode() {
    let (_dir, store) = fixture_store();
    let results = store.search("Unicode", None, None, None).unwrap();
    assert!(!results.is_empty(), "should find Unicode in notes");
}

#[test]
fn test_store_search_tag_filter_hyphenated() {
    let (_dir, store) = fixture_store();
    let results = store.search("", None, Some("feature-123"), None).unwrap();
    assert!(
        !results.is_empty(),
        "should find elements with hyphenated tag feature-123"
    );
}

#[test]
fn test_store_tag_filter_excludes_unknowns_from_mixed_file() {
    let (_dir, store) = fixture_store();
    let date = make_date(2026, 3, 6);
    let els = store.parse_date(date).unwrap();
    let visible: Vec<_> = els
        .values()
        .filter(|e| !e.is_unknown() && !e.is_mps_group())
        .collect();
    assert_eq!(
        visible.len(),
        3,
        "only task, note, log visible from mixed-unknown file"
    );
}

#[test]
fn test_store_stats_empty_body_elements_counted() {
    let (_dir, store) = fixture_store();
    let date = make_date(2026, 3, 1);
    let els = store.parse_date(date).unwrap();
    let task_count = els
        .values()
        .filter(|e| e.kind() == ElementKind::Task)
        .count();
    let log_count = els
        .values()
        .filter(|e| e.kind() == ElementKind::Log)
        .count();
    assert_eq!(task_count, 1, "empty-body task counted");
    assert_eq!(log_count, 1, "empty-body log counted");
}

#[test]
fn test_files_since_includes_new_edge_case_dates() {
    let (_dir, store) = fixture_store();
    let since = make_date(2026, 3, 1);
    let files = store.files_since(since).unwrap();
    // Should include all edge case fixtures from March 2026
    assert!(
        files.len() >= 10,
        "files_since 2026-03-01 should include all March edge cases"
    );
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 13: rewrite_element robustness
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_rewrite_empty_body_task_to_done() {
    let (dir, store) = fixture_store();
    let date = make_date(2026, 3, 1);
    let mut attrs = HashMap::new();
    attrs.insert("status".to_string(), "done".to_string());
    let ok = store.rewrite_element("task-1", &attrs, date).unwrap();
    assert!(ok, "should rewrite task with empty body");
    let file = store.find_file(date).unwrap();
    let content = std::fs::read_to_string(&file).unwrap();
    assert!(content.contains("status: done"), "status written into args");
    drop(dir);
}

#[test]
fn test_rewrite_log_add_start_time() {
    // @log[work]{ Log one - no times } → add start time
    let (dir, store) = fixture_store();
    let date = make_date(2026, 3, 3);
    let mut attrs = HashMap::new();
    attrs.insert("start".to_string(), "08:00".to_string());
    // log-1 is the first log (no times)
    let ok = store.rewrite_element("log-1", &attrs, date).unwrap();
    assert!(ok, "should add start time to a no-time log");
    let file = store.find_file(date).unwrap();
    let content = std::fs::read_to_string(&file).unwrap();
    assert!(content.contains("start: 08:00"), "start time added");
    drop(dir);
}

#[test]
fn test_rewrite_log_update_end_time() {
    // @log[work, start: 09:00, end: 10:00]{ Log two } → update end to 11:00
    let (dir, store) = fixture_store();
    let date = make_date(2026, 3, 3);
    let mut attrs = HashMap::new();
    attrs.insert("end".to_string(), "11:00".to_string());
    // log-2 is the timed log
    let ok = store.rewrite_element("log-2", &attrs, date).unwrap();
    assert!(ok, "should update end time");
    let file = store.find_file(date).unwrap();
    let content = std::fs::read_to_string(&file).unwrap();
    assert!(content.contains("end: 11:00"), "end time updated to 11:00");
    drop(dir);
}

#[test]
fn test_rewrite_preserves_tags_from_original() {
    // @task[work]{ Task one } → mark done, tag 'work' should still be there
    let (dir, store) = fixture_store();
    let date = make_date(2026, 3, 3);
    let mut attrs = HashMap::new();
    attrs.insert("status".to_string(), "done".to_string());
    store.rewrite_element("task-1", &attrs, date).unwrap();
    let file = store.find_file(date).unwrap();
    let content = std::fs::read_to_string(&file).unwrap();
    // The rewritten line should have both status: done and tag work
    assert!(
        content.contains("work"),
        "tag 'work' preserved after rewrite"
    );
    drop(dir);
}

#[test]
fn test_rewrite_nonexistent_human_ref_returns_false() {
    let (_dir, store) = fixture_store();
    let date = make_date(2026, 3, 1);
    let attrs = HashMap::new();
    let ok = store.rewrite_element("task-999", &attrs, date).unwrap();
    assert!(!ok, "nonexistent human ref returns false");
}

#[test]
fn test_rewrite_nonexistent_epoch_ref_returns_false() {
    let (_dir, store) = fixture_store();
    let date = make_date(2026, 3, 1);
    let attrs = HashMap::new();
    let ok = store
        .rewrite_element("20260301.9999", &attrs, date)
        .unwrap();
    assert!(!ok, "nonexistent epoch ref returns false");
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 14: split_args edge cases
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_split_args_time_with_colon_value() {
    use mps::elements::split_args;
    // "at: 10:30" — first colon splits key="at", val="10:30"
    let p = split_args("at: 10:30");
    assert_eq!(p.attrs.get("at").map(|s| s.as_str()), Some("10:30"));
    assert!(p.tags.is_empty());
}

#[test]
fn test_split_args_time_with_seconds() {
    use mps::elements::split_args;
    let p = split_args("start: 09:00:00");
    assert_eq!(p.attrs.get("start").map(|s| s.as_str()), Some("09:00:00"));
}

#[test]
fn test_split_args_multiple_time_attrs() {
    use mps::elements::split_args;
    let p = split_args("work, start: 09:00, end: 17:30, backend");
    assert_eq!(p.tags, vec!["work", "backend"]);
    assert_eq!(p.attrs.get("start").map(|s| s.as_str()), Some("09:00"));
    assert_eq!(p.attrs.get("end").map(|s| s.as_str()), Some("17:30"));
}

#[test]
fn test_split_args_empty_value_after_colon() {
    use mps::elements::split_args;
    // "key:" — empty value
    let p = split_args("key:");
    assert_eq!(p.attrs.get("key").map(|s| s.as_str()), Some(""));
    assert!(p.tags.is_empty());
}

#[test]
fn test_split_args_single_tag_no_comma() {
    use mps::elements::split_args;
    let p = split_args("work");
    assert_eq!(p.tags, vec!["work"]);
    assert!(p.attrs.is_empty());
}

#[test]
fn test_split_args_hyphenated_tag() {
    use mps::elements::split_args;
    let p = split_args("mps-dev, work_item");
    assert_eq!(p.tags, vec!["mps-dev", "work_item"]);
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 15: Duplicate raw_args — rewriting the SECOND of two identical-args tasks
// This is the critical regression: naive first-match regex updates the wrong element.
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_rewrite_second_duplicate_raw_args_updates_correct_element() {
    // @task[work, status: open]{ First open work task }   ← task-1 / epoch .1
    // @task[work, status: open]{ Second open work task }  ← task-2 / epoch .2
    // Updating task-2 must mark "Second" task done, NOT "First".
    let (dir, store) = fixture_store();
    let date = make_date(2026, 3, 8);
    let mut attrs = HashMap::new();
    attrs.insert("status".to_string(), "done".to_string());

    let ok = store.rewrite_element("task-2", &attrs, date).unwrap();
    assert!(ok, "rewrite task-2 should succeed");

    let file = store.find_file(date).unwrap();
    let content = std::fs::read_to_string(&file).unwrap();

    // The FIRST task should remain open
    assert!(
        content.contains("First open work task"),
        "first task body should still be present"
    );
    // The SECOND task should now be done
    assert!(
        content.contains("Second open work task"),
        "second task body should still be present"
    );

    // Parse the rewritten file and verify only task-2 changed status
    let els = parser::parse_file(&file).unwrap();
    let r = RefResolver::new(&els);
    let task1_epoch = r.to_epoch("task-1").unwrap();
    let task2_epoch = r.to_epoch("task-2").unwrap();

    let task1 = els.get(task1_epoch).unwrap();
    let task2 = els.get(task2_epoch).unwrap();

    // After rewrite: task-1 must still be OPEN, task-2 must be DONE
    if let Element::Task { data: d1, .. } = task1 {
        assert!(
            d1.is_open(),
            "task-1 should remain open after rewriting task-2"
        );
    }
    if let Element::Task { data: d2, .. } = task2 {
        assert!(d2.is_done(), "task-2 should be done after rewrite");
    }
    drop(dir);
}

#[test]
fn test_rewrite_fourth_of_four_same_args_tasks() {
    // backend tasks: task-3 and task-4 both have "backend, status: done"
    // Updating task-4 by epoch ref should only change the 4th task
    let (dir, store) = fixture_store();
    let date = make_date(2026, 3, 8);
    let mut attrs = HashMap::new();
    attrs.insert("status".to_string(), "open".to_string()); // flip back to open

    // task-4 is epoch 20260308.4
    let ok = store.rewrite_element("20260308.4", &attrs, date).unwrap();
    assert!(ok);

    let file = store.find_file(date).unwrap();
    let els = parser::parse_file(&file).unwrap();

    // task-3 at .3 should still be done
    let task3 = els.get("20260308.3").unwrap();
    if let Element::Task { data, .. } = task3 {
        assert!(data.is_done(), "task-3 (20260308.3) must remain done");
    }
    // task-4 at .4 should now be open
    let task4 = els.get("20260308.4").unwrap();
    if let Element::Task { data, .. } = task4 {
        assert!(data.is_open(), "task-4 (20260308.4) should now be open");
    }
    drop(dir);
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 16: Multiple files for the same date
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_multiple_files_same_date_find_files_returns_both() {
    let (_dir, store) = fixture_store();
    let date = make_date(2026, 3, 15);
    let files = store.find_files(date);
    assert_eq!(files.len(), 2, "both files for 2026-03-15 should be found");
}

#[test]
fn test_multiple_files_same_date_find_file_returns_first() {
    let (_dir, store) = fixture_store();
    let date = make_date(2026, 3, 15);
    let file = store.find_file(date).expect("should find a file");
    // First sorted filename: 20260315.1000000200.mps < 20260315.1000000201.mps
    assert!(
        file.to_str().unwrap().contains("1000000200"),
        "find_file should return the first (earliest epoch) file"
    );
}

#[test]
fn test_multiple_files_same_date_parse_date_uses_first() {
    let (_dir, store) = fixture_store();
    let date = make_date(2026, 3, 15);
    let els = store.parse_date(date).unwrap();
    // First file has morning task and note only
    let task = els
        .values()
        .find(|e| e.kind() == ElementKind::Task)
        .unwrap();
    assert!(
        task.body_str().contains("morning work"),
        "parse_date should parse the FIRST file for the date"
    );
}

#[test]
fn test_multiple_files_same_date_all_file_dates_deduplicates() {
    let (_dir, store) = fixture_store();
    let dates = store.all_file_dates().unwrap();
    let march_15_count = dates
        .iter()
        .filter(|&&d| d == make_date(2026, 3, 15))
        .count();
    assert_eq!(
        march_15_count, 1,
        "2026-03-15 should appear only once in all_file_dates"
    );
}

#[test]
fn test_multiple_files_same_date_all_files_includes_both() {
    let (_dir, store) = fixture_store();
    let all = store.all_files().unwrap();
    let march_15_files: Vec<_> = all
        .iter()
        .filter(|p| {
            p.file_name()
                .and_then(|n| n.to_str())
                .map(|n| n.starts_with("20260315"))
                .unwrap_or(false)
        })
        .collect();
    assert_eq!(
        march_15_files.len(),
        2,
        "all_files includes both files for 2026-03-15"
    );
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 17: files_since boundary conditions
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_files_since_includes_exact_boundary_date() {
    let (_dir, store) = fixture_store();
    // 20260315 files exist; since 2026-03-15 should include them (inclusive boundary)
    let since = make_date(2026, 3, 15);
    let files = store.files_since(since).unwrap();
    let has_march15 = files.iter().any(|p| {
        p.file_name()
            .and_then(|n| n.to_str())
            .map(|n| n.starts_with("20260315"))
            .unwrap_or(false)
    });
    assert!(
        has_march15,
        "files_since should include files on the boundary date"
    );
}

#[test]
fn test_files_since_excludes_before_boundary() {
    let (_dir, store) = fixture_store();
    // since 2026-03-15 should exclude 20260314 and earlier
    let since = make_date(2026, 3, 15);
    let files = store.files_since(since).unwrap();
    let has_before = files.iter().any(|p| {
        p.file_name()
            .and_then(|n| n.to_str())
            .map(|n| n < "20260315")
            .unwrap_or(false)
    });
    assert!(
        !has_before,
        "files_since should exclude files before boundary date"
    );
}

#[test]
fn test_files_since_epoch_less_file_included() {
    let (_dir, store) = fixture_store();
    // 20260310.mps exists; since 2026-03-10 should include it
    let since = make_date(2026, 3, 10);
    let files = store.files_since(since).unwrap();
    let has_epoch_less = files
        .iter()
        .any(|p| p.file_name().and_then(|n| n.to_str()) == Some("20260310.mps"));
    assert!(
        has_epoch_less,
        "epoch-less file should be included in files_since"
    );
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 18: Mixed-status file with all element types
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_mixed_status_file_all_types_present() {
    let els = parse_fixture("20260316.1000000202.mps");
    let visible = non_root(&els);
    assert!(visible.iter().any(|e| e.kind() == ElementKind::Task));
    assert!(visible.iter().any(|e| e.kind() == ElementKind::Note));
    assert!(visible.iter().any(|e| e.kind() == ElementKind::Log));
    assert!(visible.iter().any(|e| e.kind() == ElementKind::Reminder));
}

#[test]
fn test_mixed_status_bare_task_is_open() {
    let els = parse_fixture("20260316.1000000202.mps");
    // @task{ Bare task } — no status → defaults to open
    let tasks: Vec<_> = els
        .values()
        .filter_map(|e| {
            if let Element::Task { data, .. } = e {
                Some(data)
            } else {
                None
            }
        })
        .collect();
    let open_count = tasks.iter().filter(|d| d.is_open()).count();
    assert_eq!(open_count, 1, "bare task defaults to open");
}

#[test]
fn test_mixed_status_explicit_done_task() {
    let els = parse_fixture("20260316.1000000202.mps");
    let tasks: Vec<_> = els
        .values()
        .filter_map(|e| {
            if let Element::Task { data, .. } = e {
                Some(data)
            } else {
                None
            }
        })
        .collect();
    let done_count = tasks.iter().filter(|d| d.is_done()).count();
    assert_eq!(done_count, 1, "explicit status: done task");
}

#[test]
fn test_mixed_status_log_30_minutes() {
    let els = parse_fixture("20260316.1000000202.mps");
    let log = els.values().find(|e| e.kind() == ElementKind::Log).unwrap();
    if let Element::Log { data, .. } = log {
        assert_eq!(data.duration_minutes(), Some(30));
        assert_eq!(data.duration_str(), Some("0h30m".to_string()));
    }
}

#[test]
fn test_mixed_status_reminder_at_8am() {
    let els = parse_fixture("20260316.1000000202.mps");
    let rem = els
        .values()
        .find(|e| e.kind() == ElementKind::Reminder)
        .unwrap();
    if let Element::Reminder { data, .. } = rem {
        assert_eq!(data.at.as_deref(), Some("8am"));
    }
}

#[test]
fn test_mixed_status_ref_resolver_task_with_only_attr() {
    let els = parse_fixture("20260316.1000000202.mps");
    let r = RefResolver::new(&els);
    // @task[status: done] is the first element → task-1
    assert_eq!(r.to_epoch("task-1"), Some("20260316.1"));
    // @task (bare) is second → task-2
    assert_eq!(r.to_epoch("task-2"), Some("20260316.2"));
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 19: Store::append produces parseable output
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_append_task_then_parse_and_update() {
    let (dir, store) = fixture_store();
    let date = make_date(2026, 3, 20);
    // Append a task
    store
        .append(
            "task",
            "New integration task",
            &["work".to_string()],
            &[],
            date,
        )
        .unwrap();
    // Parse it back
    let els = store.parse_date(date).unwrap();
    let r = RefResolver::new(&els);
    assert_eq!(
        r.to_epoch("task-1"),
        Some("20260320.1"),
        "appended task gets ref task-1"
    );
    // Now update it to done
    let mut attrs = HashMap::new();
    attrs.insert("status".to_string(), "done".to_string());
    let ok = store.rewrite_element("task-1", &attrs, date).unwrap();
    assert!(ok, "should rewrite a just-appended task");
    // Parse again to verify
    let els2 = store.parse_date(date).unwrap();
    let task = els2
        .values()
        .find(|e| e.kind() == ElementKind::Task)
        .unwrap();
    if let Element::Task { data, .. } = task {
        assert!(data.is_done(), "task should be done after rewrite");
    }
    drop(dir);
}

#[test]
fn test_append_multiple_elements_ref_counters() {
    let (dir, store) = fixture_store();
    let date = make_date(2026, 3, 21);
    store
        .append("task", "Task A", &["work".to_string()], &[], date)
        .unwrap();
    store.append("note", "Note B", &[], &[], date).unwrap();
    store
        .append("task", "Task C", &["work".to_string()], &[], date)
        .unwrap();
    let els = store.parse_date(date).unwrap();
    let r = RefResolver::new(&els);
    assert_eq!(r.to_epoch("task-1"), Some("20260321.1"));
    assert_eq!(r.to_epoch("note-1"), Some("20260321.2"));
    assert_eq!(r.to_epoch("task-2"), Some("20260321.3"));
    drop(dir);
}

#[test]
fn test_append_log_with_times_duration_calculated() {
    let (dir, store) = fixture_store();
    let date = make_date(2026, 3, 22);
    store
        .append(
            "log",
            "Two hour work session",
            &["work".to_string()],
            &[("start", "09:00"), ("end", "11:00")],
            date,
        )
        .unwrap();
    let els = store.parse_date(date).unwrap();
    let log = els.values().find(|e| e.kind() == ElementKind::Log).unwrap();
    if let Element::Log { data, .. } = log {
        assert_eq!(data.duration_minutes(), Some(120));
        assert_eq!(data.duration_str(), Some("2h".to_string()));
    }
    drop(dir);
}

#[test]
fn test_append_reminder_with_at_time() {
    let (dir, store) = fixture_store();
    let date = make_date(2026, 3, 23);
    store
        .append("reminder", "Daily standup", &[], &[("at", "9:30")], date)
        .unwrap();
    let els = store.parse_date(date).unwrap();
    let rem = els
        .values()
        .find(|e| e.kind() == ElementKind::Reminder)
        .unwrap();
    if let Element::Reminder { data, .. } = rem {
        assert_eq!(data.at.as_deref(), Some("9:30"));
    }
    drop(dir);
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 20: Rewrite preserves body exactly and file idempotency
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_rewrite_preserves_body_content_exactly() {
    let (dir, store) = fixture_store();
    let date = make_date(2026, 3, 7); // unicode file
    let file = store.find_file(date).unwrap();
    let before_content = std::fs::read_to_string(&file).unwrap();
    // Rewrite the task's status (it has @task[work]{ body with email etc })
    let mut attrs = HashMap::new();
    attrs.insert("status".to_string(), "done".to_string());
    let ok = store.rewrite_element("task-1", &attrs, date).unwrap();
    assert!(ok);
    let after_content = std::fs::read_to_string(&file).unwrap();
    // Body of the task should still contain the @ sign
    assert!(
        after_content.contains("contact@example.com"),
        "@ in body preserved after rewrite"
    );
    assert!(
        after_content.contains("*bold*"),
        "markdown preserved after rewrite"
    );
    // Only the opening line changed
    assert!(after_content.contains("status: done"), "status updated");
    // Everything else unchanged
    assert_eq!(
        before_content.replace("@task[work]{", "@task[status: done, work]{"),
        after_content,
        "only the opening line should change"
    );
    drop(dir);
}

#[test]
fn test_double_rewrite_idempotent() {
    // Rewriting the same element twice with the same value should be a no-op the second time.
    let (dir, store) = fixture_store();
    let date = make_date(2026, 3, 3); // many same-type file
    let mut attrs = HashMap::new();
    attrs.insert("status".to_string(), "done".to_string());
    // First rewrite: task-3 from open → done
    let ok1 = store.rewrite_element("task-3", &attrs, date).unwrap();
    assert!(ok1, "first rewrite should succeed");
    let file = store.find_file(date).unwrap();
    let content1 = std::fs::read_to_string(&file).unwrap();
    // Second rewrite: task-3 to done again (same state)
    let ok2 = store.rewrite_element("task-3", &attrs, date).unwrap();
    assert!(
        !ok2,
        "second rewrite with same value should be a no-op (returns false)"
    );
    let content2 = std::fs::read_to_string(&file).unwrap();
    assert_eq!(
        content1, content2,
        "file should be unchanged after no-op rewrite"
    );
    drop(dir);
}

#[test]
fn test_rewrite_note_adds_arbitrary_attr() {
    // Notes don't have typed_attrs. Rewiring with a custom attr should write it.
    // This is an edge case — notes don't normally have attrs.
    let (dir, store) = fixture_store();
    let date = make_date(2026, 3, 1); // has a bare @note
    let mut attrs = HashMap::new();
    attrs.insert("priority".to_string(), "high".to_string());
    let ok = store.rewrite_element("note-1", &attrs, date).unwrap();
    assert!(ok, "should rewrite note with custom attr");
    let file = store.find_file(date).unwrap();
    let content = std::fs::read_to_string(&file).unwrap();
    assert!(
        content.contains("priority: high"),
        "custom attr written to note"
    );
    drop(dir);
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 21: Search correctness edge cases
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_search_case_insensitive() {
    let (_dir, store) = fixture_store();
    let lower = store.search("unicode", None, None, None).unwrap();
    let upper = store.search("UNICODE", None, None, None).unwrap();
    let mixed = store.search("UniCode", None, None, None).unwrap();
    assert!(!lower.is_empty(), "lowercase search should find match");
    assert_eq!(lower.len(), upper.len(), "case-insensitive: lower == UPPER");
    assert_eq!(lower.len(), mixed.len(), "case-insensitive: lower == MiXeD");
}

#[test]
fn test_search_empty_query_returns_all_known_elements() {
    // Isolated store with only the mixed-unknown fixture
    let dir = tempfile::tempdir().unwrap();
    std::fs::copy(
        fixture_path("20260306.1000000105.mps"),
        dir.path().join("20260306.1000000105.mps"),
    )
    .unwrap();
    let store = Store::new(dir.path());
    let results = store.search("", None, None, None).unwrap();
    // Should return task, note, log — NOT widget, gadget, sprocket, thingamajig
    assert_eq!(
        results.len(),
        3,
        "search with empty query finds all 3 known elements; got {}",
        results.len()
    );
    assert!(results.iter().all(|r| !r.element.is_unknown()));
}

#[test]
fn test_search_type_filter_note_only() {
    // Isolated store with only the many-same-type fixture
    let dir = tempfile::tempdir().unwrap();
    std::fs::copy(
        fixture_path("20260303.1000000102.mps"),
        dir.path().join("20260303.1000000102.mps"),
    )
    .unwrap();
    let store = Store::new(dir.path());
    let results = store.search("", Some("note"), None, None).unwrap();
    assert_eq!(
        results.len(),
        3,
        "type=note should return all 3 notes; got {}",
        results.len()
    );
    assert!(results
        .iter()
        .all(|r| r.element.kind() == ElementKind::Note));
}

#[test]
fn test_search_type_filter_log_with_since() {
    let (_dir, store) = fixture_store();
    let since = make_date(2026, 3, 4);
    let results = store.search("", Some("log"), None, Some(since)).unwrap();
    assert!(!results.is_empty(), "should find logs since 2026-03-04");
    assert!(results.iter().all(|r| r.element.kind() == ElementKind::Log));
}

#[test]
fn test_search_no_results_returns_empty_vec() {
    let (_dir, store) = fixture_store();
    let results = store
        .search("xyzzy_impossible_string_12345", None, None, None)
        .unwrap();
    assert!(results.is_empty(), "no match → empty vec");
}

#[test]
fn test_search_result_date_str_correct_format() {
    let (_dir, store) = fixture_store();
    let results = store.search("epoch-less", None, None, None).unwrap();
    assert!(
        !results.is_empty(),
        "should find 'epoch-less' in 20260310.mps"
    );
    let date_str = &results[0].date_str;
    assert_eq!(
        date_str, "20260310",
        "date_str should be YYYYMMDD from filename"
    );
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 22: RefResolver roundtrips across all new fixtures
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_ref_resolver_roundtrip_all_new_fixtures() {
    let new_fixtures = [
        "20260301.1000000100.mps",
        "20260302.1000000101.mps",
        "20260303.1000000102.mps",
        "20260304.1000000103.mps",
        "20260305.1000000104.mps",
        "20260306.1000000105.mps",
        "20260307.1000000106.mps",
        "20260308.1000000107.mps",
        "20260309.1000000108.mps",
        "20260311.1000000110.mps",
        "20260312.1000000111.mps",
        "20260313.1000000112.mps",
        "20260316.1000000202.mps",
    ];
    for fixture in &new_fixtures {
        let els = parse_fixture(fixture);
        let r = RefResolver::new(&els);
        // Iterate the elements map keys — same approach as integration_test.rs
        for epoch_ref in els.keys() {
            if let Some(human) = r.to_human(epoch_ref) {
                let back = r.to_epoch(human).unwrap_or("none");
                assert_eq!(
                    back,
                    epoch_ref.as_str(),
                    "{}: roundtrip failed: epoch {} → human {} → {}",
                    fixture,
                    epoch_ref,
                    human,
                    back
                );
            }
        }
    }
}

#[test]
fn test_ref_resolver_epoch_less_file_roundtrip() {
    let els = parse_fixture("20260310.mps");
    let r = RefResolver::new(&els);
    // base_ref = 20260310 (parsed from "20260310.mps"[..8])
    assert_eq!(r.to_human("20260310.1"), Some("task-1"));
    assert_eq!(r.to_human("20260310.2"), Some("note-1"));
    assert_eq!(r.to_human("20260310.3"), Some("log-1"));
    assert_eq!(r.to_epoch("task-1"), Some("20260310.1"));
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 23: export command excludes unknown elements
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_export_excludes_unknown_elements() {
    // The mixed-unknown fixture has 4 unknowns. Export should not include them.
    let dir = tempfile::tempdir().unwrap();
    std::fs::copy(
        fixture_path("20260306.1000000105.mps"),
        dir.path().join("20260306.1000000105.mps"),
    )
    .unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 3, 6);
    let els = store.parse_date(date).unwrap();
    // Simulate what export does: skip mps_group and unknown
    let exported: Vec<_> = els
        .values()
        .filter(|e| !e.is_mps_group() && !e.is_unknown())
        .collect();
    assert_eq!(
        exported.len(),
        3,
        "export should have 3 known elements, not 7"
    );
}

#[test]
fn test_export_includes_all_element_types() {
    let dir = tempfile::tempdir().unwrap();
    std::fs::copy(
        fixture_path("20260316.1000000202.mps"),
        dir.path().join("20260316.1000000202.mps"),
    )
    .unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 3, 16);
    let els = store.parse_date(date).unwrap();
    let exported: Vec<_> = els
        .values()
        .filter(|e| !e.is_mps_group() && !e.is_unknown())
        .collect();
    // task×2, note×1, log×1, reminder×1 = 5
    assert_eq!(exported.len(), 5);
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 24: Tags command with status filter
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_tags_status_filter_open_tasks_only() {
    let dir = tempfile::tempdir().unwrap();
    std::fs::copy(
        fixture_path("20260303.1000000102.mps"),
        dir.path().join("20260303.1000000102.mps"),
    )
    .unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 3, 3);
    let els = store.parse_date(date).unwrap();
    // Status filter: only open tasks have tags counted
    // Open tasks: task1 (work), task2 (work), task3 (work) → "work" appears 3 times
    // Done tasks: task4 (work), task5 (work) → NOT counted
    let mut counts: std::collections::HashMap<String, usize> = std::collections::HashMap::new();
    for (_, el) in &els {
        if el.is_mps_group() || el.is_unknown() {
            continue;
        }
        match el {
            Element::Task { data, .. } if data.is_open() => {
                for tag in el.tags() {
                    *counts.entry(tag.clone()).or_insert(0) += 1;
                }
            }
            _ => {}
        }
    }
    assert_eq!(
        counts.get("work").copied().unwrap_or(0),
        3,
        "open task status filter: only 3 open tasks have 'work' tag"
    );
}

#[test]
fn test_tags_type_filter_log_only() {
    let dir = tempfile::tempdir().unwrap();
    std::fs::copy(
        fixture_path("20260304.1000000103.mps"),
        dir.path().join("20260304.1000000103.mps"),
    )
    .unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 3, 4);
    let els = store.parse_date(date).unwrap();
    // Type filter: only log tags counted
    let mut counts: std::collections::HashMap<String, usize> = std::collections::HashMap::new();
    for (_, el) in &els {
        if el.kind() != ElementKind::Log {
            continue;
        }
        for tag in el.tags() {
            *counts.entry(tag.clone()).or_insert(0) += 1;
        }
    }
    // Log has tag "feature-123"
    assert!(
        counts.contains_key("feature-123"),
        "log tag feature-123 counted"
    );
    // Tags from tasks and notes should NOT be counted
    assert!(
        !counts.contains_key("mps-dev"),
        "task tag mps-dev not counted with log filter"
    );
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 25: Parser with body containing special chars that could confuse regex
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_parser_body_with_regex_metacharacters() {
    // Body has characters that are regex special: . * + ? [ ] ( ) ^ $ |
    // Note: literal } CANNOT appear in a body — it would close the element early.
    // This is a known parser limitation (matches Ruby behaviour).
    let dir = tempfile::tempdir().unwrap();
    let content = "@task[work]{\n  Fix regex: (a|b).* and [xyz]+ and ^start and end$ and a|b\n}";
    let path = dir.path().join("20260320.1000000300.mps");
    std::fs::write(&path, content).unwrap();
    let els = parser::parse_file(&path).unwrap();
    let task = els
        .values()
        .find(|e| e.kind() == ElementKind::Task)
        .unwrap();
    assert!(
        task.body_str().contains("(a|b).*"),
        "regex parens/dot/star preserved"
    );
    assert!(
        task.body_str().contains("[xyz]+"),
        "regex brackets/plus preserved"
    );
    assert!(task.body_str().contains("^start"), "regex caret preserved");
    assert!(task.body_str().contains("end$"), "regex dollar preserved");
    assert!(task.body_str().contains("a|b"), "regex pipe preserved");
}

#[test]
fn test_rewrite_element_body_with_regex_metacharacters() {
    // The rewrite should not break when element has regex metacharacters in body.
    // No literal } allowed in body (known parser limitation).
    let dir = tempfile::tempdir().unwrap();
    let content = "@task[work]{\n  Fix regex: (a|b).* and [xyz]+ and ^start$ and a|b\n}\n";
    let path = dir.path().join("20260320.1000000300.mps");
    std::fs::write(&path, content).unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 3, 20);
    let mut attrs = HashMap::new();
    attrs.insert("status".to_string(), "done".to_string());
    let ok = store.rewrite_element("task-1", &attrs, date).unwrap();
    assert!(ok, "rewrite should succeed with regex chars in body");
    let after = std::fs::read_to_string(&path).unwrap();
    assert!(
        after.contains("(a|b).*"),
        "regex chars in body preserved after rewrite"
    );
    assert!(
        after.contains("[xyz]+"),
        "bracket chars preserved after rewrite"
    );
    assert!(after.contains("status: done"), "status updated");
    drop(dir);
}

#[test]
fn test_rewrite_element_tag_with_regex_metacharacters_in_args() {
    // Args with special chars that need regex escaping (e.g., . in tag names)
    // Note: tags can't contain . in practice, but raw_args might have other special chars
    let dir = tempfile::tempdir().unwrap();
    // Use a reminder with "at: 3:30pm" — the colon in time gets regex-escaped
    let content = "@reminder[at: 3:30pm]{\n  afternoon meeting\n}\n";
    let path = dir.path().join("20260321.1000000301.mps");
    std::fs::write(&path, content).unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 3, 21);
    let mut attrs = HashMap::new();
    attrs.insert("at".to_string(), "4:00pm".to_string());
    let ok = store.rewrite_element("reminder-1", &attrs, date).unwrap();
    assert!(ok, "rewrite reminder with colon in time should succeed");
    let after = std::fs::read_to_string(&path).unwrap();
    assert!(after.contains("at: 4:00pm"), "time updated correctly");
    assert!(after.contains("afternoon meeting"), "body preserved");
    drop(dir);
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 26: all_file_dates sorted and unique across epoch-less + epoch files
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_all_file_dates_is_sorted() {
    let (_dir, store) = fixture_store();
    let dates = store.all_file_dates().unwrap();
    let sorted_copy = {
        let mut v = dates.clone();
        v.sort();
        v
    };
    assert_eq!(
        dates, sorted_copy,
        "all_file_dates should be sorted ascending"
    );
}

#[test]
fn test_all_file_dates_no_duplicates() {
    let (_dir, store) = fixture_store();
    let dates = store.all_file_dates().unwrap();
    let unique: std::collections::HashSet<_> = dates.iter().collect();
    assert_eq!(
        dates.len(),
        unique.len(),
        "all_file_dates should have no duplicates"
    );
}

#[test]
fn test_all_file_dates_epoch_less_file_at_correct_position() {
    let (_dir, store) = fixture_store();
    let dates = store.all_file_dates().unwrap();
    let march_10 = make_date(2026, 3, 10);
    let march_9_pos = dates.iter().position(|&d| d < march_10);
    let march_10_pos = dates.iter().position(|&d| d == march_10).unwrap();
    let march_11_pos = dates.iter().position(|&d| d > march_10);
    // march_10 must come after all dates < it and before all dates > it
    if let (Some(before), Some(after)) = (march_9_pos, march_11_pos) {
        assert!(
            before < march_10_pos,
            "epoch-less date sorted correctly after earlier dates"
        );
        assert!(
            march_10_pos < after,
            "epoch-less date sorted correctly before later dates"
        );
    }
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 27: element count assertions across the full fixture set
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_store_total_element_counts_across_all_fixtures() {
    let (_dir, store) = fixture_store();
    let all_files = store.all_files().unwrap();
    let mut total_tasks = 0usize;
    let mut total_notes = 0usize;
    let mut total_logs = 0usize;
    let mut total_unknowns = 0usize;
    for file in &all_files {
        let els = parser::parse_file(file).unwrap();
        for (_, el) in &els {
            match el.kind() {
                ElementKind::Task => total_tasks += 1,
                ElementKind::Note => total_notes += 1,
                ElementKind::Log => total_logs += 1,
                ElementKind::Unknown => total_unknowns += 1,
                _ => {}
            }
        }
    }
    // Verify unknowns exist (from the mixed-unknown fixture)
    assert!(
        total_unknowns >= 4,
        "should have at least 4 unknown elements from the mixed fixture"
    );
    // Verify reasonable element counts
    assert!(
        total_tasks >= 20,
        "should have many tasks across all fixtures; got {}",
        total_tasks
    );
    assert!(
        total_notes >= 10,
        "should have many notes across all fixtures; got {}",
        total_notes
    );
    assert!(
        total_logs >= 10,
        "should have many logs across all fixtures; got {}",
        total_logs
    );
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 28: Task-in-task nesting — non-@mps parent containers
// The nested_test.mps pattern: @task contains child @tasks and @notes.
// ═══════════════════════════════════════════════════════════════════════════

fn parse_task_in_task() -> IndexMap<String, Element> {
    parse_fixture("20260501.1800000001.mps")
}

#[test]
fn test_task_in_task_total_element_count() {
    // root mps wrapper + outer task + 3 child tasks + 3 child notes = 8
    let els = parse_task_in_task();
    assert_eq!(els.len(), 8, "expected 8 elements, got {}", els.len());
}

#[test]
fn test_task_in_task_all_ref_paths_present() {
    let els = parse_task_in_task();
    assert!(els.contains_key("20260501"), "root mps wrapper");
    assert!(els.contains_key("20260501.1"), "outer task");
    assert!(els.contains_key("20260501.1.1"), "child task: bugs");
    assert!(els.contains_key("20260501.1.2"), "child task: meetings");
    assert!(els.contains_key("20260501.1.3"), "child task: dev works");
    assert!(
        els.contains_key("20260501.1.3.1"),
        "nested note: items not done"
    );
    assert!(
        els.contains_key("20260501.1.3.2"),
        "nested note: peoples support"
    );
    assert!(els.contains_key("20260501.1.3.3"), "nested note: say no");
}

#[test]
fn test_task_in_task_outer_task_type_and_status() {
    let els = parse_task_in_task();
    let outer = els.get("20260501.1").unwrap();
    assert_eq!(outer.kind(), ElementKind::Task, "outer element is a Task");
    if let Element::Task { data, .. } = outer {
        assert!(data.is_open(), "outer task has status: open");
        assert!(
            data.tags.is_empty(),
            "outer task has no tags (status: open is an attr)"
        );
    } else {
        panic!("expected Task");
    }
}

#[test]
fn test_task_in_task_inner_tasks_default_open() {
    let els = parse_task_in_task();
    for key in &["20260501.1.1", "20260501.1.2", "20260501.1.3"] {
        let el = els.get(*key).expect(key);
        assert_eq!(el.kind(), ElementKind::Task, "{} should be Task", key);
        if let Element::Task { data, .. } = el {
            assert!(data.is_open(), "{} should default to open (no args)", key);
        }
    }
}

#[test]
fn test_task_in_task_nested_notes_are_notes() {
    let els = parse_task_in_task();
    for key in &["20260501.1.3.1", "20260501.1.3.2", "20260501.1.3.3"] {
        let el = els.get(*key).expect(key);
        assert_eq!(el.kind(), ElementKind::Note, "{} should be Note", key);
    }
}

#[test]
fn test_task_in_task_leaf_bodies_are_clean() {
    let els = parse_task_in_task();
    assert!(els
        .get("20260501.1.1")
        .unwrap()
        .body_str()
        .trim()
        .contains("Several bugs to be fixed"));
    assert!(els
        .get("20260501.1.2")
        .unwrap()
        .body_str()
        .trim()
        .contains("Several meetings to be attended"));
    assert!(els
        .get("20260501.1.3.1")
        .unwrap()
        .body_str()
        .trim()
        .contains("Check for items not done"));
    assert!(els
        .get("20260501.1.3.2")
        .unwrap()
        .body_str()
        .trim()
        .contains("Check for peoples support"));
    assert!(els
        .get("20260501.1.3.3")
        .unwrap()
        .body_str()
        .trim()
        .contains("Say No to not aligned with my goal"));
}

#[test]
fn test_task_in_task_outer_body_contains_child_syntax() {
    // The outer task's body_str is the raw text between its braces,
    // which includes the literal "@task{...}" source of its children.
    let els = parse_task_in_task();
    let body = els.get("20260501.1").unwrap().body_str();
    assert!(
        body.contains("These items to be done by monday"),
        "own text present"
    );
    assert!(
        body.contains("@task{"),
        "child @task syntax embedded in body"
    );
}

#[test]
fn test_task_in_task_dev_task_body_contains_note_syntax() {
    let els = parse_task_in_task();
    let body = els.get("20260501.1.3").unwrap().body_str();
    assert!(
        body.contains("Several dev works to be done"),
        "own text present"
    );
    assert!(
        body.contains("@note{"),
        "child @note syntax embedded in body"
    );
}

#[test]
fn test_task_in_task_resolver_top_level_human_ref() {
    let els = parse_task_in_task();
    let r = RefResolver::new(&els);
    assert_eq!(
        r.to_human("20260501.1"),
        Some("task-1"),
        "outer task → task-1"
    );
    assert_eq!(r.to_epoch("task-1"), Some("20260501.1"), "roundtrip");
}

#[test]
fn test_task_in_task_resolver_child_tasks() {
    let els = parse_task_in_task();
    let r = RefResolver::new(&els);
    assert_eq!(r.to_human("20260501.1.1"), Some("task-1.1"), "bugs task");
    assert_eq!(
        r.to_human("20260501.1.2"),
        Some("task-1.2"),
        "meetings task"
    );
    assert_eq!(r.to_human("20260501.1.3"), Some("task-1.3"), "dev task");
    assert_eq!(r.to_epoch("task-1.1"), Some("20260501.1.1"));
    assert_eq!(r.to_epoch("task-1.2"), Some("20260501.1.2"));
    assert_eq!(r.to_epoch("task-1.3"), Some("20260501.1.3"));
}

#[test]
fn test_task_in_task_resolver_deep_notes() {
    let els = parse_task_in_task();
    let r = RefResolver::new(&els);
    assert_eq!(r.to_human("20260501.1.3.1"), Some("task-1.3.1"));
    assert_eq!(r.to_human("20260501.1.3.2"), Some("task-1.3.2"));
    assert_eq!(r.to_human("20260501.1.3.3"), Some("task-1.3.3"));
    assert_eq!(r.to_epoch("task-1.3.1"), Some("20260501.1.3.1"));
    assert_eq!(r.to_epoch("task-1.3.2"), Some("20260501.1.3.2"));
    assert_eq!(r.to_epoch("task-1.3.3"), Some("20260501.1.3.3"));
}

#[test]
fn test_task_in_task_resolver_full_roundtrip() {
    let els = parse_task_in_task();
    let r = RefResolver::new(&els);
    let pairs = [
        ("20260501.1", "task-1"),
        ("20260501.1.1", "task-1.1"),
        ("20260501.1.2", "task-1.2"),
        ("20260501.1.3", "task-1.3"),
        ("20260501.1.3.1", "task-1.3.1"),
        ("20260501.1.3.2", "task-1.3.2"),
        ("20260501.1.3.3", "task-1.3.3"),
    ];
    for (epoch, human) in &pairs {
        assert_eq!(r.to_human(epoch), Some(*human), "epoch→human: {}", epoch);
        assert_eq!(r.to_epoch(human), Some(*epoch), "human→epoch: {}", human);
    }
}

#[test]
fn test_task_in_task_type_counter_only_counts_toplevel() {
    // The RefResolver "task" type counter is 1 — only the outer task at depth 1.
    // The 3 inner tasks are nested and get dotted refs (task-1.1 etc.), not new task-N slots.
    let els = parse_task_in_task();
    let r = RefResolver::new(&els);
    assert_eq!(
        r.to_human("20260501.1"),
        Some("task-1"),
        "outer task → task-1"
    );
    assert!(
        r.to_epoch("task-2").is_none(),
        "no task-2 — inner tasks are not top-level"
    );
    assert!(r.to_epoch("task-3").is_none(), "no task-3");
    assert!(r.to_epoch("task-4").is_none(), "no task-4");
    // Confirm inner tasks are accessible only via dotted refs
    assert!(
        r.to_epoch("task-1.1").is_some(),
        "task-1.1 is the bugs child task"
    );
}

#[test]
fn test_task_in_task_rewrite_child_by_human_ref() {
    let (dir, store) = fixture_store();
    let date = make_date(2026, 5, 1);
    let mut attrs = HashMap::new();
    attrs.insert("status".to_string(), "done".to_string());
    let ok = store.rewrite_element("task-1.2", &attrs, date).unwrap();
    assert!(ok, "rewrite task-1.2 (meetings) to done");
    let content = std::fs::read_to_string(store.find_file(date).unwrap()).unwrap();
    assert!(content.contains("status: done"), "status written");
    assert!(
        content.contains("Several meetings to be attended"),
        "body preserved"
    );
    assert!(
        content.contains("Several bugs to be fixed"),
        "sibling task unchanged"
    );
    drop(dir);
}

#[test]
fn test_task_in_task_rewrite_deep_child_by_human_ref() {
    let (dir, store) = fixture_store();
    let date = make_date(2026, 5, 1);
    let mut attrs = HashMap::new();
    attrs.insert("status".to_string(), "done".to_string());
    let ok = store.rewrite_element("task-1.3", &attrs, date).unwrap();
    assert!(ok, "rewrite task-1.3 (dev task, level 2) to done");
    let content = std::fs::read_to_string(store.find_file(date).unwrap()).unwrap();
    assert!(
        content.contains("Several dev works to be done"),
        "dev task body preserved"
    );
    drop(dir);
}

#[test]
fn test_task_in_task_rewrite_outer_task_by_human_ref() {
    let (dir, store) = fixture_store();
    let date = make_date(2026, 5, 1);
    let mut attrs = HashMap::new();
    attrs.insert("status".to_string(), "done".to_string());
    let ok = store.rewrite_element("task-1", &attrs, date).unwrap();
    assert!(ok, "rewrite outer task-1 to done");
    let content = std::fs::read_to_string(store.find_file(date).unwrap()).unwrap();
    assert!(content.contains("status: done"), "outer task now done");
    assert!(
        content.contains("Several bugs to be fixed"),
        "inner task body still present"
    );
    drop(dir);
}

#[test]
fn test_task_in_task_task_count() {
    let els = parse_task_in_task();
    let tasks = els
        .values()
        .filter(|e| e.kind() == ElementKind::Task)
        .count();
    assert_eq!(tasks, 4, "4 tasks: 1 outer + 3 nested");
}

#[test]
fn test_task_in_task_note_count() {
    let els = parse_task_in_task();
    let notes = els
        .values()
        .filter(|e| e.kind() == ElementKind::Note)
        .count();
    assert_eq!(notes, 3, "3 notes, all nested inside the dev task");
}

#[test]
fn test_task_in_task_no_logs_or_reminders() {
    let els = parse_task_in_task();
    assert_eq!(
        els.values()
            .filter(|e| e.kind() == ElementKind::Log)
            .count(),
        0
    );
    assert_eq!(
        els.values()
            .filter(|e| e.kind() == ElementKind::Reminder)
            .count(),
        0
    );
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 29: Tagged @mps blocks and task-as-parent (note-in-task)
// Tests @mps[tags]{} capturing and non-mps elements as parent containers.
// ═══════════════════════════════════════════════════════════════════════════

fn parse_tagged_mps_fixture() -> IndexMap<String, Element> {
    parse_fixture("20260502.1800000002.mps")
}

#[test]
fn test_tagged_mps_total_element_count() {
    // root mps + sprint mps + task in sprint + note in sprint + done-task in sprint
    // + top-level done task + note in done task + bare note = 8
    let els = parse_tagged_mps_fixture();
    assert_eq!(els.len(), 8, "expected 8 elements, got {}", els.len());
}

#[test]
fn test_tagged_mps_all_ref_paths_present() {
    let els = parse_tagged_mps_fixture();
    assert!(els.contains_key("20260502"), "root mps");
    assert!(els.contains_key("20260502.1"), "sprint mps block");
    assert!(els.contains_key("20260502.1.1"), "task inside sprint");
    assert!(els.contains_key("20260502.1.2"), "note inside sprint");
    assert!(els.contains_key("20260502.1.3"), "done task inside sprint");
    assert!(els.contains_key("20260502.2"), "top-level done task");
    assert!(els.contains_key("20260502.2.1"), "note inside done task");
    assert!(els.contains_key("20260502.3"), "bare top-level note");
}

#[test]
fn test_tagged_mps_block_captures_tags() {
    let els = parse_tagged_mps_fixture();
    let mps_block = els.get("20260502.1").unwrap();
    assert!(mps_block.is_mps_group(), "sprint block is MpsGroup");
    assert!(
        mps_block.tags().contains(&"sprint-42".to_string()),
        "sprint-42 tag"
    );
    assert!(
        mps_block.tags().contains(&"current".to_string()),
        "current tag"
    );
}

#[test]
fn test_tagged_mps_block_raw_args() {
    let els = parse_tagged_mps_fixture();
    let mps_block = els.get("20260502.1").unwrap();
    assert_eq!(
        mps_block.raw_args(),
        "sprint-42, current",
        "raw_args preserved"
    );
}

#[test]
fn test_tagged_mps_resolver_sprint_block() {
    let els = parse_tagged_mps_fixture();
    let r = RefResolver::new(&els);
    assert_eq!(
        r.to_human("20260502.1"),
        Some("mps-1"),
        "sprint mps → mps-1"
    );
    assert_eq!(r.to_epoch("mps-1"), Some("20260502.1"), "roundtrip");
}

#[test]
fn test_tagged_mps_children_refs() {
    let els = parse_tagged_mps_fixture();
    let r = RefResolver::new(&els);
    assert_eq!(
        r.to_human("20260502.1.1"),
        Some("mps-1.1"),
        "task inside sprint"
    );
    assert_eq!(
        r.to_human("20260502.1.2"),
        Some("mps-1.2"),
        "note inside sprint"
    );
    assert_eq!(
        r.to_human("20260502.1.3"),
        Some("mps-1.3"),
        "done task inside sprint"
    );
    assert_eq!(r.to_epoch("mps-1.1"), Some("20260502.1.1"));
    assert_eq!(r.to_epoch("mps-1.2"), Some("20260502.1.2"));
    assert_eq!(r.to_epoch("mps-1.3"), Some("20260502.1.3"));
}

#[test]
fn test_tagged_mps_child_types() {
    let els = parse_tagged_mps_fixture();
    assert_eq!(
        els.get("20260502.1.1").unwrap().kind(),
        ElementKind::Task,
        ".1.1 is Task"
    );
    assert_eq!(
        els.get("20260502.1.2").unwrap().kind(),
        ElementKind::Note,
        ".1.2 is Note"
    );
    assert_eq!(
        els.get("20260502.1.3").unwrap().kind(),
        ElementKind::Task,
        ".1.3 is Task"
    );
}

#[test]
fn test_tagged_mps_child_statuses() {
    let els = parse_tagged_mps_fixture();
    if let Element::Task { data, .. } = els.get("20260502.1.1").unwrap() {
        assert!(data.is_open(), "sprint planning task is open");
    }
    if let Element::Task { data, .. } = els.get("20260502.1.3").unwrap() {
        assert!(data.is_done(), "backend task is done");
        assert!(
            data.tags.contains(&"backend".to_string()),
            "backend tag preserved"
        );
    }
}

#[test]
fn test_tagged_mps_note_has_review_tag() {
    let els = parse_tagged_mps_fixture();
    let note = els.get("20260502.1.2").unwrap();
    assert!(
        note.tags().contains(&"review".to_string()),
        "note has review tag"
    );
}

#[test]
fn test_note_in_task_ref_path() {
    let els = parse_tagged_mps_fixture();
    let note = els.get("20260502.2.1").expect("note inside done task");
    assert_eq!(note.kind(), ElementKind::Note, "it is a note");
}

#[test]
fn test_note_in_task_resolver() {
    let els = parse_tagged_mps_fixture();
    let r = RefResolver::new(&els);
    // top-level done task → task-1 (first Task at depth 1)
    assert_eq!(
        r.to_human("20260502.2"),
        Some("task-1"),
        "top-level done task → task-1"
    );
    // note nested inside it → task-1.1 (not note-1)
    assert_eq!(
        r.to_human("20260502.2.1"),
        Some("task-1.1"),
        "note in task → task-1.1"
    );
    assert_eq!(r.to_epoch("task-1"), Some("20260502.2"), "roundtrip task-1");
    assert_eq!(
        r.to_epoch("task-1.1"),
        Some("20260502.2.1"),
        "roundtrip task-1.1"
    );
}

#[test]
fn test_bare_top_level_note_gets_note_counter() {
    // @note{ Bare top-level note } is at depth 1 → note-1.
    // The notes inside the mps block (mps-1.2) and inside the task (task-1.1)
    // are at depth 2 and do NOT consume the depth-1 note counter.
    let els = parse_tagged_mps_fixture();
    let r = RefResolver::new(&els);
    assert_eq!(
        r.to_human("20260502.3"),
        Some("note-1"),
        "bare top-level note → note-1"
    );
    assert_eq!(r.to_epoch("note-1"), Some("20260502.3"), "roundtrip");
}

#[test]
fn test_nested_notes_do_not_inflate_note_counter() {
    // Only the bare note at depth 1 gets a note-N ref.
    // The note inside the mps block is mps-1.2 and the note inside the task is task-1.1.
    let els = parse_tagged_mps_fixture();
    let r = RefResolver::new(&els);
    assert!(
        r.to_epoch("note-2").is_none(),
        "no note-2 — only one top-level note"
    );
}

#[test]
fn test_tagged_mps_done_task_top_level_ref() {
    let els = parse_tagged_mps_fixture();
    let done = els.get("20260502.2").unwrap();
    if let Element::Task { data, .. } = done {
        assert!(data.is_done(), "top-level done task");
    }
}

#[test]
fn test_tagged_mps_done_task_body_contains_note_syntax() {
    let els = parse_tagged_mps_fixture();
    let body = els.get("20260502.2").unwrap().body_str();
    assert!(body.contains("Top level done task"), "own text");
    assert!(body.contains("@note{"), "nested note syntax in body");
}

#[test]
fn test_tagged_mps_resolver_full_roundtrip() {
    let els = parse_tagged_mps_fixture();
    let r = RefResolver::new(&els);
    let pairs = [
        ("20260502.1", "mps-1"),
        ("20260502.1.1", "mps-1.1"),
        ("20260502.1.2", "mps-1.2"),
        ("20260502.1.3", "mps-1.3"),
        ("20260502.2", "task-1"),
        ("20260502.2.1", "task-1.1"),
        ("20260502.3", "note-1"),
    ];
    for (epoch, human) in &pairs {
        assert_eq!(r.to_human(epoch), Some(*human), "epoch→human: {}", epoch);
        assert_eq!(r.to_epoch(human), Some(*epoch), "human→epoch: {}", human);
    }
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 30: character element type
// ═══════════════════════════════════════════════════════════════════════════

const CHAR_FILE: &str = "20260601.1900000001.mps";

fn parse_char_fixture() -> IndexMap<String, Element> {
    parse_fixture(CHAR_FILE)
}

fn char_elements(els: &IndexMap<String, Element>) -> Vec<&Element> {
    els.values()
        .filter(|e| e.kind() == ElementKind::Character)
        .collect()
}

// ── Parser correctness ────────────────────────────────────────────────────

#[test]
fn test_character_parser_produces_character_kind() {
    let els = parse_char_fixture();
    assert!(
        els.values().any(|e| e.kind() == ElementKind::Character),
        "fixture must contain at least one Character element"
    );
}

#[test]
fn test_character_parser_name_and_tags() {
    let els = parse_char_fixture();
    let el = char_elements(&els)
        .into_iter()
        .find(|e| {
            if let Element::Character { data, .. } = e {
                data.name.as_deref() == Some("Mahfuz Vai")
            } else {
                false
            }
        })
        .expect("Mahfuz Vai character not found");
    if let Element::Character { data, .. } = el {
        assert_eq!(data.name.as_deref(), Some("Mahfuz Vai"));
        assert!(data.tags.contains(&"mahfuz-vai".to_string()));
        assert!(data.tags.contains(&"favor".to_string()));
    }
}

#[test]
fn test_character_parser_tags_only_no_name() {
    let els = parse_char_fixture();
    // The second character has [favor, colleague] — no name
    let nameless: Vec<_> = char_elements(&els)
        .into_iter()
        .filter(|e| {
            if let Element::Character { data, .. } = e {
                data.name.is_none()
            } else {
                false
            }
        })
        .collect();
    assert!(
        !nameless.is_empty(),
        "fixture must have at least one nameless character"
    );
    let el = nameless[0];
    if let Element::Character { data, .. } = el {
        assert!(
            data.tags.contains(&"colleague".to_string())
                || data.tags.contains(&"favor".to_string())
        );
    }
}

#[test]
fn test_character_parser_bare_braces_no_args() {
    let els = parse_char_fixture();
    // The anonymous character has no args at all
    let bare = char_elements(&els)
        .into_iter()
        .find(|e| e.raw_args().is_empty())
        .expect("bare character (no args) not found");
    if let Element::Character { data, .. } = bare {
        assert!(data.name.is_none());
        assert!(data.tags.is_empty());
    }
}

#[test]
fn test_character_parser_name_with_spaces_preserved() {
    let els = parse_char_fixture();
    let el = char_elements(&els)
        .into_iter()
        .find(|e| {
            if let Element::Character { data, .. } = e {
                data.name.as_deref() == Some("Dr. Alice Smith")
            } else {
                false
            }
        })
        .expect("Dr. Alice Smith character not found");
    if let Element::Character { data, .. } = el {
        assert_eq!(data.name.as_deref(), Some("Dr. Alice Smith"));
        assert!(data.tags.contains(&"mentor".to_string()));
    }
}

#[test]
fn test_character_not_parsed_as_unknown() {
    let els = parse_char_fixture();
    for el in els.values() {
        if el.sign() == "character" {
            assert!(!el.is_unknown(), "character elements must not be Unknown");
        }
    }
}

#[test]
fn test_character_kind_is_character() {
    let els = parse_char_fixture();
    for el in els.values().filter(|e| e.sign() == "character") {
        assert_eq!(el.kind(), ElementKind::Character);
    }
}

#[test]
fn test_character_is_not_is_mps_group() {
    let els = parse_char_fixture();
    for el in char_elements(&els) {
        assert!(!el.is_mps_group());
    }
}

// ── ElementKind + Element trait ───────────────────────────────────────────

#[test]
fn test_element_kind_character_display() {
    assert_eq!(format!("{}", ElementKind::Character), "character");
}

#[test]
fn test_element_from_parts_character_not_unknown() {
    let el = Element::from_parts(
        "character",
        "name: Mahfuz Vai, mahfuz-vai".to_string(),
        vec![],
        "He helped me".to_string(),
    );
    assert!(!el.is_unknown());
    assert_eq!(el.kind(), ElementKind::Character);
    assert_eq!(el.sign(), "character");
}

#[test]
fn test_element_character_tags_accessible() {
    let el = Element::from_parts(
        "character",
        "name: Mahfuz Vai, mahfuz-vai, favor".to_string(),
        vec![],
        "body".to_string(),
    );
    assert!(el.tags().contains(&"mahfuz-vai".to_string()));
    assert!(el.tags().contains(&"favor".to_string()));
}

#[test]
fn test_element_character_typed_attrs_with_name() {
    let el = Element::from_parts(
        "character",
        "name: Mahfuz Vai".to_string(),
        vec![],
        "body".to_string(),
    );
    let attrs = el.typed_attrs();
    assert_eq!(attrs.len(), 1);
    assert_eq!(attrs[0].0, "name");
    assert_eq!(attrs[0].1, "Mahfuz Vai");
}

#[test]
fn test_element_character_typed_attrs_without_name() {
    let el = Element::from_parts(
        "character",
        "mahfuz-vai".to_string(),
        vec![],
        "body".to_string(),
    );
    assert!(el.typed_attrs().is_empty());
}

#[test]
fn test_element_character_body_str() {
    let el = Element::from_parts(
        "character",
        "name: Mahfuz Vai".to_string(),
        vec![],
        "He helped me today".to_string(),
    );
    assert_eq!(el.body_str(), "He helped me today");
}

// ── Filtering ─────────────────────────────────────────────────────────────

#[test]
fn test_type_filter_character_only() {
    use mps::commands::{visible, DisplayOpts};
    let els = parse_char_fixture();
    let opts = DisplayOpts {
        type_filter: Some("character".to_string()),
        tag_filter: None,
        status_filter: None,
        name_filter: None,
    };
    for el in els
        .values()
        .filter(|e| !e.is_mps_group() && !e.is_unknown())
    {
        if el.kind() == ElementKind::Character {
            assert!(visible(el, &opts), "character should pass type filter");
        } else {
            assert!(
                !visible(el, &opts),
                "non-character should fail character type filter"
            );
        }
    }
}

#[test]
fn test_type_filter_task_excludes_character() {
    use mps::commands::{visible, DisplayOpts};
    let els = parse_char_fixture();
    let opts = DisplayOpts {
        type_filter: Some("task".to_string()),
        tag_filter: None,
        status_filter: None,
        name_filter: None,
    };
    for el in char_elements(&els) {
        assert!(
            !visible(el, &opts),
            "character must not pass task type filter"
        );
    }
}

#[test]
fn test_tag_filter_matches_character() {
    use mps::commands::{visible, DisplayOpts};
    let els = parse_char_fixture();
    let opts = DisplayOpts {
        type_filter: None,
        tag_filter: Some("mahfuz-vai".to_string()),
        status_filter: None,
        name_filter: None,
    };
    let matched = els
        .values()
        .filter(|e| !e.is_mps_group() && !e.is_unknown() && visible(e, &opts))
        .count();
    assert!(
        matched >= 1,
        "at least one character should match tag mahfuz-vai"
    );
}

#[test]
fn test_name_filter_matches_character() {
    use mps::commands::{visible, DisplayOpts};
    let els = parse_char_fixture();
    let opts = DisplayOpts {
        type_filter: None,
        tag_filter: None,
        status_filter: None,
        name_filter: Some("Mahfuz Vai".to_string()),
    };
    let matched = els
        .values()
        .filter(|e| !e.is_mps_group() && !e.is_unknown() && visible(e, &opts))
        .count();
    assert_eq!(matched, 1, "exactly one character named Mahfuz Vai");
}

#[test]
fn test_name_filter_case_insensitive() {
    use mps::commands::{visible, DisplayOpts};
    let els = parse_char_fixture();
    let opts = DisplayOpts {
        type_filter: None,
        tag_filter: None,
        status_filter: None,
        name_filter: Some("mahfuz vai".to_string()),
    };
    let matched = els
        .values()
        .filter(|e| !e.is_mps_group() && !e.is_unknown() && visible(e, &opts))
        .count();
    assert_eq!(matched, 1, "name filter should be case-insensitive");
}

#[test]
fn test_name_filter_excludes_nameless_character() {
    use mps::commands::{visible, DisplayOpts};
    let els = parse_char_fixture();
    let opts = DisplayOpts {
        type_filter: None,
        tag_filter: None,
        status_filter: None,
        name_filter: Some("Mahfuz Vai".to_string()),
    };
    // The nameless character (bare braces) must not match
    let nameless = els
        .values()
        .find(|e| e.kind() == ElementKind::Character && e.raw_args().is_empty())
        .expect("bare character must exist");
    assert!(!visible(nameless, &opts));
}

#[test]
fn test_name_filter_excludes_task() {
    use mps::commands::{visible, DisplayOpts};
    let els = parse_char_fixture();
    let opts = DisplayOpts {
        type_filter: None,
        tag_filter: None,
        status_filter: None,
        name_filter: Some("Mahfuz Vai".to_string()),
    };
    let task = els
        .values()
        .find(|e| e.kind() == ElementKind::Task)
        .expect("task in fixture");
    assert!(!visible(task, &opts), "task must not pass name filter");
}

#[test]
fn test_status_filter_excludes_character() {
    use mps::commands::{visible, DisplayOpts};
    let els = parse_char_fixture();
    let opts = DisplayOpts {
        type_filter: None,
        tag_filter: None,
        status_filter: Some("open".to_string()),
        name_filter: None,
    };
    for el in char_elements(&els) {
        assert!(!visible(el, &opts), "character must not pass status filter");
    }
}

// ── Display ───────────────────────────────────────────────────────────────

#[test]
fn test_type_badge_character_contains_bracket_text() {
    use mps::commands::type_badge;
    let badge = type_badge(&ElementKind::Character);
    // Strip ANSI codes by checking the raw string contains "character"
    assert!(
        badge.contains("character"),
        "badge must contain 'character'"
    );
}

#[test]
fn test_element_extra_character_with_name() {
    use mps::commands::element_extra;
    let el = Element::from_parts(
        "character",
        "name: Mahfuz Vai".to_string(),
        vec![],
        "body".to_string(),
    );
    let extra = element_extra(&el);
    assert!(
        extra.contains("Mahfuz Vai"),
        "element_extra must include the name"
    );
}

#[test]
fn test_element_extra_character_without_name() {
    use mps::commands::element_extra;
    let el = Element::from_parts(
        "character",
        "mahfuz-vai".to_string(),
        vec![],
        "body".to_string(),
    );
    let extra = element_extra(&el);
    assert!(
        extra.is_empty(),
        "element_extra must be empty when name absent"
    );
}

// ── Append roundtrip ──────────────────────────────────────────────────────

#[test]
fn test_character_append_roundtrip_name_and_tags() {
    let (_dir, store) = fixture_store();
    let date = make_date(2026, 6, 30);
    store
        .append(
            "character",
            "He did me a great favor",
            &["mahfuz-vai".to_string(), "favor".to_string()],
            &[("name", "Mahfuz Vai")],
            date,
        )
        .unwrap();
    let els = store.parse_date(date).unwrap();
    let el = els
        .values()
        .find(|e| e.kind() == ElementKind::Character)
        .expect("character not found after append");
    if let Element::Character { data, .. } = el {
        assert_eq!(data.name.as_deref(), Some("Mahfuz Vai"));
        assert!(data.tags.contains(&"mahfuz-vai".to_string()));
        assert!(data.tags.contains(&"favor".to_string()));
    }
    assert_eq!(el.body_str().trim(), "He did me a great favor");
}

#[test]
fn test_character_append_roundtrip_no_name() {
    let (_dir, store) = fixture_store();
    let date = make_date(2026, 6, 30);
    store
        .append(
            "character",
            "Anonymous observation",
            &["anonymous".to_string()],
            &[],
            date,
        )
        .unwrap();
    let els = store.parse_date(date).unwrap();
    let el = els
        .values()
        .find(|e| e.kind() == ElementKind::Character)
        .expect("character not found after append");
    if let Element::Character { data, .. } = el {
        assert!(data.name.is_none());
        assert!(data.tags.contains(&"anonymous".to_string()));
    }
}

#[test]
fn test_character_rewrite_name_attr() {
    let (_dir, store) = fixture_store();
    let date = make_date(2026, 6, 30);
    store
        .append("character", "Knew them before", &[], &[], date)
        .unwrap();
    let els = store.parse_date(date).unwrap();
    let (ref_key, _) = els
        .iter()
        .find(|(_, e)| e.kind() == ElementKind::Character)
        .expect("character not found");
    let ref_key = ref_key.clone();

    let mut new_attrs = HashMap::new();
    new_attrs.insert("name".to_string(), "Late Name".to_string());
    let ok = store.rewrite_element(&ref_key, &new_attrs, date).unwrap();
    assert!(ok, "rewrite_element must succeed");

    let els2 = store.parse_date(date).unwrap();
    let el = els2
        .values()
        .find(|e| e.kind() == ElementKind::Character)
        .unwrap();
    if let Element::Character { data, .. } = el {
        assert_eq!(data.name.as_deref(), Some("Late Name"));
    }
}

// ── Export ────────────────────────────────────────────────────────────────

#[test]
fn test_export_character_kind_field() {
    // build_record is private; test via store + parse
    let els = parse_char_fixture();
    for el in char_elements(&els) {
        assert_eq!(el.sign(), "character");
    }
}

#[test]
fn test_character_stats_counting() {
    let els = parse_char_fixture();
    let char_count = els
        .values()
        .filter(|e| e.kind() == ElementKind::Character)
        .count();
    assert_eq!(char_count, 4, "fixture has exactly 4 character elements");
    let task_count = els
        .values()
        .filter(|e| e.kind() == ElementKind::Task)
        .count();
    assert_eq!(task_count, 1, "fixture has exactly 1 task element");
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 31: Pre-release robustness sweep
// ═══════════════════════════════════════════════════════════════════════════

// ── Config ────────────────────────────────────────────────────────────────

#[test]
fn test_config_init_creates_file_with_defaults() {
    use mps::config::Config;
    let dir = tempfile::tempdir().unwrap();
    let path = dir.path().join(".mps_config.yaml");
    assert!(!path.exists());
    Config::init(&path).unwrap();
    assert!(path.exists());
    let cfg = Config::load(&path).unwrap();
    assert_eq!(cfg.git_remote, "origin");
    assert_eq!(cfg.git_branch, "master");
    assert_eq!(cfg.default_command, "open");
    assert!(cfg.type_aliases.is_empty());
    assert!(cfg.command_aliases.is_empty());
}

#[test]
fn test_config_init_is_idempotent() {
    use mps::config::Config;
    let dir = tempfile::tempdir().unwrap();
    let path = dir.path().join(".mps_config.yaml");
    Config::init(&path).unwrap();
    let content_first = std::fs::read_to_string(&path).unwrap();
    Config::init(&path).unwrap(); // second call must not overwrite
    let content_second = std::fs::read_to_string(&path).unwrap();
    assert_eq!(content_first, content_second);
}

#[test]
fn test_config_load_nonexistent_returns_error() {
    use mps::config::Config;
    use mps::error::MpsError;
    let path = std::path::Path::new("/tmp/mps_does_not_exist_xyz.yaml");
    let err = Config::load(path).unwrap_err();
    assert!(matches!(err, MpsError::ConfigNotFound(_)));
}

#[test]
fn test_config_ensure_dirs_creates_directories() {
    use mps::config::Config;
    let dir = tempfile::tempdir().unwrap();
    let mps_dir = dir.path().join("mps_dir");
    let storage_dir = mps_dir.join("storage");
    let log_file = mps_dir.join("mps.log");
    let cfg = Config {
        mps_dir: mps_dir.clone(),
        storage_dir: storage_dir.clone(),
        log_file: log_file.clone(),
        git_remote: "origin".into(),
        git_branch: "master".into(),
        default_command: "open".into(),
        type_aliases: std::collections::HashMap::new(),
        command_aliases: std::collections::HashMap::new(),
        custom_tags: vec![],
        notify: mps::config::NotifyConfig::default(),
        serve: Default::default(),
    };
    assert!(!mps_dir.exists());
    cfg.ensure_dirs().unwrap();
    assert!(mps_dir.exists());
    assert!(storage_dir.exists());
    assert!(log_file.exists());
}

// ── Date parsing ──────────────────────────────────────────────────────────

#[test]
fn test_date_parse_rejects_garbage_string() {
    use mps::date_parse::parse_date;
    assert!(parse_date("not-a-date").is_err());
    assert!(parse_date("99999999").is_err());
    assert!(parse_date("the beginning of time").is_err());
}

#[test]
fn test_date_parse_rejects_empty_string() {
    use mps::date_parse::parse_date;
    assert!(parse_date("").is_err());
}

#[test]
fn test_date_parse_absolute_formats_accepted() {
    use mps::date_parse::parse_date;
    let d1 = parse_date("20260421").unwrap();
    let d2 = parse_date("2026-04-21").unwrap();
    assert_eq!(d1, d2);
    assert_eq!(d1.format("%Y-%m-%d").to_string(), "2026-04-21");
}

// ── ElementKind round-trip ────────────────────────────────────────────────

#[test]
fn test_element_kind_all_known_signs_round_trip() {
    for sign in &["task", "note", "log", "reminder", "mps", "character"] {
        let kind = ElementKind::from_sign(sign);
        assert_ne!(
            kind,
            ElementKind::Unknown,
            "sign '{}' must map to a known kind",
            sign
        );
        assert_eq!(
            kind.to_string(),
            *sign,
            "kind→sign must be identity for '{}'",
            sign
        );
    }
}

#[test]
fn test_element_kind_unknown_sign_maps_to_unknown() {
    let kind = ElementKind::from_sign("sprocket");
    assert_eq!(kind, ElementKind::Unknown);
}

// ── Append valid/invalid types ────────────────────────────────────────────

#[test]
fn test_store_append_all_valid_element_types() {
    let (_dir, store) = fixture_store();
    let date = make_date(2026, 9, 1);
    for kind in &["task", "note", "log", "reminder", "character"] {
        store
            .append(kind, &format!("body for {}", kind), &[], &[], date)
            .unwrap_or_else(|e| panic!("append '{}' failed: {}", kind, e));
    }
    let els = store.parse_date(date).unwrap();
    let kinds: Vec<&str> = els
        .values()
        .filter(|e| !e.is_mps_group() && !e.is_unknown())
        .map(|e| e.sign())
        .collect();
    assert!(kinds.contains(&"task"));
    assert!(kinds.contains(&"note"));
    assert!(kinds.contains(&"log"));
    assert!(kinds.contains(&"reminder"));
    assert!(kinds.contains(&"character"));
}

// ── DisplayOpts / visible() ───────────────────────────────────────────────

#[test]
fn test_visible_with_type_and_tag_filter_combined() {
    use mps::commands::{visible, DisplayOpts};
    let els = parse_char_fixture();
    let opts = DisplayOpts {
        type_filter: Some("character".into()),
        tag_filter: Some("favor".into()),
        status_filter: None,
        name_filter: None,
    };
    let matching: Vec<_> = els.values().filter(|e| visible(e, &opts)).collect();
    // Fixture has two character elements with "favor" tag
    assert_eq!(matching.len(), 2);
    assert!(matching.iter().all(|e| e.sign() == "character"));
    assert!(matching
        .iter()
        .all(|e| e.tags().iter().any(|t| t == "favor")));
}

#[test]
fn test_visible_name_filter_excludes_non_character() {
    use mps::commands::{visible, DisplayOpts};
    let opts = DisplayOpts {
        type_filter: None,
        tag_filter: None,
        status_filter: None,
        name_filter: Some("anyone".into()),
    };
    // A task should be excluded when name_filter is set
    let task_raw = "@task[work]{\n  Fix bug\n}\n";
    let els = parser::parse_str(task_raw);
    let all_excluded = els.values().all(|e| !visible(e, &opts));
    assert!(
        all_excluded,
        "name_filter must exclude non-character elements"
    );
}

#[test]
fn test_visible_status_filter_excludes_non_task() {
    use mps::commands::{visible, DisplayOpts};
    let opts = DisplayOpts {
        type_filter: None,
        tag_filter: None,
        status_filter: Some("open".into()),
        name_filter: None,
    };
    let raw = "@note{\n  Just a note\n}\n";
    let els = parser::parse_str(raw);
    let all_excluded = els.values().all(|e| !visible(e, &opts));
    assert!(all_excluded, "status_filter must exclude non-task elements");
}

// ── Character edge cases ──────────────────────────────────────────────────

#[test]
fn test_character_name_with_apostrophe() {
    let raw = "@character[name: O'Brien, colleague]{\n  Helped with the migration.\n}\n";
    let els = parser::parse_str(raw);
    let chars: Vec<_> = els.values().filter(|e| e.sign() == "character").collect();
    assert_eq!(chars.len(), 1);
    if let Element::Character { data, .. } = chars[0] {
        assert_eq!(data.name.as_deref(), Some("O'Brien"));
        assert!(data.tags.iter().any(|t| t == "colleague"));
    } else {
        panic!("expected Character element");
    }
}

#[test]
fn test_character_name_with_comma_inside_quotes_parsed_as_tag() {
    // Without quoting, "Smith, Jr" splits on comma — name gets "Smith" and "Jr" becomes a tag
    let raw = "@character[name: Smith, jr, trusted]{\n  Body\n}\n";
    let els = parser::parse_str(raw);
    let chars: Vec<_> = els.values().filter(|e| e.sign() == "character").collect();
    assert_eq!(chars.len(), 1);
    if let Element::Character { data, .. } = chars[0] {
        // After the colon, the raw_args parser splits on comma, so name ends at first comma
        assert_eq!(data.name.as_deref(), Some("Smith"));
    } else {
        panic!("expected Character element");
    }
}

#[test]
fn test_character_rewrite_roundtrip_preserves_body() {
    let (_dir, store) = fixture_store();
    let date = make_date(2026, 9, 10);
    store
        .append(
            "character",
            "A trusted advisor.",
            &["mentor".into()],
            &[("name", "Dr. Kay")],
            date,
        )
        .unwrap();
    let els = store.parse_date(date).unwrap();
    let (ref_key, _) = els.iter().find(|(_, e)| e.sign() == "character").unwrap();
    let ref_key = ref_key.clone();

    let mut attrs = HashMap::new();
    attrs.insert("name".to_string(), "Dr. Kay".to_string());
    attrs.insert("extra".to_string(), "added".to_string());
    let updated = store.rewrite_element(&ref_key, &attrs, date).unwrap();
    assert!(updated);

    let els2 = store.parse_date(date).unwrap();
    let char_el = els2.values().find(|e| e.sign() == "character").unwrap();
    assert!(char_el.body_str().contains("A trusted advisor."));
}

// ── Export ────────────────────────────────────────────────────────────────

#[test]
fn test_export_csv_character_row_has_name_field() {
    let (_dir, store) = fixture_store();
    let date = make_date(2026, 9, 15);
    store
        .append(
            "character",
            "Met at the conference.",
            &["networking".into()],
            &[("name", "Sam Lee")],
            date,
        )
        .unwrap();

    let els = store.parse_date(date).unwrap();
    let char_el = els.values().find(|e| e.sign() == "character").unwrap();
    if let Element::Character { data, .. } = char_el {
        assert_eq!(data.name.as_deref(), Some("Sam Lee"));
    } else {
        panic!("expected Character element");
    }
    // typed_attrs must contain name for CSV export
    let attrs = char_el.typed_attrs();
    assert!(
        attrs.iter().any(|(k, v)| k == "name" && v == "Sam Lee"),
        "typed_attrs must expose name for export; got {:?}",
        attrs
    );
}

#[test]
fn test_export_log_typed_attrs_start_end() {
    let (_dir, store) = fixture_store();
    let date = make_date(2026, 9, 16);
    store
        .append(
            "log",
            "Deep work.",
            &[],
            &[("start", "09:00"), ("end", "11:30")],
            date,
        )
        .unwrap();

    let els = store.parse_date(date).unwrap();
    let log_el = els.values().find(|e| e.sign() == "log").unwrap();
    let attrs = log_el.typed_attrs();
    let starts: Vec<_> = attrs.iter().filter(|(k, _)| k == "start").collect();
    let ends: Vec<_> = attrs.iter().filter(|(k, _)| k == "end").collect();
    assert!(!starts.is_empty(), "log typed_attrs must contain start");
    assert!(!ends.is_empty(), "log typed_attrs must contain end");
}

// ── Store rewrite edge cases ──────────────────────────────────────────────

#[test]
fn test_rewrite_sets_status_open_to_done() {
    let (_dir, store) = fixture_store();
    let date = make_date(2026, 9, 20);
    store
        .append("task", "A task to complete.", &["work".into()], &[], date)
        .unwrap();

    let els = store.parse_date(date).unwrap();
    let (ref_key, task) = els.iter().find(|(_, e)| e.sign() == "task").unwrap();
    if let Element::Task { data, .. } = task {
        assert!(!data.is_done(), "task must start as open");
    }
    let ref_key = ref_key.clone();
    let mut attrs = HashMap::new();
    attrs.insert("status".to_string(), "done".to_string());
    let updated = store.rewrite_element(&ref_key, &attrs, date).unwrap();
    assert!(updated);

    let els2 = store.parse_date(date).unwrap();
    let task2 = els2.values().find(|e| e.sign() == "task").unwrap();
    if let Element::Task { data, .. } = task2 {
        assert!(data.is_done(), "task must be done after rewrite");
    } else {
        panic!("expected Task element");
    }
}

#[test]
fn test_rewrite_reminder_at_time() {
    let (_dir, store) = fixture_store();
    let date = make_date(2026, 9, 21);
    store
        .append("reminder", "Call the client.", &[], &[("at", "3pm")], date)
        .unwrap();

    let els = store.parse_date(date).unwrap();
    let (ref_key, _) = els.iter().find(|(_, e)| e.sign() == "reminder").unwrap();
    let ref_key = ref_key.clone();

    let mut attrs = HashMap::new();
    attrs.insert("at".to_string(), "5pm".to_string());
    let updated = store.rewrite_element(&ref_key, &attrs, date).unwrap();
    assert!(updated);

    let els2 = store.parse_date(date).unwrap();
    let rem = els2.values().find(|e| e.sign() == "reminder").unwrap();
    if let Element::Reminder { data, .. } = rem {
        assert_eq!(data.at.as_deref(), Some("5pm"));
    } else {
        panic!("expected Reminder element");
    }
}

// ── Ref resolver ──────────────────────────────────────────────────────────

#[test]
fn test_ref_resolver_character_gets_own_counter() {
    let raw = "@character[name: Alice]{\n  Body A\n}\n@character[name: Bob]{\n  Body B\n}\n@task{\n  A task\n}\n";
    let els = parser::parse_str(raw);
    let resolver = RefResolver::new(&els);

    let char_refs: Vec<&str> = els
        .keys()
        .filter(|k| els[*k].sign() == "character")
        .map(|k| resolver.to_human(k).unwrap_or("?"))
        .collect();
    assert!(
        char_refs.iter().any(|r| r.starts_with("character-1")),
        "first character must be character-1"
    );
    assert!(
        char_refs.iter().any(|r| r.starts_with("character-2")),
        "second character must be character-2"
    );

    let task_refs: Vec<&str> = els
        .keys()
        .filter(|k| els[*k].sign() == "task")
        .map(|k| resolver.to_human(k).unwrap_or("?"))
        .collect();
    assert!(task_refs.iter().any(|r| r.starts_with("task-1")));
}

#[test]
fn test_ref_resolver_to_epoch_roundtrip_for_character() {
    let raw = "@character[name: Carol]{\n  Notes on Carol\n}\n";
    let els = parser::parse_str(raw);
    let resolver = RefResolver::new(&els);

    let epoch_key = els.keys().find(|k| els[*k].sign() == "character").unwrap();
    let human = resolver.to_human(epoch_key).unwrap();
    let back = resolver.to_epoch(human).unwrap();
    assert_eq!(back, epoch_key);
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 32: Malformed / corrupt file robustness
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_parse_empty_file_does_not_crash() {
    let dir = tempfile::tempdir().unwrap();
    let path = dir.path().join("20260601.1000000000.mps");
    std::fs::write(&path, "").unwrap();
    let els = parser::parse_file(&path).unwrap();
    let meaningful: Vec<_> = els
        .values()
        .filter(|e| !e.is_mps_group() && !e.is_unknown())
        .collect();
    assert!(meaningful.is_empty(), "empty file must yield no real elements");
}

#[test]
fn test_parse_whitespace_only_file_does_not_crash() {
    let dir = tempfile::tempdir().unwrap();
    let path = dir.path().join("20260602.1000000001.mps");
    std::fs::write(&path, "   \n\t\n  \n").unwrap();
    let els = parser::parse_file(&path).unwrap();
    let meaningful: Vec<_> = els
        .values()
        .filter(|e| !e.is_mps_group() && !e.is_unknown())
        .collect();
    assert!(meaningful.is_empty());
}

#[test]
fn test_parse_str_unclosed_brace_does_not_crash() {
    // Missing closing `}` — parser should not panic or infinite-loop
    let raw = "@mps{\n@task[work]{\n  Unclosed task body\n";
    let els = parser::parse_str(raw);
    // Just needs to complete; no assertion on element count — best-effort parse
    let _ = els;
}

#[test]
fn test_parse_str_bom_prefix_does_not_crash() {
    // UTF-8 BOM (EF BB BF) prepended to content
    let bom = "\u{FEFF}";
    let raw = format!("{}@mps{{\n@task[work]{{\n  BOM task\n}}\n}}\n", bom);
    let els = parser::parse_str(&raw);
    // Parser may or may not find the task depending on BOM handling; it must not panic
    let _ = els;
}

#[test]
fn test_parse_str_no_mps_wrapper_yields_no_elements() {
    // Elements outside an @mps{} wrapper are not counted as real elements
    let raw = "@task[work]{\n  Orphan task\n}\n";
    let els = parser::parse_str(raw);
    let tasks: Vec<_> = els.values().filter(|e| e.sign() == "task").collect();
    // Either 0 (strict parser) or 1 (lenient) — must not panic either way
    let _ = tasks;
}

#[test]
fn test_parse_str_nested_braces_in_body_do_not_crash() {
    // Body containing literal `{` and `}` characters (JSON-like)
    let raw = "@mps{\n@note[dev]{\n  config = {\"key\": \"value\"}\n}\n}\n";
    let els = parser::parse_str(raw);
    // May end early but must not panic
    let _ = els;
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 33: Windows CRLF line endings
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_parse_crlf_task_body_preserved() {
    // \r\n line endings — parser should produce a task element
    let raw = "@mps{\r\n@task[work, status: open]{\r\n  CRLF task body\r\n}\r\n}\r\n";
    let els = parser::parse_str(raw);
    let tasks: Vec<_> = els.values().filter(|e| e.sign() == "task").collect();
    assert!(!tasks.is_empty(), "CRLF file must produce at least one task");
}

#[test]
fn test_parse_crlf_multiple_elements() {
    let raw = "@mps{\r\n@task[work]{\r\n  Task one\r\n}\r\n@note[journal]{\r\n  Note one\r\n}\r\n}\r\n";
    let els = parser::parse_str(raw);
    let real: Vec<_> = els
        .values()
        .filter(|e| !e.is_mps_group() && !e.is_unknown())
        .collect();
    assert!(real.len() >= 2, "must find both task and note in CRLF file");
}

#[test]
fn test_parse_crlf_tags_parsed_correctly() {
    let raw = "@mps{\r\n@task[work, urgent, status: open]{\r\n  Tagged CRLF task\r\n}\r\n}\r\n";
    let els = parser::parse_str(raw);
    let task = els.values().find(|e| e.sign() == "task");
    assert!(task.is_some(), "task must be found");
    let tags = task.unwrap().tags();
    assert!(tags.contains(&"work".to_string()));
    assert!(tags.contains(&"urgent".to_string()));
}

#[test]
fn test_parse_crlf_reminder_at_attribute() {
    let raw = "@mps{\r\n@reminder[at: 9am]{\r\n  Morning stand-up\r\n}\r\n}\r\n";
    let els = parser::parse_str(raw);
    let rem = els.values().find(|e| e.sign() == "reminder");
    assert!(rem.is_some(), "reminder must be found in CRLF file");
    if let Some(Element::Reminder { data, .. }) = rem {
        assert_eq!(data.at.as_deref(), Some("9am"));
    }
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 34: Empty store operations
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_empty_store_all_files_returns_empty() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let files = store.all_files().unwrap();
    assert!(files.is_empty(), "empty dir must yield no .mps files");
}

#[test]
fn test_empty_store_files_since_returns_empty() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let files = store.files_since(make_date(2020, 1, 1)).unwrap();
    assert!(files.is_empty());
}

#[test]
fn test_empty_store_all_file_dates_returns_empty() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let dates = store.all_file_dates().unwrap();
    assert!(dates.is_empty());
}

#[test]
fn test_empty_store_search_returns_empty() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let results = store.search("anything", None, None, None).unwrap();
    assert!(results.is_empty());
}

#[test]
fn test_empty_store_parse_date_returns_empty() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let els = store.parse_date(make_date(2026, 6, 1)).unwrap();
    let real: Vec<_> = els
        .values()
        .filter(|e| !e.is_mps_group() && !e.is_unknown())
        .collect();
    assert!(real.is_empty());
}

#[test]
fn test_empty_store_delete_returns_false() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let result = store.delete_element("task-1", make_date(2026, 6, 1)).unwrap();
    assert!(!result, "delete on empty store must return false");
}

#[test]
fn test_non_mps_files_ignored_by_all_files() {
    let dir = tempfile::tempdir().unwrap();
    // Create files that don't conform to the YYYYMMDD[.epoch].mps naming pattern
    std::fs::write(dir.path().join("notes.txt"), "hello").unwrap();
    std::fs::write(dir.path().join("2026.mps"), "only 4 digits").unwrap();
    std::fs::write(dir.path().join(".gitignore"), ".mps.local").unwrap();
    std::fs::write(dir.path().join("config.yaml"), "key: val").unwrap();
    let store = Store::new(dir.path());
    let files = store.all_files().unwrap();
    // None of the above match mps_file_name_regexp (need YYYYMMDD[.epoch].mps)
    assert!(files.is_empty(), "non-conforming filenames must be excluded");
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 35: Delete edge cases
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_delete_only_element_leaves_no_real_elements() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 6, 10);
    store
        .append("task", "Only task", &[], &[], date)
        .unwrap();

    let els = store.parse_date(date).unwrap();
    let key = els
        .keys()
        .find(|k| els[*k].sign() == "task")
        .unwrap()
        .clone();
    let deleted = store.delete_element(&key, date).unwrap();
    assert!(deleted);

    let els2 = store.parse_date(date).unwrap();
    let real: Vec<_> = els2
        .values()
        .filter(|e| !e.is_mps_group() && !e.is_unknown())
        .collect();
    assert!(real.is_empty(), "no real elements must remain after sole element deleted");
}

#[test]
fn test_delete_second_of_two_elements_first_survives() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 6, 11);
    store.append("task", "First task", &[], &[], date).unwrap();
    store.append("task", "Second task", &[], &[], date).unwrap();

    let els = store.parse_date(date).unwrap();
    let task_keys: Vec<String> = els
        .keys()
        .filter(|k| els[*k].sign() == "task")
        .cloned()
        .collect();
    assert_eq!(task_keys.len(), 2);

    let second_body = "Second task";
    let second_key = els
        .iter()
        .find(|(_, e)| e.sign() == "task" && e.body_str().contains(second_body))
        .map(|(k, _)| k.clone())
        .unwrap();

    let deleted = store.delete_element(&second_key, date).unwrap();
    assert!(deleted);

    let els2 = store.parse_date(date).unwrap();
    let remaining_tasks: Vec<_> = els2.values().filter(|e| e.sign() == "task").collect();
    assert_eq!(remaining_tasks.len(), 1);
    assert!(remaining_tasks[0].body_str().contains("First task"));
}

#[test]
fn test_delete_nonexistent_human_ref_returns_false() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 6, 12);
    store.append("note", "Some note", &[], &[], date).unwrap();
    // Human ref for a task that doesn't exist
    let result = store.delete_element("task-99", date).unwrap();
    assert!(!result);
}

#[test]
fn test_delete_nonexistent_epoch_ref_returns_false() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 6, 13);
    store.append("note", "Some note", &[], &[], date).unwrap();
    // Epoch ref from a date that has no file
    let result = store
        .delete_element("20990101.9999999999.1", make_date(2099, 1, 1))
        .unwrap();
    assert!(!result);
}

#[test]
fn test_delete_first_of_two_leaves_second() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 6, 14);
    store.append("note", "Alpha note", &[], &[], date).unwrap();
    store.append("note", "Beta note", &[], &[], date).unwrap();

    let els = store.parse_date(date).unwrap();
    let first_key = els
        .iter()
        .find(|(_, e)| e.sign() == "note" && e.body_str().contains("Alpha"))
        .map(|(k, _)| k.clone())
        .unwrap();

    store.delete_element(&first_key, date).unwrap();

    let els2 = store.parse_date(date).unwrap();
    let notes: Vec<_> = els2.values().filter(|e| e.sign() == "note").collect();
    assert_eq!(notes.len(), 1);
    assert!(notes[0].body_str().contains("Beta"));
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 36: Extract / replace body — unicode and multiline
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_extract_body_unicode_preserved() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 6, 20);
    let body = "Résumé: αβγ — 日本語 🎉";
    store.append("note", body, &[], &[], date).unwrap();

    let els = store.parse_date(date).unwrap();
    let key = els
        .keys()
        .find(|k| els[*k].sign() == "note")
        .unwrap()
        .clone();

    let extracted = store.extract_element_body(&key, date).unwrap();
    assert!(
        extracted.is_some(),
        "extract must return Some for a valid note"
    );
    let text = extracted.unwrap();
    assert!(text.contains("Résumé"), "extracted body must contain 'Résumé'");
    assert!(text.contains("日本語"), "extracted body must contain Japanese");
    assert!(text.contains("🎉"), "extracted body must contain emoji");
}

#[test]
fn test_replace_body_unicode_roundtrip() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 6, 21);
    store.append("note", "Initial body", &[], &[], date).unwrap();

    let els = store.parse_date(date).unwrap();
    let key = els
        .keys()
        .find(|k| els[*k].sign() == "note")
        .unwrap()
        .clone();

    let new_body = "  Новый текст: 中文 العربية\n  line2: ñoño\n";
    let replaced = store.replace_element_body(&key, new_body, date).unwrap();
    assert!(replaced, "replace must return true for a real element");

    let extracted = store.extract_element_body(&key, date).unwrap().unwrap();
    assert!(
        extracted.contains("Новый"),
        "replaced body must contain Cyrillic"
    );
    assert!(extracted.contains("中文"), "replaced body must contain Chinese");
}

#[test]
fn test_replace_body_multiline() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 6, 22);
    store.append("task", "Old body", &[], &[], date).unwrap();

    let els = store.parse_date(date).unwrap();
    let key = els
        .keys()
        .find(|k| els[*k].sign() == "task")
        .unwrap()
        .clone();

    let new_body = "  Line one\n  Line two\n  Line three";
    let replaced = store.replace_element_body(&key, new_body, date).unwrap();
    assert!(replaced);

    let extracted = store.extract_element_body(&key, date).unwrap().unwrap();
    assert!(extracted.contains("Line one"));
    assert!(extracted.contains("Line three"));
}

#[test]
fn test_extract_body_on_nonexistent_ref_returns_none() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 6, 23);
    store.append("note", "Existing note", &[], &[], date).unwrap();
    let result = store.extract_element_body("task-99", date).unwrap();
    assert!(result.is_none());
}

#[test]
fn test_replace_body_on_nonexistent_ref_returns_false() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 6, 24);
    store.append("note", "Existing note", &[], &[], date).unwrap();
    let result = store
        .replace_element_body("task-99", "new content", date)
        .unwrap();
    assert!(!result);
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 37: split_args stress tests
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_split_args_fifty_tags() {
    let tags: Vec<String> = (1..=50).map(|i| format!("tag{}", i)).collect();
    let raw = tags.join(", ");
    let parsed = split_args(&raw);
    assert_eq!(
        parsed.tags.len(),
        50,
        "50 comma-separated tags must all be captured"
    );
    assert!(parsed.attrs.is_empty(), "no attrs when all parts are bare");
    for i in 1..=50 {
        assert!(
            parsed.tags.contains(&format!("tag{}", i)),
            "tag{} must be present",
            i
        );
    }
}

#[test]
fn test_split_args_duplicate_attr_key_last_wins() {
    // HashMap insert overwrites; the last "status: done" overrides "status: open"
    let raw = "status: open, work, status: done";
    let parsed = split_args(raw);
    assert_eq!(
        parsed.attrs.get("status").map(String::as_str),
        Some("done"),
        "last duplicate key must win"
    );
    assert!(parsed.tags.contains(&"work".to_string()));
}

#[test]
fn test_split_args_empty_segments_ignored() {
    // Leading/trailing commas and double commas create empty segments
    let raw = ", ,work, , urgent,  ,";
    let parsed = split_args(raw);
    assert_eq!(parsed.tags.len(), 2, "empty segments must be ignored");
    assert!(parsed.tags.contains(&"work".to_string()));
    assert!(parsed.tags.contains(&"urgent".to_string()));
}

#[test]
fn test_split_args_whitespace_around_colon_trimmed() {
    let raw = "  key  :  value  , tag1 , tag2  ";
    let parsed = split_args(raw);
    assert_eq!(
        parsed.attrs.get("key").map(String::as_str),
        Some("value"),
        "whitespace around colon must be trimmed"
    );
    assert!(parsed.tags.contains(&"tag1".to_string()));
    assert!(parsed.tags.contains(&"tag2".to_string()));
}

#[test]
fn test_split_args_attr_value_with_colon() {
    // "at: 9:30am" — only the first colon is the separator
    let raw = "at: 9:30am";
    let parsed = split_args(raw);
    assert_eq!(
        parsed.attrs.get("at").map(String::as_str),
        Some("9:30am"),
        "value containing colon must include everything after first colon"
    );
}

#[test]
fn test_split_args_only_whitespace_returns_empty() {
    let parsed = split_args("   \t  ");
    assert!(parsed.tags.is_empty());
    assert!(parsed.attrs.is_empty());
}

#[test]
fn test_split_args_mixed_attrs_and_many_tags() {
    let mut parts = vec!["status: open".to_string(), "at: 3pm".to_string()];
    for i in 1..=20 {
        parts.push(format!("tag{}", i));
    }
    let raw = parts.join(", ");
    let parsed = split_args(&raw);
    assert_eq!(parsed.tags.len(), 20);
    assert_eq!(parsed.attrs.len(), 2);
    assert_eq!(parsed.attrs.get("status").map(String::as_str), Some("open"));
    assert_eq!(parsed.attrs.get("at").map(String::as_str), Some("3pm"));
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 38: RefResolver pathological inputs
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_ref_resolver_empty_map_no_panic() {
    let els: IndexMap<String, Element> = IndexMap::new();
    let resolver = RefResolver::new(&els);
    // to_epoch and to_human on any input must return None, not panic
    assert!(resolver.to_epoch("task-1").is_none());
    assert!(resolver.to_human("20260101.1.1").is_none());
}

#[test]
fn test_ref_resolver_mps_group_gets_mps_counter() {
    // parse_str wraps content in @mps{}, so passing "@mps{}" creates a nested @mps
    // The inner @mps at depth-1 gets "mps-1", not task/note counters
    let raw = "@mps{\n}\n";
    let els = parser::parse_str(raw);
    let resolver = RefResolver::new(&els);
    // The inner @mps group at depth 1 gets its own counter "mps-1"
    assert!(resolver.to_epoch("mps-1").is_some(), "inner @mps group gets mps-1 counter");
    // But no task or note counters exist (there are none)
    assert!(resolver.to_epoch("task-1").is_none());
    assert!(resolver.to_epoch("note-1").is_none());
}

#[test]
fn test_ref_resolver_only_unknown_elements() {
    let raw = "@mps{\n@widget[fancy]{\n  Custom element\n}\n}\n";
    let els = parser::parse_str(raw);
    let resolver = RefResolver::new(&els);
    // Unknown elements not part of any typed counter
    assert!(resolver.to_epoch("task-1").is_none());
    assert!(resolver.to_epoch("widget-1").is_none());
}

#[test]
fn test_ref_resolver_counter_per_type_independent() {
    // parse_str wraps content — do NOT include an outer @mps{} here
    let raw = concat!(
        "@task[]{\n  T1\n}\n",
        "@note[]{\n  N1\n}\n",
        "@task[]{\n  T2\n}\n",
        "@note[]{\n  N2\n}\n",
        "@log[]{\n  L1\n}\n",
    );
    let els = parser::parse_str(raw);
    let resolver = RefResolver::new(&els);

    let task_keys: Vec<_> = els.keys().filter(|k| els[*k].sign() == "task").collect();
    let note_keys: Vec<_> = els.keys().filter(|k| els[*k].sign() == "note").collect();
    let log_keys: Vec<_> = els.keys().filter(|k| els[*k].sign() == "log").collect();

    // Each type has its own 1-based counter
    for k in &task_keys {
        let h = resolver.to_human(k).unwrap();
        assert!(h.starts_with("task-"), "task human ref must start with task-: got {}", h);
    }
    for k in &note_keys {
        let h = resolver.to_human(k).unwrap();
        assert!(h.starts_with("note-"), "note human ref must start with note-: got {}", h);
    }
    for k in &log_keys {
        let h = resolver.to_human(k).unwrap();
        assert!(h.starts_with("log-"), "log human ref must start with log-: got {}", h);
    }

    // Each type counter is independent
    assert_eq!(task_keys.len(), 2, "must find 2 tasks");
    assert_eq!(note_keys.len(), 2, "must find 2 notes");
    assert_eq!(log_keys.len(), 1, "must find 1 log");
}

#[test]
fn test_ref_resolver_epoch_with_no_matching_date() {
    let raw = "@mps{\n@task[]{\n  T1\n}\n}\n";
    let els = parser::parse_str(raw);
    let resolver = RefResolver::new(&els);
    // Epoch ref from a completely different date → not found
    assert!(resolver.to_human("19990101.1.1").is_none());
}

#[test]
fn test_ref_resolver_invalid_human_ref_format() {
    let raw = "@mps{\n@task[]{\n  T1\n}\n}\n";
    let els = parser::parse_str(raw);
    let resolver = RefResolver::new(&els);
    // Nonsense strings must return None, not panic
    assert!(resolver.to_epoch("").is_none());
    assert!(resolver.to_epoch("not-a-ref").is_none());
    assert!(resolver.to_epoch("task-").is_none());
    assert!(resolver.to_epoch("task-0").is_none());
    assert!(resolver.to_epoch("-1").is_none());
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 39: MetaLocal — prune, notified state machine, task briefing
// ═══════════════════════════════════════════════════════════════════════════

use mps::meta::MetaLocal;

#[test]
fn test_meta_local_prune_removes_old_entries() {
    let mut local = MetaLocal::default();
    let now = chrono::Utc::now().timestamp();
    // Insert one entry from 2 days ago, one from 1 second ago
    local.notified.insert("old-ref".to_string(), now - 2 * 86400);
    local.notified.insert("recent-ref".to_string(), now - 1);

    let cutoff = now - 86400; // 1 day ago
    local.prune(cutoff);

    assert!(
        !local.notified.contains_key("old-ref"),
        "entry older than cutoff must be pruned"
    );
    assert!(
        local.notified.contains_key("recent-ref"),
        "recent entry must survive prune"
    );
}

#[test]
fn test_meta_local_prune_empty_map_no_panic() {
    let mut local = MetaLocal::default();
    local.prune(chrono::Utc::now().timestamp());
    assert!(local.notified.is_empty());
}

#[test]
fn test_meta_local_was_notified_absent_returns_false() {
    let local = MetaLocal::default();
    assert!(!local.was_notified("nonexistent-ref", 3600));
}

#[test]
fn test_meta_local_was_notified_with_zero_cooldown() {
    // Even if we set timestamp to now, cooldown=0 means "already expired"
    let mut local = MetaLocal::default();
    local.mark_notified("test-ref");
    // cooldown_secs = 0 → now - ts < 0 is false for any ts <= now
    assert!(
        !local.was_notified("test-ref", 0),
        "zero cooldown must always return false"
    );
}

#[test]
fn test_meta_local_was_notified_within_cooldown() {
    let mut local = MetaLocal::default();
    local.mark_notified("reminder-ref");
    // cooldown 1 hour — just notified, so still in window
    assert!(
        local.was_notified("reminder-ref", 3600),
        "freshly notified ref must be in cooldown"
    );
}

#[test]
fn test_meta_local_was_notified_expired_cooldown() {
    let mut local = MetaLocal::default();
    let far_past = chrono::Utc::now().timestamp() - 7200; // 2 hours ago
    local.notified.insert("old-reminder".to_string(), far_past);
    // cooldown 1 hour — entry is 2 hours old, so expired
    assert!(
        !local.was_notified("old-reminder", 3600),
        "expired cooldown must return false"
    );
}

#[test]
fn test_meta_local_mark_notified_overwrites() {
    let mut local = MetaLocal::default();
    let old_ts = chrono::Utc::now().timestamp() - 7200;
    local.notified.insert("ref-1".to_string(), old_ts);
    assert!(!local.was_notified("ref-1", 3600));

    // Re-marking resets the timestamp to now
    local.mark_notified("ref-1");
    assert!(local.was_notified("ref-1", 3600));
}

#[test]
fn test_meta_local_task_briefing_done_today_false_initially() {
    let local = MetaLocal::default();
    assert!(
        !local.task_briefing_done_today(),
        "brand-new MetaLocal must not have briefing done"
    );
}

#[test]
fn test_meta_local_mark_and_check_briefing_today() {
    let mut local = MetaLocal::default();
    local.mark_task_briefing();
    assert!(
        local.task_briefing_done_today(),
        "after mark_task_briefing, done_today must return true"
    );
}

#[test]
fn test_meta_local_briefing_done_yesterday_returns_false() {
    let mut local = MetaLocal::default();
    let yesterday = (chrono::Local::now() - chrono::Duration::days(1))
        .date_naive()
        .format("%Y-%m-%d")
        .to_string();
    local.last_task_date = Some(yesterday);
    assert!(
        !local.task_briefing_done_today(),
        "briefing from yesterday must not count as done today"
    );
}

#[test]
fn test_meta_local_save_load_roundtrip_with_notified() {
    let dir = tempfile::tempdir().unwrap();
    let mut local = MetaLocal::default();
    local.mark_notified("ref-a");
    local.mark_notified("ref-b");
    local.mark_task_briefing();
    local.save(dir.path()).unwrap();

    let loaded = MetaLocal::load(dir.path());
    assert!(loaded.notified.contains_key("ref-a"));
    assert!(loaded.notified.contains_key("ref-b"));
    assert!(loaded.task_briefing_done_today());
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 40: 50-append stress + sequential delete
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_fifty_appends_all_parseable() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 7, 1);

    for i in 1..=50 {
        store
            .append("task", &format!("Task number {}", i), &[], &[], date)
            .unwrap();
    }

    let els = store.parse_date(date).unwrap();
    let tasks: Vec<_> = els.values().filter(|e| e.sign() == "task").collect();
    assert_eq!(tasks.len(), 50, "all 50 appended tasks must be parseable");
}

#[test]
fn test_fifty_appends_sequential_delete_all() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 7, 2);

    for i in 1..=50 {
        store
            .append("task", &format!("Task {}", i), &[], &[], date)
            .unwrap();
    }

    // Delete all tasks one at a time, always re-parsing to get the fresh key list
    for _ in 0..50 {
        let els = store.parse_date(date).unwrap();
        let key = els
            .keys()
            .find(|k| els[*k].sign() == "task")
            .cloned();
        match key {
            Some(k) => {
                let deleted = store.delete_element(&k, date).unwrap();
                assert!(deleted, "delete must succeed for existing task");
            }
            None => break,
        }
    }

    let els_final = store.parse_date(date).unwrap();
    let remaining: Vec<_> = els_final.values().filter(|e| e.sign() == "task").collect();
    assert!(remaining.is_empty(), "no tasks must remain after deleting all 50");
}

#[test]
fn test_fifty_appends_mixed_types() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 7, 3);
    let types = ["task", "note", "log", "reminder", "character"];

    for i in 0..50 {
        let kind = types[i % types.len()];
        store
            .append(kind, &format!("Element {}", i), &[], &[], date)
            .unwrap();
    }

    let els = store.parse_date(date).unwrap();
    let real: Vec<_> = els
        .values()
        .filter(|e| !e.is_mps_group() && !e.is_unknown())
        .collect();
    assert_eq!(real.len(), 50, "all 50 mixed elements must be parseable");
}

#[test]
fn test_fifty_appends_with_tags_and_attrs() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 7, 4);

    for i in 1..=50 {
        let tags: Vec<String> = vec![format!("tag{}", i), "stress".to_string()];
        store
            .append("task", &format!("Tagged task {}", i), &tags, &[], date)
            .unwrap();
    }

    let els = store.parse_date(date).unwrap();
    let tasks: Vec<_> = els.values().filter(|e| e.sign() == "task").collect();
    assert_eq!(tasks.len(), 50);
    // Each task should have "stress" tag
    for t in &tasks {
        assert!(
            t.tags().contains(&"stress".to_string()),
            "every task must have 'stress' tag"
        );
    }
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 41: MetaCache integration end-to-end
// ═══════════════════════════════════════════════════════════════════════════

use mps::meta::MetaCache;

#[test]
fn test_metacache_empty_dir_snapshot_is_empty() {
    let dir = tempfile::tempdir().unwrap();
    let snapshot = MetaCache::build_snapshot(dir.path());
    assert!(snapshot.is_empty(), "empty dir must yield empty snapshot");
}

#[test]
fn test_metacache_is_valid_empty_vs_empty() {
    let cache = MetaCache::default();
    let snapshot: Vec<(String, u64)> = vec![];
    assert!(
        cache.is_valid(&snapshot),
        "empty cache must be valid against empty snapshot"
    );
}

#[test]
fn test_metacache_snapshot_includes_mps_files() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 8, 1);
    store.append("task", "A task", &[], &[], date).unwrap();

    let snapshot = MetaCache::build_snapshot(dir.path());
    assert!(!snapshot.is_empty(), "snapshot must include .mps files after append");
    assert!(
        snapshot.iter().all(|(name, _)| name.ends_with(".mps")),
        "all snapshot entries must be .mps files"
    );
}

#[test]
fn test_metacache_snapshot_excludes_non_mps_files() {
    let dir = tempfile::tempdir().unwrap();
    std::fs::write(dir.path().join("config.yaml"), "key: value").unwrap();
    std::fs::write(dir.path().join(".gitignore"), ".mps.local").unwrap();
    std::fs::write(dir.path().join(".mps.local"), "{}").unwrap();

    let snapshot = MetaCache::build_snapshot(dir.path());
    assert!(
        snapshot.is_empty(),
        "non-.mps files must not appear in snapshot"
    );
}

#[test]
fn test_metacache_invalidated_after_new_file() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date1 = make_date(2026, 8, 2);
    store.append("task", "First task", &[], &[], date1).unwrap();

    let snapshot1 = MetaCache::build_snapshot(dir.path());
    let mut cache = MetaCache::default();
    cache.files_snapshot = snapshot1.clone();

    // Cache is valid before any change
    assert!(cache.is_valid(&MetaCache::build_snapshot(dir.path())));

    // Add a second file on a new date
    let date2 = make_date(2026, 8, 3);
    store.append("note", "New note", &[], &[], date2).unwrap();

    let snapshot2 = MetaCache::build_snapshot(dir.path());
    assert!(
        !cache.is_valid(&snapshot2),
        "cache must be invalid after new file added"
    );
}

#[test]
fn test_metacache_invalidated_after_file_mutation() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 8, 4);
    store.append("task", "A task", &[], &[], date).unwrap();

    let snapshot1 = MetaCache::build_snapshot(dir.path());
    let mut cache = MetaCache::default();
    cache.files_snapshot = snapshot1;

    // Append again to same date file (changes its size)
    store.append("note", "A note", &[], &[], date).unwrap();

    let snapshot2 = MetaCache::build_snapshot(dir.path());
    assert!(
        !cache.is_valid(&snapshot2),
        "cache must be invalid after file size changes"
    );
}

#[test]
fn test_metacache_save_load_roundtrip() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 8, 5);
    store.append("task", "Cached task", &["work".to_string()], &[], date).unwrap();

    let snapshot = MetaCache::build_snapshot(dir.path());
    let mut local = MetaLocal::load(dir.path());
    local.cache.files_snapshot = snapshot.clone();
    local.cache.tag_counts.insert("work".to_string(), 1);
    local.save(dir.path()).unwrap();

    let loaded = MetaLocal::load(dir.path());
    assert!(
        loaded.cache.is_valid(&snapshot),
        "loaded cache must be valid against same snapshot"
    );
    assert_eq!(loaded.cache.tag_counts.get("work"), Some(&1));
}

#[test]
fn test_metacache_tag_counts_not_affected_by_local_file() {
    let dir = tempfile::tempdir().unwrap();
    // .mps.local must not appear in cache snapshot
    let store = Store::new(dir.path());
    let date = make_date(2026, 8, 6);
    store.append("task", "Task with tag", &["work".to_string()], &[], date).unwrap();

    // Save local state (creates .mps.local)
    let mut local = MetaLocal::load(dir.path());
    local.mark_notified("some-ref");
    local.save(dir.path()).unwrap();

    // Snapshot must still only contain .mps files
    let snapshot = MetaCache::build_snapshot(dir.path());
    assert!(
        snapshot.iter().all(|(name, _)| name.ends_with(".mps")),
        ".mps.local must not appear in cache snapshot"
    );
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 42: Store::search() edge cases
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_search_empty_query_matches_all_elements() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 9, 1);
    store.append("task", "Alpha task", &[], &[], date).unwrap();
    store.append("note", "Beta note", &[], &[], date).unwrap();
    store.append("log", "Gamma log", &[], &[], date).unwrap();

    // Empty query → body_str.contains("") is always true
    let results = store.search("", None, None, None).unwrap();
    assert_eq!(results.len(), 3, "empty query must match all elements");
}

#[test]
fn test_search_no_match_returns_empty() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 9, 2);
    store.append("task", "Real task body", &[], &[], date).unwrap();

    let results = store
        .search("xyzzy_no_match_ever", None, None, None)
        .unwrap();
    assert!(results.is_empty(), "no match must return empty results");
}

#[test]
fn test_search_case_insensitive_isolated_store() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 9, 3);
    store.append("task", "Deploy to Production", &[], &[], date).unwrap();

    // Lowercase query should match uppercase body
    let results = store.search("production", None, None, None).unwrap();
    assert_eq!(results.len(), 1, "search must be case-insensitive");

    let results2 = store.search("DEPLOY", None, None, None).unwrap();
    assert_eq!(results2.len(), 1, "uppercase query must match lowercase body");
}

#[test]
fn test_search_type_filter_excludes_others() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 9, 4);
    store.append("task", "A common word", &[], &[], date).unwrap();
    store.append("note", "A common word", &[], &[], date).unwrap();
    store.append("log", "A common word", &[], &[], date).unwrap();

    let results = store.search("common", Some("task"), None, None).unwrap();
    assert_eq!(results.len(), 1);
    assert_eq!(results[0].element.sign(), "task");
}

#[test]
fn test_search_tag_filter_matches_only_tagged() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 9, 5);
    let tags = vec!["urgent".to_string()];
    store.append("task", "Urgent thing", &tags, &[], date).unwrap();
    store.append("task", "Normal thing", &[], &[], date).unwrap();

    let results = store.search("thing", None, Some("urgent"), None).unwrap();
    assert_eq!(results.len(), 1, "tag filter must exclude untagged elements");
    assert!(results[0].element.tags().contains(&"urgent".to_string()));
}

#[test]
fn test_search_type_and_tag_combined() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 9, 6);
    let urgent = vec!["urgent".to_string()];
    store.append("task", "Urgent task", &urgent, &[], date).unwrap();
    store.append("note", "Urgent note", &urgent, &[], date).unwrap();
    store.append("task", "Normal task", &[], &[], date).unwrap();

    let results = store
        .search("urgent", Some("task"), Some("urgent"), None)
        .unwrap();
    assert_eq!(
        results.len(),
        1,
        "type+tag combined filter must match only task with urgent tag"
    );
    assert_eq!(results[0].element.sign(), "task");
}

#[test]
fn test_search_since_date_excludes_older_files() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());

    let old_date = make_date(2026, 1, 1);
    let new_date = make_date(2026, 9, 7);
    store.append("task", "Old task", &[], &[], old_date).unwrap();
    store.append("task", "New task", &[], &[], new_date).unwrap();

    let results = store
        .search("task", None, None, Some(make_date(2026, 6, 1)))
        .unwrap();
    assert_eq!(results.len(), 1, "since_date filter must exclude older files");
    assert!(results[0].element.body_str().contains("New task"));
}

#[test]
fn test_search_special_characters_in_query_treated_as_literal() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 9, 8);
    // Body containing characters that would be regex-special if search used regex
    store
        .append("note", "Price: $9.99 (special)", &[], &[], date)
        .unwrap();

    // Query with regex-special chars — must not panic or error
    let results = store.search("$9.99", None, None, None).unwrap();
    assert_eq!(
        results.len(),
        1,
        "literal string with special chars must match"
    );
}

#[test]
fn test_search_across_multiple_dates() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());

    for day in 1..=5u32 {
        let date = make_date(2026, 9, day + 10);
        store
            .append("task", &format!("Recurring task day {}", day), &[], &[], date)
            .unwrap();
    }

    let results = store.search("Recurring", None, None, None).unwrap();
    assert_eq!(
        results.len(),
        5,
        "search must span all date files and return all 5 matches"
    );
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 43: Rewrite edge cases — no-op, bidirectional status, empty attrs
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_rewrite_with_empty_attrs_on_note_returns_false() {
    // Notes have no typed_attrs, so an empty attrs map produces the same args string → false
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 10, 1);
    store.append("note", "A note", &["work".to_string()], &[], date).unwrap();

    let els = store.parse_date(date).unwrap();
    let key = els.keys().find(|k| els[*k].sign() == "note").unwrap().clone();

    let result = store.rewrite_element(&key, &HashMap::new(), date).unwrap();
    assert!(!result, "empty attrs on note (no typed attrs) must return false (no change)");
}

#[test]
fn test_rewrite_status_open_then_done_then_open() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 10, 2);
    store.append("task", "Toggleable task", &[], &[], date).unwrap();

    let els = store.parse_date(date).unwrap();
    let key = els.keys().find(|k| els[*k].sign() == "task").unwrap().clone();

    let mut attrs = HashMap::new();
    attrs.insert("status".to_string(), "done".to_string());
    store.rewrite_element(&key, &attrs, date).unwrap();

    // Verify it's done
    let els2 = store.parse_date(date).unwrap();
    let task = els2.get(&key).unwrap();
    if let Element::Task { data, .. } = task {
        assert_eq!(data.status_str(), "done");
    }

    // Toggle back to open
    attrs.insert("status".to_string(), "open".to_string());
    store.rewrite_element(&key, &attrs, date).unwrap();

    let els3 = store.parse_date(date).unwrap();
    let task3 = els3.get(&key).unwrap();
    if let Element::Task { data, .. } = task3 {
        assert_eq!(data.status_str(), "open");
    }
}

#[test]
fn test_rewrite_same_value_second_call_returns_false() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 10, 3);
    store.append("task", "Same status task", &[], &[], date).unwrap();

    let els = store.parse_date(date).unwrap();
    let key = els.keys().find(|k| els[*k].sign() == "task").unwrap().clone();

    let mut attrs = HashMap::new();
    attrs.insert("status".to_string(), "done".to_string());
    // First call: changes status from default to "done" — returns true
    let first = store.rewrite_element(&key, &attrs, date).unwrap();
    assert!(first, "first rewrite must return true");

    // Second call: same value "done" again — content unchanged → false
    let second = store.rewrite_element(&key, &attrs, date).unwrap();
    assert!(!second, "second rewrite with same value must return false");
}

#[test]
fn test_rewrite_adds_new_attribute_to_reminder() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 10, 4);
    store.append("reminder", "No time set", &[], &[], date).unwrap();

    let els = store.parse_date(date).unwrap();
    let key = els
        .keys()
        .find(|k| els[*k].sign() == "reminder")
        .unwrap()
        .clone();

    let mut attrs = HashMap::new();
    attrs.insert("at".to_string(), "2pm".to_string());
    let result = store.rewrite_element(&key, &attrs, date).unwrap();
    assert!(result, "adding a new attr must return true");

    let els2 = store.parse_date(date).unwrap();
    let rem = els2.get(&key).unwrap();
    if let Element::Reminder { data, .. } = rem {
        assert_eq!(data.at.as_deref(), Some("2pm"));
    }
}

#[test]
fn test_rewrite_on_wrong_date_returns_false() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 10, 5);
    store.append("task", "Task on date A", &[], &[], date).unwrap();

    let els = store.parse_date(date).unwrap();
    let key = els.keys().find(|k| els[*k].sign() == "task").unwrap().clone();

    // Try to rewrite against a different date that has no file
    let wrong_date = make_date(2026, 10, 6);
    let mut attrs = HashMap::new();
    attrs.insert("status".to_string(), "done".to_string());
    // Human ref resolution will fail because the file for wrong_date doesn't exist
    let result = store.rewrite_element("task-1", &attrs, wrong_date).unwrap();
    assert!(!result, "rewrite against wrong date must return false");
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 44: Date boundary — files_since, all_file_dates, multi-file
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_files_since_date_matching_exactly_includes_that_date() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 11, 1);
    store.append("task", "Task on the since date", &[], &[], date).unwrap();

    let files = store.files_since(date).unwrap();
    assert_eq!(files.len(), 1, "files_since must include the boundary date itself");
}

#[test]
fn test_files_since_one_day_after_only_file_returns_empty() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 11, 2);
    store.append("task", "Task", &[], &[], date).unwrap();

    let after = make_date(2026, 11, 3);
    let files = store.files_since(after).unwrap();
    assert!(files.is_empty(), "files_since one day after must return empty");
}

#[test]
fn test_files_since_future_date_returns_empty() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    store.append("task", "Old task", &[], &[], make_date(2020, 1, 1)).unwrap();

    let far_future = make_date(2099, 12, 31);
    let files = store.files_since(far_future).unwrap();
    assert!(files.is_empty(), "files_since far future must return empty");
}

#[test]
fn test_files_since_filters_correctly_multiple_dates() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    // Create files on Jan, Feb, Mar 2026
    store.append("task", "Jan task", &[], &[], make_date(2026, 1, 1)).unwrap();
    store.append("task", "Feb task", &[], &[], make_date(2026, 2, 1)).unwrap();
    store.append("task", "Mar task", &[], &[], make_date(2026, 3, 1)).unwrap();

    let since_feb = make_date(2026, 2, 1);
    let files = store.files_since(since_feb).unwrap();
    assert_eq!(files.len(), 2, "files_since Feb must return Feb and Mar files");
}

#[test]
fn test_all_file_dates_sorted_ascending() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    // Append in reverse chronological order
    store.append("task", "Mar", &[], &[], make_date(2026, 3, 1)).unwrap();
    store.append("task", "Jan", &[], &[], make_date(2026, 1, 1)).unwrap();
    store.append("task", "Feb", &[], &[], make_date(2026, 2, 1)).unwrap();

    let dates = store.all_file_dates().unwrap();
    assert_eq!(dates.len(), 3);
    assert!(dates[0] < dates[1] && dates[1] < dates[2], "dates must be sorted ascending");
}

#[test]
fn test_all_file_dates_deduplicates_same_date_multiple_files() {
    let dir = tempfile::tempdir().unwrap();
    // Manually create two files with same date but different epochs
    let content = "@mps{\n@task[]{\n  Task\n}\n}\n";
    std::fs::write(dir.path().join("20261201.1000000001.mps"), content).unwrap();
    std::fs::write(dir.path().join("20261201.1000000002.mps"), content).unwrap();
    let store = Store::new(dir.path());
    let dates = store.all_file_dates().unwrap();
    assert_eq!(dates.len(), 1, "two files on same date must yield 1 unique date");
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 45: Append persistence — new Store object reads same data
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_append_persists_across_store_instances() {
    let dir = tempfile::tempdir().unwrap();
    let date = make_date(2026, 12, 1);

    {
        let store = Store::new(dir.path());
        store.append("task", "Persistent task", &["work".to_string()], &[], date).unwrap();
    }

    // New Store object — simulates a new process opening the same directory
    let store2 = Store::new(dir.path());
    let els = store2.parse_date(date).unwrap();
    let tasks: Vec<_> = els.values().filter(|e| e.sign() == "task").collect();
    assert_eq!(tasks.len(), 1, "task must persist across Store instances");
    assert!(tasks[0].tags().contains(&"work".to_string()));
    assert!(tasks[0].body_str().contains("Persistent task"));
}

#[test]
fn test_multiple_appends_persist_across_store_instances() {
    let dir = tempfile::tempdir().unwrap();
    let date = make_date(2026, 12, 2);

    {
        let store = Store::new(dir.path());
        for i in 1..=10 {
            store.append("note", &format!("Note {}", i), &[], &[], date).unwrap();
        }
    }

    let store2 = Store::new(dir.path());
    let els = store2.parse_date(date).unwrap();
    let notes: Vec<_> = els.values().filter(|e| e.sign() == "note").collect();
    assert_eq!(notes.len(), 10, "all 10 notes must persist");
}

#[test]
fn test_delete_persists_across_store_instances() {
    let dir = tempfile::tempdir().unwrap();
    let date = make_date(2026, 12, 3);
    let store = Store::new(dir.path());
    store.append("task", "To be deleted", &[], &[], date).unwrap();
    store.append("task", "To survive", &[], &[], date).unwrap();

    let els = store.parse_date(date).unwrap();
    let key = els
        .iter()
        .find(|(_, e)| e.sign() == "task" && e.body_str().contains("To be deleted"))
        .map(|(k, _)| k.clone())
        .unwrap();
    store.delete_element(&key, date).unwrap();

    // New instance verifies persistence
    let store2 = Store::new(dir.path());
    let els2 = store2.parse_date(date).unwrap();
    let tasks: Vec<_> = els2.values().filter(|e| e.sign() == "task").collect();
    assert_eq!(tasks.len(), 1);
    assert!(tasks[0].body_str().contains("To survive"));
}

#[test]
fn test_rewrite_persists_across_store_instances() {
    let dir = tempfile::tempdir().unwrap();
    let date = make_date(2026, 12, 4);
    let store = Store::new(dir.path());
    store.append("task", "Open task", &[], &[], date).unwrap();

    let els = store.parse_date(date).unwrap();
    let key = els.keys().find(|k| els[*k].sign() == "task").unwrap().clone();
    let mut attrs = HashMap::new();
    attrs.insert("status".to_string(), "done".to_string());
    store.rewrite_element(&key, &attrs, date).unwrap();

    let store2 = Store::new(dir.path());
    let els2 = store2.parse_date(date).unwrap();
    let task = els2.get(&key).unwrap();
    if let Element::Task { data, .. } = task {
        assert_eq!(data.status_str(), "done", "done status must persist");
    }
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 46: Deep nesting — RefResolver and parse correctness
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_deeply_nested_refs_resolve_correctly() {
    // 3 levels: outer mps → inner mps → task
    let raw = concat!(
        "@mps[outer]{\n",
        "  @mps[inner]{\n",
        "    @task[]{\n      Deep task\n    }\n",
        "  }\n",
        "}\n",
    );
    let els = parser::parse_str(raw);
    let resolver = RefResolver::new(&els);

    // outer @mps gets mps-1
    let outer_key = els
        .iter()
        .find(|(_, e)| e.sign() == "mps" && e.raw_args() == "outer")
        .map(|(k, _)| k.clone());
    assert!(outer_key.is_some(), "outer mps must be parsed");

    if let Some(k) = &outer_key {
        let human = resolver.to_human(k);
        assert!(human.is_some(), "outer mps must have a human ref");
        assert!(human.unwrap().starts_with("mps-"));
    }
}

#[test]
fn test_parse_three_levels_does_not_panic() {
    let raw = concat!(
        "@mps{\n",
        "  @mps{\n",
        "    @mps{\n",
        "      @task[]{\n        Triple nested\n      }\n",
        "    }\n",
        "  }\n",
        "}\n",
    );
    let els = parser::parse_str(raw);
    // Must not panic; deeply nested elements must be in the map
    let tasks: Vec<_> = els.values().filter(|e| e.sign() == "task").collect();
    assert!(!tasks.is_empty(), "task must be found inside triple nesting");
    assert!(tasks[0].body_str().contains("Triple nested"));
}

#[test]
fn test_nested_task_body_extracted_correctly() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2026, 12, 10);
    // Append a top-level task
    store.append("task", "Parent task", &[], &[], date).unwrap();

    let els = store.parse_date(date).unwrap();
    let key = els.keys().find(|k| els[*k].sign() == "task").unwrap().clone();

    // Replace body with nested structure (simulating note-in-task)
    let new_body = "  Parent task body\n  @note[]{\n    Child note\n  }";
    let replaced = store.replace_element_body(&key, new_body, date).unwrap();
    assert!(replaced);

    // Parse again — parent task plus nested note
    let els2 = store.parse_date(date).unwrap();
    let all: Vec<_> = els2.values().filter(|e| !e.is_mps_group() && !e.is_unknown()).collect();
    // Should have parent task + child note
    assert!(all.len() >= 2, "nested note must be parsed from task body: got {}", all.len());
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 47: MetaLocal save/load integrity and gitignore side-effect
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_meta_local_save_creates_gitignore_entry() {
    let dir = tempfile::tempdir().unwrap();
    let mut local = MetaLocal::default();
    local.mark_notified("any-ref");
    local.save(dir.path()).unwrap();

    let gitignore = dir.path().join(".gitignore");
    let content = std::fs::read_to_string(&gitignore).unwrap_or_default();
    assert!(
        content.lines().any(|l| l.trim() == ".mps.local"),
        ".gitignore must contain .mps.local after save"
    );
}

#[test]
fn test_meta_local_save_gitignore_idempotent() {
    let dir = tempfile::tempdir().unwrap();
    let mut local = MetaLocal::default();
    local.mark_notified("ref-a");
    local.save(dir.path()).unwrap();
    local.mark_notified("ref-b");
    local.save(dir.path()).unwrap();
    local.save(dir.path()).unwrap();

    // .mps.local must appear exactly once in .gitignore
    let gitignore = dir.path().join(".gitignore");
    let content = std::fs::read_to_string(&gitignore).unwrap_or_default();
    let count = content.lines().filter(|l| l.trim() == ".mps.local").count();
    assert_eq!(count, 1, ".mps.local must appear exactly once in .gitignore");
}

#[test]
fn test_meta_local_load_absent_does_not_create_file() {
    let dir = tempfile::tempdir().unwrap();
    let _local = MetaLocal::load(dir.path());
    let path = MetaLocal::path(dir.path());
    assert!(!path.exists(), "load on absent .mps.local must not create the file");
}

#[test]
fn test_meta_local_corrupted_json_returns_default() {
    let dir = tempfile::tempdir().unwrap();
    let path = MetaLocal::path(dir.path());
    std::fs::write(&path, "{ not valid json at all !! }").unwrap();
    let local = MetaLocal::load(dir.path());
    // Must return Default (empty state), not panic
    assert!(local.notified.is_empty());
    assert!(local.last_task_date.is_none());
}

#[test]
fn test_meta_local_truncated_json_returns_default() {
    let dir = tempfile::tempdir().unwrap();
    let path = MetaLocal::path(dir.path());
    std::fs::write(&path, r#"{"notified":{"ref-1": 17"#).unwrap();
    let local = MetaLocal::load(dir.path());
    assert!(local.notified.is_empty());
}

#[test]
fn test_meta_local_empty_json_object_returns_default() {
    let dir = tempfile::tempdir().unwrap();
    let path = MetaLocal::path(dir.path());
    std::fs::write(&path, "{}").unwrap();
    let local = MetaLocal::load(dir.path());
    assert!(local.notified.is_empty());
    assert!(local.last_task_date.is_none());
    assert!(local.cache.tag_counts.is_empty());
}

#[test]
fn test_meta_local_prune_keeps_exact_cutoff_entries() {
    let mut local = MetaLocal::default();
    let cutoff = 1_000_000i64;
    // Entry exactly at cutoff: ts >= cutoff → kept
    local.notified.insert("at-cutoff".to_string(), cutoff);
    // Entry 1 second before cutoff: ts < cutoff → pruned
    local.notified.insert("before-cutoff".to_string(), cutoff - 1);
    // Entry well after cutoff → kept
    local.notified.insert("after-cutoff".to_string(), cutoff + 3600);

    local.prune(cutoff);

    assert!(local.notified.contains_key("at-cutoff"), "entry at cutoff must be kept");
    assert!(
        !local.notified.contains_key("before-cutoff"),
        "entry before cutoff must be removed"
    );
    assert!(local.notified.contains_key("after-cutoff"), "entry after cutoff must be kept");
}

#[test]
fn test_meta_local_prune_all_entries_removed() {
    let mut local = MetaLocal::default();
    let far_future = i64::MAX;
    local.notified.insert("ref-1".to_string(), 0);
    local.notified.insert("ref-2".to_string(), 100);
    local.notified.insert("ref-3".to_string(), 999);
    local.prune(far_future);
    assert!(local.notified.is_empty(), "prune with i64::MAX cutoff must remove all entries");
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 48: Disastrous input — enormous bodies, pathological args, zero-width chars
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_parse_str_very_large_body_does_not_panic() {
    let big_body = "x".repeat(1_000_000);
    let raw = format!("@task[work]{{\n  {}\n}}\n", big_body);
    let els = parser::parse_str(&raw);
    let tasks: Vec<_> = els.values().filter(|e| e.sign() == "task").collect();
    assert!(!tasks.is_empty(), "task with 1MB body must parse");
    assert!(tasks[0].body_str().contains("xxx"), "body content must be preserved");
}

#[test]
fn test_parse_str_many_tags_does_not_panic() {
    let tags: Vec<String> = (1..=200).map(|i| format!("tag{}", i)).collect();
    let args = tags.join(", ");
    let raw = format!("@task[{}]{{\n  Big tag task\n}}\n", args);
    let els = parser::parse_str(&raw);
    let tasks: Vec<_> = els.values().filter(|e| e.sign() == "task").collect();
    assert!(!tasks.is_empty(), "task with 200 tags must parse");
    assert!(
        tasks[0].tags().len() >= 200,
        "all 200 tags must be captured, got {}",
        tasks[0].tags().len()
    );
}

#[test]
fn test_split_args_zero_width_space_in_tag_name() {
    // U+200B zero-width space embedded in a tag name
    let raw = "work\u{200B}tag, normal";
    let parsed = split_args(raw);
    assert_eq!(parsed.tags.len(), 2, "zero-width space in tag must not crash split_args");
}

#[test]
fn test_split_args_tab_character_in_value() {
    let raw = "key:\tvalue with tab";
    let parsed = split_args(raw);
    // trim() strips leading/trailing whitespace but tab in value middle is preserved
    assert!(parsed.attrs.contains_key("key"), "key must be parsed from tab-separated kv");
}

#[test]
fn test_append_body_with_only_whitespace() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2027, 1, 1);
    store.append("note", "   \t\t   ", &[], &[], date).unwrap();
    let els = store.parse_date(date).unwrap();
    let notes: Vec<_> = els.values().filter(|e| e.sign() == "note").collect();
    assert_eq!(notes.len(), 1, "note with whitespace-only body must append");
}

#[test]
fn test_append_hundred_files_on_distinct_dates() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());

    // One element per day for 100 days starting Jan 1 2027
    for day in 0..100u32 {
        let date = make_date(2027, 1, 1) + chrono::Duration::days(day as i64);
        store.append("task", &format!("Day {} task", day), &[], &[], date).unwrap();
    }

    let all_dates = store.all_file_dates().unwrap();
    assert_eq!(all_dates.len(), 100, "must have 100 distinct date files");

    let all = store.all_files().unwrap();
    assert_eq!(all.len(), 100);

    let results = store.search("Day", None, None, None).unwrap();
    assert_eq!(results.len(), 100, "search must find all 100 tasks");
}

#[test]
fn test_body_with_backslash_and_quotes_preserved() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2027, 1, 2);
    let body = r#"path: C:\Users\name, note: "quoted""#;
    store.append("log", body, &[], &[], date).unwrap();

    let els = store.parse_date(date).unwrap();
    let log = els.values().find(|e| e.sign() == "log").unwrap();
    assert!(
        log.body_str().contains(r"C:\Users"),
        "backslash in body must be preserved"
    );
    assert!(log.body_str().contains(r#""quoted""#), "double-quoted text must be preserved");
}

#[test]
fn test_search_query_that_is_only_whitespace() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2027, 1, 3);
    store.append("task", "Normal task", &[], &[], date).unwrap();

    // Whitespace-only query → contains("   ") on a normal body → no match
    let results = store.search("   ", None, None, None).unwrap();
    assert!(results.is_empty(), "whitespace-only query should not match normal bodies");
}

#[test]
fn test_all_element_types_round_trip_with_all_ascii_body() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2027, 1, 4);
    // Every printable ASCII character in the body (excluding `}` which would break parsing)
    let ascii_body: String = (32u8..127u8)
        .filter(|&c| c != b'}' && c != b'{')
        .map(|c| c as char)
        .collect();

    for kind in &["task", "note", "log", "reminder", "character"] {
        store.append(kind, &ascii_body, &[], &[], date).unwrap();
    }

    let els = store.parse_date(date).unwrap();
    let real: Vec<_> = els.values().filter(|e| !e.is_mps_group() && !e.is_unknown()).collect();
    assert_eq!(real.len(), 5, "all 5 element types must round-trip with full ASCII body");
    for el in &real {
        assert!(
            el.body_str().contains("!"),
            "printable ASCII must be preserved in {} body",
            el.sign()
        );
    }
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 49: Store::all_files() ordering guarantees
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_all_files_returns_sorted_by_filename() {
    let dir = tempfile::tempdir().unwrap();
    // Write files out-of-order; all_files must return them sorted lexicographically
    std::fs::write(dir.path().join("20270301.1000000003.mps"), "@mps{\n@task[]{T}\n}\n").unwrap();
    std::fs::write(dir.path().join("20270101.1000000001.mps"), "@mps{\n@task[]{T}\n}\n").unwrap();
    std::fs::write(dir.path().join("20270201.1000000002.mps"), "@mps{\n@task[]{T}\n}\n").unwrap();

    let store = Store::new(dir.path());
    let files = store.all_files().unwrap();
    assert_eq!(files.len(), 3);
    let names: Vec<String> = files
        .iter()
        .map(|p| p.file_name().unwrap().to_str().unwrap().to_string())
        .collect();
    assert!(
        names[0].starts_with("20270101"),
        "first file must be Jan: {}",
        names[0]
    );
    assert!(
        names[1].starts_with("20270201"),
        "second file must be Feb: {}",
        names[1]
    );
    assert!(
        names[2].starts_with("20270301"),
        "third file must be Mar: {}",
        names[2]
    );
}

#[test]
fn test_files_since_with_exact_same_files_all_return() {
    let dir = tempfile::tempdir().unwrap();
    let dates = [
        make_date(2027, 4, 1),
        make_date(2027, 4, 15),
        make_date(2027, 4, 30),
    ];
    let store = Store::new(dir.path());
    for d in &dates {
        store.append("task", "Task", &[], &[], *d).unwrap();
    }

    // since the earliest date → all 3 returned
    let files = store.files_since(dates[0]).unwrap();
    assert_eq!(files.len(), 3, "all 3 dates must be returned when since == earliest");
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 50: Element tags — ordering preserved, dedup behavior, empty tag
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_tags_order_preserved_after_append() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2027, 5, 1);
    let tags: Vec<String> = vec!["zzz".into(), "aaa".into(), "mmm".into()];
    store.append("task", "Ordered tags task", &tags, &[], date).unwrap();

    let els = store.parse_date(date).unwrap();
    let task = els.values().find(|e| e.sign() == "task").unwrap();
    let t = task.tags();
    assert_eq!(t, &["zzz", "aaa", "mmm"], "tag insertion order must be preserved");
}

#[test]
fn test_duplicate_tags_stored_as_duplicates() {
    // mps doesn't deduplicate tags; if user adds "work" twice, both are stored
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2027, 5, 2);
    let tags: Vec<String> = vec!["work".into(), "work".into()];
    store.append("task", "Dup tag task", &tags, &[], date).unwrap();

    let els = store.parse_date(date).unwrap();
    let task = els.values().find(|e| e.sign() == "task").unwrap();
    let work_count = task.tags().iter().filter(|t| t.as_str() == "work").count();
    assert_eq!(work_count, 2, "duplicate tags must be stored as-is");
}

#[test]
fn test_element_with_twenty_tags_roundtrips_all() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2027, 5, 3);
    let tags: Vec<String> = (1..=20).map(|i| format!("tag{:02}", i)).collect();
    store.append("task", "Multi-tag task", &tags, &[], date).unwrap();

    let els = store.parse_date(date).unwrap();
    let task = els.values().find(|e| e.sign() == "task").unwrap();
    assert_eq!(task.tags().len(), 20, "all 20 tags must round-trip");
    for (i, tag) in tags.iter().enumerate() {
        assert_eq!(&task.tags()[i], tag, "tag order must be preserved at index {}", i);
    }
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 51: Concurrent-like: alternating writes to two dates
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_alternating_writes_to_two_dates_both_correct() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date_a = make_date(2027, 6, 1);
    let date_b = make_date(2027, 6, 2);

    // Interleave appends to two different dates
    for i in 0..10 {
        store.append("task", &format!("A-task-{}", i), &[], &[], date_a).unwrap();
        store.append("note", &format!("B-note-{}", i), &[], &[], date_b).unwrap();
    }

    let els_a = store.parse_date(date_a).unwrap();
    let tasks_a: Vec<_> = els_a.values().filter(|e| e.sign() == "task").collect();
    assert_eq!(tasks_a.len(), 10, "date A must have 10 tasks");

    let els_b = store.parse_date(date_b).unwrap();
    let notes_b: Vec<_> = els_b.values().filter(|e| e.sign() == "note").collect();
    assert_eq!(notes_b.len(), 10, "date B must have 10 notes");

    // No cross-contamination
    let tasks_b: Vec<_> = els_b.values().filter(|e| e.sign() == "task").collect();
    assert!(tasks_b.is_empty(), "date B must have no tasks");
}

#[test]
fn test_alternating_delete_from_two_dates() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date_a = make_date(2027, 6, 3);
    let date_b = make_date(2027, 6, 4);

    for i in 0..5 {
        store.append("task", &format!("A-{}", i), &[], &[], date_a).unwrap();
        store.append("task", &format!("B-{}", i), &[], &[], date_b).unwrap();
    }

    // Delete all from date_a; date_b must remain intact
    for _ in 0..5 {
        let els = store.parse_date(date_a).unwrap();
        if let Some(key) = els.keys().find(|k| els[*k].sign() == "task").cloned() {
            store.delete_element(&key, date_a).unwrap();
        }
    }

    let remaining_a: Vec<_> = store
        .parse_date(date_a)
        .unwrap()
        .values()
        .filter(|e| e.sign() == "task")
        .cloned()
        .collect();
    let remaining_b: Vec<_> = store
        .parse_date(date_b)
        .unwrap()
        .values()
        .filter(|e| e.sign() == "task")
        .cloned()
        .collect();

    assert!(remaining_a.is_empty(), "date A must have no tasks after deleting all");
    assert_eq!(remaining_b.len(), 5, "date B must still have all 5 tasks");
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 52: MetaShared — save, load, corrupt, version bumping
// ═══════════════════════════════════════════════════════════════════════════

use mps::meta::MetaShared;

#[test]
fn test_meta_shared_load_absent_is_default() {
    let dir = tempfile::tempdir().unwrap();
    let m = MetaShared::load(dir.path());
    assert_eq!(m.version, 0);
    assert!(m.config.type_aliases.is_empty());
    assert!(m.config.command_aliases.is_empty());
    assert!(m.config.custom_tags.is_empty());
    assert!(m.config.default_command.is_none());
}

#[test]
fn test_meta_shared_save_load_full_roundtrip() {
    let dir = tempfile::tempdir().unwrap();
    let mut m = MetaShared::default();
    m.version = 42;
    m.config.type_aliases.insert("t".into(), "task".into());
    m.config.command_aliases.insert("ls".into(), "list".into());
    m.config.default_command = Some("open".into());
    m.config.custom_tags = vec!["urgent".into(), "personal".into()];
    m.config.notify.window_minutes = 10;
    m.save(dir.path()).unwrap();

    let loaded = MetaShared::load(dir.path());
    assert_eq!(loaded.version, 42);
    assert_eq!(loaded.config.type_aliases.get("t"), Some(&"task".to_string()));
    assert_eq!(loaded.config.command_aliases.get("ls"), Some(&"list".to_string()));
    assert_eq!(loaded.config.default_command.as_deref(), Some("open"));
    assert!(loaded.config.custom_tags.contains(&"urgent".to_string()));
    assert_eq!(loaded.config.notify.window_minutes, 10);
}

#[test]
fn test_meta_shared_corrupted_json_returns_default() {
    let dir = tempfile::tempdir().unwrap();
    let path = MetaShared::path(dir.path());
    std::fs::write(&path, "{ broken json !!! }").unwrap();
    let m = MetaShared::load(dir.path());
    assert_eq!(m.version, 0, "corrupted .mps.meta must return default");
}

#[test]
fn test_meta_shared_empty_json_returns_default() {
    let dir = tempfile::tempdir().unwrap();
    let path = MetaShared::path(dir.path());
    std::fs::write(&path, "{}").unwrap();
    let m = MetaShared::load(dir.path());
    assert_eq!(m.version, 0);
    assert!(m.config.custom_tags.is_empty());
}

#[test]
fn test_meta_shared_version_does_not_regress() {
    let dir = tempfile::tempdir().unwrap();
    let mut m = MetaShared::default();
    m.version = 5;
    m.save(dir.path()).unwrap();

    let loaded = MetaShared::load(dir.path());
    // max(5, 1) is a common pattern — loaded version must be at least what was saved
    assert!(loaded.version >= 5, "loaded version must be at least what was saved");
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 53: parser::parse_str correctness corner cases
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_parse_str_element_with_no_brackets_at_all() {
    // @task{ body } — no brackets, not even empty ones
    let raw = "@task{\n  Bracketless task\n}\n";
    let els = parser::parse_str(raw);
    let tasks: Vec<_> = els.values().filter(|e| e.sign() == "task").collect();
    assert!(!tasks.is_empty(), "task without brackets must parse");
    assert!(tasks[0].raw_args().is_empty(), "raw_args must be empty for bracketless element");
}

#[test]
fn test_parse_str_element_with_empty_brackets() {
    let raw = "@note[]{\n  Empty bracket note\n}\n";
    let els = parser::parse_str(raw);
    let notes: Vec<_> = els.values().filter(|e| e.sign() == "note").collect();
    assert!(!notes.is_empty(), "note with empty brackets must parse");
}

#[test]
fn test_parse_str_body_with_newlines_preserved() {
    let raw = "@note[]{\n  Line one\n  Line two\n  Line three\n}\n";
    let els = parser::parse_str(raw);
    let note = els.values().find(|e| e.sign() == "note").unwrap();
    assert!(note.body_str().contains("Line one"), "body line 1 must be preserved");
    assert!(note.body_str().contains("Line two"), "body line 2 must be preserved");
    assert!(note.body_str().contains("Line three"), "body line 3 must be preserved");
}

#[test]
fn test_parse_str_unknown_sign_creates_unknown_element() {
    let raw = "@foobar[custom: val]{\n  Unknown element body\n}\n";
    let els = parser::parse_str(raw);
    let unknowns: Vec<_> = els.values().filter(|e| e.is_unknown()).collect();
    assert!(!unknowns.is_empty(), "unrecognized sign must create Unknown element");
}

#[test]
fn test_parse_str_ten_elements_in_sequence() {
    let parts: Vec<String> = (1..=10)
        .map(|i| format!("@task[]{{\n  Task {}\n}}\n", i))
        .collect();
    let raw = parts.join("");
    let els = parser::parse_str(&raw);
    let tasks: Vec<_> = els.values().filter(|e| e.sign() == "task").collect();
    assert_eq!(tasks.len(), 10, "ten sequential tasks must all parse");
}

#[test]
fn test_parse_str_mixed_types_correct_counts() {
    let raw = concat!(
        "@task[]{\n  T\n}\n",
        "@note[]{\n  N\n}\n",
        "@log[]{\n  L\n}\n",
        "@reminder[]{\n  R\n}\n",
        "@character[name: X]{\n  C\n}\n",
    );
    let els = parser::parse_str(raw);
    assert_eq!(els.values().filter(|e| e.sign() == "task").count(), 1);
    assert_eq!(els.values().filter(|e| e.sign() == "note").count(), 1);
    assert_eq!(els.values().filter(|e| e.sign() == "log").count(), 1);
    assert_eq!(els.values().filter(|e| e.sign() == "reminder").count(), 1);
    assert_eq!(els.values().filter(|e| e.sign() == "character").count(), 1);
}

#[test]
fn test_parse_str_element_body_with_at_sign_in_content() {
    // Body containing '@' that is NOT a new element opener (no following word + brace)
    let raw = "@note[]{\n  Email: user@example.com\n}\n";
    let els = parser::parse_str(raw);
    let notes: Vec<_> = els.values().filter(|e| e.sign() == "note").collect();
    assert!(!notes.is_empty(), "note with '@' in body must parse");
    assert!(
        notes[0].body_str().contains("user@example.com"),
        "email in body must be preserved"
    );
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 54: Extreme human-ref boundary — task-0, task-1000, non-numeric suffix
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_ref_resolver_task_zero_is_invalid() {
    let raw = "@task[]{\n  T\n}\n";
    let els = parser::parse_str(raw);
    let resolver = RefResolver::new(&els);
    // task-0 is never valid (counters start at 1)
    assert!(resolver.to_epoch("task-0").is_none(), "task-0 must not resolve");
}

#[test]
fn test_ref_resolver_task_1000_resolves_only_if_1000_tasks_exist() {
    let raw = "@task[]{\n  T\n}\n";
    let els = parser::parse_str(raw);
    let resolver = RefResolver::new(&els);
    // Only 1 task → task-1000 must not resolve
    assert!(resolver.to_epoch("task-1000").is_none());
    // task-1 must resolve
    assert!(resolver.to_epoch("task-1").is_some());
}


#[test]
fn test_ref_resolver_non_numeric_suffix_is_none() {
    let raw = "@task[]{\n  T\n}\n";
    let els = parser::parse_str(raw);
    let resolver = RefResolver::new(&els);
    assert!(resolver.to_epoch("task-abc").is_none());
    assert!(resolver.to_epoch("task-1a").is_none());
    assert!(resolver.to_epoch("task-").is_none());
}

#[test]
fn test_ref_resolver_epoch_ref_with_only_date_is_none() {
    // "20270101" with no dot-suffix is not a valid epoch ref (needs at least YYYYMMDD.n)
    let raw = "@task[]{\n  T\n}\n";
    let els = parser::parse_str(raw);
    let resolver = RefResolver::new(&els);
    // Neither epoch nor human ref
    assert!(resolver.to_epoch("20270101").is_none());
    assert!(resolver.to_human("20270101").is_none());
}

#[test]
fn test_ref_resolver_large_number_of_tasks_all_resolve() {
    let parts: Vec<String> = (1..=50)
        .map(|i| format!("@task[]{{\n  Task {}\n}}\n", i))
        .collect();
    let raw = parts.join("");
    let els = parser::parse_str(&raw);
    let resolver = RefResolver::new(&els);

    for i in 1..=50 {
        let human = format!("task-{}", i);
        assert!(
            resolver.to_epoch(&human).is_some(),
            "{} must resolve for 50-task set",
            human
        );
    }
    // task-51 must not resolve (only 50 tasks)
    assert!(resolver.to_epoch("task-51").is_none());
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 55: Element invariants — body_str, tags(), sign() contracts
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_all_elements_have_consistent_kind_and_sign() {
    // For every element kind, kind() and sign() must agree
    let raw = concat!(
        "@task[]{\n  T\n}\n",
        "@note[]{\n  N\n}\n",
        "@log[]{\n  L\n}\n",
        "@reminder[]{\n  R\n}\n",
        "@character[name: Q]{\n  C\n}\n",
        "@unknown_type[]{\n  U\n}\n",
    );
    let els = parser::parse_str(raw);
    for (_, el) in &els {
        if el.is_mps_group() {
            continue;
        }
        let sign = el.sign();
        let kind = el.kind();
        let kind_str = format!("{}", kind);
        // For non-unknown elements, kind display must equal sign
        if !el.is_unknown() {
            assert_eq!(
                kind_str, sign,
                "kind() display '{}' must equal sign() '{}' for element",
                kind_str, sign
            );
        }
    }
}

#[test]
fn test_body_str_never_includes_wrapper_braces() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2027, 7, 1);
    store.append("task", "Just the body text", &[], &[], date).unwrap();

    let els = store.parse_date(date).unwrap();
    let task = els.values().find(|e| e.sign() == "task").unwrap();
    // body_str must NOT include the outer { and }
    assert!(
        !task.body_str().starts_with('{'),
        "body_str must not start with opening brace"
    );
    assert!(
        !task.body_str().trim().ends_with('}'),
        "body_str must not end with closing brace"
    );
    assert!(task.body_str().contains("Just the body text"));
}

#[test]
fn test_tags_returns_empty_for_element_with_only_attrs() {
    let raw = "@task[status: done]{\n  Done task\n}\n";
    let els = parser::parse_str(raw);
    let task = els.values().find(|e| e.sign() == "task").unwrap();
    assert!(
        task.tags().is_empty(),
        "task with only attr (status:done) must have empty tags"
    );
}

#[test]
fn test_tags_does_not_include_attr_keys() {
    let raw = "@task[work, project: alpha]{\n  Mixed task\n}\n";
    let els = parser::parse_str(raw);
    let task = els.values().find(|e| e.sign() == "task").unwrap();
    assert!(task.tags().contains(&"work".to_string()), "tag 'work' must be in tags()");
    assert!(
        !task.tags().contains(&"project".to_string()),
        "attr key 'project' must NOT appear in tags()"
    );
    assert!(
        !task.tags().iter().any(|t| t.contains(':')),
        "no colon-containing tag should appear (those are attrs)"
    );
}

#[test]
fn test_is_unknown_only_for_unknown_elements() {
    let raw = concat!(
        "@task[]{\n  T\n}\n",
        "@bogustype[]{\n  U\n}\n",
    );
    let els = parser::parse_str(raw);
    for (_, el) in &els {
        if el.is_mps_group() {
            continue;
        }
        if el.sign() == "task" {
            assert!(!el.is_unknown(), "task must not be unknown");
        } else if el.sign() == "bogustype" {
            assert!(el.is_unknown(), "bogustype must be unknown");
        }
    }
}

#[test]
fn test_element_from_parts_roundtrip_all_types() {
    let types = ["task", "note", "log", "reminder", "character"];
    for t in &types {
        let el = Element::from_parts(t, "work".to_string(), vec![], "body".to_string());
        assert_eq!(el.sign(), *t, "from_parts sign must match type");
        assert!(!el.is_unknown(), "known type must not be unknown");
    }
    // Unknown type
    let el = Element::from_parts("mythical", "args".to_string(), vec![], "body".to_string());
    assert!(el.is_unknown(), "unknown type must be_unknown");
    assert_eq!(el.sign(), "mythical");
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 56: Store find_file / find_or_create_path behavior
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_find_file_returns_none_when_no_file_for_date() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let result = store.find_file(make_date(2027, 8, 1));
    assert!(result.is_none(), "find_file must return None for date with no file");
}

#[test]
fn test_find_file_returns_path_after_append() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2027, 8, 2);
    store.append("task", "A task", &[], &[], date).unwrap();
    let result = store.find_file(date);
    assert!(result.is_some(), "find_file must return Some after append creates file");
    assert!(result.unwrap().exists(), "found path must actually exist");
}

#[test]
fn test_find_or_create_path_creates_dir_entry() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2027, 8, 3);
    let path = store.find_or_create_path(date);
    // The path itself may not exist until a write happens, but the name must be correct
    let filename = path.file_name().unwrap().to_str().unwrap();
    assert!(filename.starts_with("20270803"), "filename must start with YYYYMMDD");
    assert!(filename.ends_with(".mps"), "filename must end with .mps");
}

#[test]
fn test_find_file_picks_up_manually_created_file() {
    let dir = tempfile::tempdir().unwrap();
    // Create a file manually with a valid .mps name
    let filename = "20270804.1000000099.mps";
    let content = "@mps{\n@task[]{\n  Manual task\n}\n}\n";
    std::fs::write(dir.path().join(filename), content).unwrap();

    let store = Store::new(dir.path());
    let result = store.find_file(make_date(2027, 8, 4));
    assert!(result.is_some(), "find_file must find manually created file");
    let els = store.parse_date(make_date(2027, 8, 4)).unwrap();
    let tasks: Vec<_> = els.values().filter(|e| e.sign() == "task").collect();
    assert_eq!(tasks.len(), 1, "manually created file must parse correctly");
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 57: Disastrous file system — permissions, missing dirs
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_parse_date_nonexistent_date_returns_empty() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    // Date with no file should return empty, not error
    let els = store.parse_date(make_date(2019, 1, 1)).unwrap();
    assert!(
        els.values()
            .filter(|e| !e.is_mps_group() && !e.is_unknown())
            .count()
            == 0
    );
}

#[test]
fn test_store_all_files_on_dir_with_only_hidden_files() {
    let dir = tempfile::tempdir().unwrap();
    // Hidden files and metadata files should not be picked up
    std::fs::write(dir.path().join(".gitignore"), ".mps.local\n").unwrap();
    std::fs::write(dir.path().join(".mps.local"), "{}").unwrap();
    std::fs::write(dir.path().join(".mps.meta"), "{}").unwrap();
    let store = Store::new(dir.path());
    let files = store.all_files().unwrap();
    assert!(files.is_empty(), "hidden files and meta files must not appear in all_files");
}

#[test]
fn test_search_on_dir_with_only_meta_files_returns_empty() {
    let dir = tempfile::tempdir().unwrap();
    std::fs::write(dir.path().join(".mps.local"), "{}").unwrap();
    std::fs::write(dir.path().join(".mps.meta"), "{}").unwrap();
    let store = Store::new(dir.path());
    let results = store.search("anything", None, None, None).unwrap();
    assert!(results.is_empty());
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 58: Reminder data — at field parsing correctness
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_reminder_at_various_time_formats() {
    let cases = [
        ("at: 9am", "9am"),
        ("at: 2pm", "2pm"),
        ("at: 10:30am", "10:30am"),
        ("at: 14:00", "14:00"),
        ("at: midnight", "midnight"),
    ];
    for (args, expected) in &cases {
        let raw = format!("@reminder[{}]{{\n  Reminder\n}}\n", args);
        let els = parser::parse_str(&raw);
        let rem = els.values().find(|e| e.sign() == "reminder").unwrap();
        if let Element::Reminder { data, .. } = rem {
            assert_eq!(
                data.at.as_deref(),
                Some(*expected),
                "at field must be '{}' for args '{}'",
                expected,
                args
            );
        } else {
            panic!("expected Reminder for args '{}'", args);
        }
    }
}

#[test]
fn test_reminder_with_tags_and_at() {
    let raw = "@reminder[work, urgent, at: 5pm]{\n  Team standup\n}\n";
    let els = parser::parse_str(raw);
    let rem = els.values().find(|e| e.sign() == "reminder").unwrap();
    assert!(rem.tags().contains(&"work".to_string()));
    assert!(rem.tags().contains(&"urgent".to_string()));
    if let Element::Reminder { data, .. } = rem {
        assert_eq!(data.at.as_deref(), Some("5pm"));
    }
}

#[test]
fn test_reminder_without_at_has_none() {
    let raw = "@reminder[work]{\n  No time specified\n}\n";
    let els = parser::parse_str(raw);
    let rem = els.values().find(|e| e.sign() == "reminder").unwrap();
    if let Element::Reminder { data, .. } = rem {
        assert!(data.at.is_none(), "reminder without 'at' must have None at field");
    }
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 59: Task status transitions and log start/end fields
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_task_default_status_is_open() {
    let raw = "@task[]{\n  New task\n}\n";
    let els = parser::parse_str(raw);
    let task = els.values().find(|e| e.sign() == "task").unwrap();
    if let Element::Task { data, .. } = task {
        assert_eq!(data.status_str(), "open", "task without status attr defaults to open");
    }
}

#[test]
fn test_task_status_done_and_open_roundtrip() {
    for status in &["open", "done"] {
        let raw = format!("@task[status: {}]{{\n  Task\n}}\n", status);
        let els = parser::parse_str(&raw);
        let task = els.values().find(|e| e.sign() == "task").unwrap();
        if let Element::Task { data, .. } = task {
            assert_eq!(data.status_str(), *status);
        }
    }
}

#[test]
fn test_log_start_and_end_fields_parsed() {
    let raw = "@log[start: 9:00, end: 10:30]{\n  Standup\n}\n";
    let els = parser::parse_str(raw);
    let log = els.values().find(|e| e.sign() == "log").unwrap();
    if let Element::Log { data, .. } = log {
        assert_eq!(data.start.as_deref(), Some("9:00"), "start must be parsed");
        assert_eq!(data.end.as_deref(), Some("10:30"), "end must be parsed");
    }
}

#[test]
fn test_log_without_times_has_none_fields() {
    let raw = "@log[work]{\n  Timeless log\n}\n";
    let els = parser::parse_str(raw);
    let log = els.values().find(|e| e.sign() == "log").unwrap();
    if let Element::Log { data, .. } = log {
        assert!(data.start.is_none(), "log without start must have None start");
        assert!(data.end.is_none(), "log without end must have None end");
    }
}

#[test]
fn test_log_rewrite_start_and_end() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2027, 9, 1);
    store.append("log", "Work session", &[], &[], date).unwrap();

    let els = store.parse_date(date).unwrap();
    let key = els.keys().find(|k| els[*k].sign() == "log").unwrap().clone();

    let mut attrs = HashMap::new();
    attrs.insert("start".to_string(), "10:00".to_string());
    attrs.insert("end".to_string(), "11:30".to_string());
    store.rewrite_element(&key, &attrs, date).unwrap();

    let els2 = store.parse_date(date).unwrap();
    let log = els2.get(&key).unwrap();
    if let Element::Log { data, .. } = log {
        assert_eq!(data.start.as_deref(), Some("10:00"));
        assert_eq!(data.end.as_deref(), Some("11:30"));
    }
}

// ═══════════════════════════════════════════════════════════════════════════
// ITERATION 60: Final integration — multi-op scenario end-to-end
// ═══════════════════════════════════════════════════════════════════════════

#[test]
fn test_full_workflow_append_search_rewrite_delete() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2027, 10, 1);

    // 1. Append 3 tasks
    store.append("task", "Fix the auth bug", &["backend".to_string(), "urgent".to_string()], &[], date).unwrap();
    store.append("task", "Write unit tests", &["backend".to_string()], &[], date).unwrap();
    store.append("note", "Auth module overview", &["backend".to_string()], &[], date).unwrap();

    // 2. Search finds backend elements
    let results = store.search("", None, Some("backend"), None).unwrap();
    assert_eq!(results.len(), 3, "all 3 backend elements must be found");

    // 3. Search for urgent task
    let urgent = store.search("auth bug", Some("task"), Some("urgent"), None).unwrap();
    assert_eq!(urgent.len(), 1);

    // 4. Mark auth bug as done
    let key = urgent[0].element.refs().first()
        .map(|_| {
            let els = store.parse_date(date).unwrap();
            els.iter()
                .find(|(_, e)| e.sign() == "task" && e.body_str().contains("auth bug"))
                .map(|(k, _)| k.clone())
        })
        .flatten()
        .or_else(|| {
            let els = store.parse_date(date).unwrap();
            els.iter()
                .find(|(_, e)| e.sign() == "task" && e.body_str().contains("auth bug"))
                .map(|(k, _)| k.clone())
        })
        .unwrap();

    let mut attrs = HashMap::new();
    attrs.insert("status".to_string(), "done".to_string());
    let changed = store.rewrite_element(&key, &attrs, date).unwrap();
    assert!(changed, "marking auth bug done must return true");

    // 5. Verify status is done
    let els = store.parse_date(date).unwrap();
    let task = els.get(&key).unwrap();
    if let Element::Task { data, .. } = task {
        assert_eq!(data.status_str(), "done");
    }

    // 6. Delete the done task
    let deleted = store.delete_element(&key, date).unwrap();
    assert!(deleted);

    // 7. Final state: 2 elements remain (Write unit tests + note)
    let final_els = store.parse_date(date).unwrap();
    let remaining: Vec<_> = final_els
        .values()
        .filter(|e| !e.is_mps_group() && !e.is_unknown())
        .collect();
    assert_eq!(remaining.len(), 2, "2 elements must remain after deleting done task");

    // 8. Search for deleted task returns empty
    let deleted_search = store.search("auth bug", None, None, None).unwrap();
    assert!(deleted_search.is_empty(), "deleted task must no longer appear in search");
}

#[test]
fn test_full_workflow_cache_invalidated_by_mutation() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2027, 10, 2);

    // Append and build initial cache
    store.append("task", "Cached task", &["work".to_string()], &[], date).unwrap();
    let snap1 = MetaCache::build_snapshot(dir.path());

    let mut local = MetaLocal::load(dir.path());
    local.cache.files_snapshot = snap1.clone();
    local.cache.tag_counts.insert("work".to_string(), 1);
    local.save(dir.path()).unwrap();

    // Cache must be valid now
    assert!(MetaLocal::load(dir.path()).cache.is_valid(&snap1));

    // Append another element → file grows → cache stale
    store.append("task", "New task", &["urgent".to_string()], &[], date).unwrap();
    let snap2 = MetaCache::build_snapshot(dir.path());

    let local2 = MetaLocal::load(dir.path());
    assert!(
        !local2.cache.is_valid(&snap2),
        "cache must be stale after file grows from append"
    );
}

#[test]
fn test_full_workflow_ref_roundtrip_survives_rewrite() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2027, 10, 3);

    store.append("task", "Ref roundtrip task", &[], &[], date).unwrap();
    store.append("note", "Some note", &[], &[], date).unwrap();

    // Get human ref for the task
    let els = store.parse_date(date).unwrap();
    let task_key = els.iter().find(|(_, e)| e.sign() == "task").map(|(k, _)| k.clone()).unwrap();
    let resolver = RefResolver::new(&els);
    let human = resolver.to_human(&task_key).unwrap().to_string();

    // Rewrite using human ref
    let mut attrs = HashMap::new();
    attrs.insert("status".to_string(), "done".to_string());
    let changed = store.rewrite_element(&human, &attrs, date).unwrap();
    assert!(changed, "human ref rewrite must work");

    // After rewrite, epoch key still resolves to same element
    let els2 = store.parse_date(date).unwrap();
    let task2 = els2.get(&task_key).unwrap();
    if let Element::Task { data, .. } = task2 {
        assert_eq!(data.status_str(), "done", "rewrite via human ref must persist");
    }
}

#[test]
fn test_100_element_stress_all_operations() {
    let dir = tempfile::tempdir().unwrap();
    let store = Store::new(dir.path());
    let date = make_date(2027, 10, 4);

    // Append 100 tasks
    for i in 1..=100 {
        store.append("task", &format!("Stress task {}", i), &[format!("s{}", i % 10)], &[], date).unwrap();
    }

    // Verify all 100 exist
    let els = store.parse_date(date).unwrap();
    assert_eq!(
        els.values().filter(|e| e.sign() == "task").count(),
        100
    );

    // Rewrite tasks 1, 50, 100 as done
    let task_keys: Vec<String> = els.iter()
        .filter(|(_, e)| e.sign() == "task")
        .map(|(k, _)| k.clone())
        .collect();

    for key in task_keys.iter().take(3) {
        let mut attrs = HashMap::new();
        attrs.insert("status".to_string(), "done".to_string());
        store.rewrite_element(key, &attrs, date).unwrap();
    }

    // Search for done tasks
    let els2 = store.parse_date(date).unwrap();
    let done_count = els2.values()
        .filter(|e| {
            if let Element::Task { data, .. } = e {
                data.status_str() == "done"
            } else {
                false
            }
        })
        .count();
    assert_eq!(done_count, 3, "exactly 3 tasks must be done");

    // Delete all 100 tasks
    for _ in 0..100 {
        let els = store.parse_date(date).unwrap();
        if let Some(key) = els.iter().find(|(_, e)| e.sign() == "task").map(|(k, _)| k.clone()) {
            store.delete_element(&key, date).unwrap();
        } else {
            break;
        }
    }

    let final_count = store.parse_date(date).unwrap()
        .values()
        .filter(|e| e.sign() == "task")
        .count();
    assert_eq!(final_count, 0, "all 100 tasks must be deleted");
}
