use chrono::Utc;

use crate::config::OmReflectorConfigSnapshot;
use crate::om::{OmInferenceModelConfig, OmOriginType, OmReflectorRequest, OmScope};
use crate::state::OmActiveEntry;

use super::*;

fn reflector_config(mode: OmReflectorMode, model_enabled: bool) -> OmReflectorConfig {
    OmReflectorConfig {
        mode,
        model_enabled,
        llm_endpoint: "http://127.0.0.1:11434/api/chat".to_string(),
        llm_model: "qwen2.5:7b-instruct".to_string(),
        llm_timeout_ms: DEFAULT_OM_REFLECTOR_LLM_TIMEOUT_MS,
        llm_max_output_tokens: DEFAULT_OM_REFLECTOR_LLM_MAX_OUTPUT_TOKENS,
        llm_temperature_milli: DEFAULT_OM_REFLECTOR_LLM_TEMPERATURE_MILLI,
        llm_strict: false,
        llm_target_observation_tokens: DEFAULT_REFLECTOR_OBSERVATION_TOKENS,
        llm_buffer_activation: DEFAULT_REFLECTOR_BUFFER_ACTIVATION,
        max_chars: DEFAULT_OM_REFLECTOR_MAX_CHARS,
    }
}

fn om_record(active_observations: &str) -> crate::om::OmRecord {
    let now = Utc::now();
    crate::om::OmRecord {
        id: "om-reflector-test".to_string(),
        scope: OmScope::Session,
        scope_key: "session:reflector-test".to_string(),
        session_id: Some("reflector-test".to_string()),
        thread_id: None,
        resource_id: None,
        generation_count: 0,
        last_applied_outbox_event_id: None,
        origin_type: OmOriginType::Initial,
        active_observations: active_observations.to_string(),
        observation_token_count: 10,
        pending_message_tokens: 0,
        last_observed_at: Some(now),
        current_task: None,
        suggested_response: None,
        last_activated_message_ids: Vec::new(),
        observer_trigger_count_total: 0,
        reflector_trigger_count_total: 0,
        is_observing: false,
        is_reflecting: false,
        is_buffering_observation: false,
        is_buffering_reflection: false,
        last_buffered_at_tokens: 0,
        last_buffered_at_time: None,
        buffered_reflection: None,
        buffered_reflection_tokens: None,
        buffered_reflection_input_tokens: None,
        created_at: now,
        updated_at: now,
    }
}

fn active_entry(entry_id: &str, text: &str, created_at_rfc3339: &str) -> OmActiveEntry {
    OmActiveEntry {
        entry_id: entry_id.to_string(),
        canonical_thread_id: "thread:1".to_string(),
        priority: "medium".to_string(),
        text: text.to_string(),
        origin_kind: "observation".to_string(),
        created_at: chrono::DateTime::parse_from_rfc3339(created_at_rfc3339)
            .expect("valid timestamp")
            .with_timezone(&Utc),
    }
}

#[test]
fn reflector_prompt_contract_json_contains_v2_contract_fields() {
    let request = OmReflectorRequest {
        scope: OmScope::Session,
        scope_key: "session:reflector-contract".to_string(),
        model: OmInferenceModelConfig {
            provider: "local-http".to_string(),
            model: "qwen2.5:7b".to_string(),
            max_output_tokens: 512,
            temperature_milli: 0,
        },
        generation_count: 7,
        active_observations: "line-a\nline-b".to_string(),
    };
    let encoded =
        reflector_prompt_contract_json(&request, 1, false, DEFAULT_OM_REFLECTOR_MAX_CHARS)
            .expect("json");
    let value = serde_json::from_str::<serde_json::Value>(&encoded).expect("parse json");
    assert_eq!(value["header"]["contract_name"], "axiomsync.om.prompt");
    assert_eq!(value["header"]["contract_version"], "2.0.0");
    assert_eq!(value["header"]["protocol_version"], "om-v2");
    assert_eq!(value["header"]["request_kind"], "reflector");
    assert_eq!(value["generation_count"], 7);
    assert_eq!(value["compression_level"], 1);
}

#[test]
fn parse_reflector_response_value_reads_object_payload() {
    let payload = serde_json::json!({
        "header": {
            "contract_name": "axiomsync.om.prompt",
            "contract_version": "2.0.0",
            "protocol_version": "om-v2"
        },
        "reflection": "one two three",
        "reflected_observation_line_count": 3,
        "usage": {"input_tokens": 11, "output_tokens": 7},
        "reflection_token_count": 4,
        "current_task": "Primary: summarize",
        "suggested_response": "Ask for confirmation"
    });
    let parsed =
        parse_reflector_response_value(&payload, "a\nb\nc", DEFAULT_OM_REFLECTOR_MAX_CHARS)
            .expect("parsed");
    assert_eq!(parsed.reflection, "one two three");
    assert_eq!(parsed.usage.input_tokens, 11);
    assert_eq!(parsed.usage.output_tokens, 7);
    assert_eq!(parsed.reflection_token_count, 4);
    assert_eq!(parsed.current_task.as_deref(), Some("Primary: summarize"));
    assert_eq!(
        parsed.suggested_response.as_deref(),
        Some("Ask for confirmation")
    );
}

#[test]
fn parse_reflector_response_value_accepts_mastra_alias_fields() {
    let payload = serde_json::json!({
        "header": {
            "contract_name": "axiomsync.om.prompt",
            "contract_version": "2.0.0",
            "protocol_version": "om-v2"
        },
        "observations": "compact summary",
        "reflectedObservationLineCount": 2,
        "usage": {"inputTokens": 9, "outputTokens": 4},
        "reflectionTokenCount": 3,
        "currentTask": "Primary: summarize",
        "suggestedContinuation": "Wait for user confirmation"
    });
    let parsed =
        parse_reflector_response_value(&payload, "a\nb\nc", DEFAULT_OM_REFLECTOR_MAX_CHARS)
            .expect("parsed");
    assert_eq!(parsed.reflection, "compact summary");
    assert_eq!(parsed.usage.input_tokens, 9);
    assert_eq!(parsed.usage.output_tokens, 4);
    assert_eq!(parsed.reflection_token_count, 3);
    assert_eq!(parsed.current_task.as_deref(), Some("Primary: summarize"));
    assert_eq!(
        parsed.suggested_response.as_deref(),
        Some("Wait for user confirmation")
    );
}

#[test]
fn parse_reflector_response_value_accepts_continuation_only_payload() {
    let payload = serde_json::json!({
        "header": {
            "contract_name": "axiomsync.om.prompt",
            "contract_version": "2.0.0",
            "protocol_version": "om-v2"
        },
        "current_task": "Primary: implement release hardening",
        "suggested_response": "Proceed with targeted verification"
    });
    let parsed =
        parse_reflector_response_value(&payload, "a\nb\nc", DEFAULT_OM_REFLECTOR_MAX_CHARS)
            .expect("parsed");
    assert!(parsed.reflection.is_empty());
    assert_eq!(parsed.reflection_token_count, 0);
    assert_eq!(
        parsed.current_task.as_deref(),
        Some("Primary: implement release hardening")
    );
    assert_eq!(
        parsed.suggested_response.as_deref(),
        Some("Proceed with targeted verification")
    );
}

#[test]
fn parse_reflector_response_value_rejects_metadata_only_payload() {
    let payload = serde_json::json!({
        "header": {
            "contract_name": "axiomsync.om.prompt",
            "contract_version": "2.0.0",
            "protocol_version": "om-v2"
        },
        "usage": {"input_tokens": 21, "output_tokens": 8},
        "reflected_observation_line_count": 3
    });
    assert!(
        parse_reflector_response_value(&payload, "a\nb\nc", DEFAULT_OM_REFLECTOR_MAX_CHARS)
            .is_none(),
        "metadata-only payload must not be parsed as a valid reflector response"
    );
}

#[test]
fn parse_llm_reflector_response_accepts_embedded_json_content() {
    let payload = serde_json::json!({
        "message": {
            "content": "```json\n{\"header\":{\"contract_name\":\"axiomsync.om.prompt\",\"contract_version\":\"2.0.0\",\"protocol_version\":\"om-v2\"},\"reflection\":\"compact summary\",\"reflected_observation_line_count\":2}\n```"
        }
    });
    let parsed = parse_llm_reflector_response(
        &payload,
        "line-1\nline-2\nline-3",
        DEFAULT_OM_REFLECTOR_MAX_CHARS,
    )
    .expect("parsed");
    assert_eq!(parsed.reflection, "compact summary");
}

#[test]
fn parse_llm_reflector_response_accepts_continuation_only_json_payload() {
    let payload = serde_json::json!({
        "header": {
            "contract_name": "axiomsync.om.prompt",
            "contract_version": "2.0.0",
            "protocol_version": "om-v2"
        },
        "current_task": "Primary: refine om boundary",
        "suggested_response": "Run focused release checks"
    });
    let parsed = parse_llm_reflector_response(
        &payload,
        "line-1\nline-2\nline-3",
        DEFAULT_OM_REFLECTOR_MAX_CHARS,
    )
    .expect("parsed");
    assert!(parsed.reflection.is_empty());
    assert_eq!(
        parsed.current_task.as_deref(),
        Some("Primary: refine om boundary")
    );
    assert_eq!(
        parsed.suggested_response.as_deref(),
        Some("Run focused release checks")
    );
}

#[test]
fn parse_llm_reflector_response_accepts_xml_observations_content() {
    let payload = serde_json::json!({
        "message": {
            "content": "<contract-name>axiomsync.om.prompt</contract-name>\n<contract-version>2.0.0</contract-version>\n<protocol-version>om-v2</protocol-version>\n<observations>\n* 🔴 user prefers direct answers\n* 🟡 agent updated auth flow\n</observations>\n<current-task>\nPrimary: debug auth\n</current-task>\n<suggested-response>\nAsk user to confirm\n</suggested-response>"
        }
    });
    let parsed = parse_llm_reflector_response(
        &payload,
        "line-1\nline-2\nline-3",
        DEFAULT_OM_REFLECTOR_MAX_CHARS,
    )
    .expect("parsed");
    assert!(parsed.reflection.contains("user prefers direct answers"));
    assert!(parsed.reflection.contains("agent updated auth flow"));
    assert!(parsed.reflection_token_count > 0);
    assert_eq!(parsed.current_task.as_deref(), Some("Primary: debug auth"));
    assert_eq!(
        parsed.suggested_response.as_deref(),
        Some("Ask user to confirm")
    );
}

#[test]
fn parse_llm_reflector_response_accepts_list_items_without_xml_tags() {
    let payload = serde_json::json!({
        "message": {
            "content": "<contract-name>axiomsync.om.prompt</contract-name>\n<contract-version>2.0.0</contract-version>\n<protocol-version>om-v2</protocol-version>\n* 🔴 user prefers direct answers\n- 🟡 agent updated auth flow\n1. 🟢 assistant suggested follow-up"
        }
    });
    let parsed = parse_llm_reflector_response(
        &payload,
        "line-1\nline-2\nline-3",
        DEFAULT_OM_REFLECTOR_MAX_CHARS,
    )
    .expect("parsed");
    assert!(parsed.reflection.contains("user prefers direct answers"));
    assert!(parsed.reflection.contains("agent updated auth flow"));
    assert!(parsed.reflection.contains("assistant suggested follow-up"));
}

#[test]
fn parse_llm_reflector_response_uses_trimmed_content_when_no_xml_or_list() {
    let payload = serde_json::json!({
        "message": {
            "content": "<contract-name>axiomsync.om.prompt</contract-name>\n<contract-version>2.0.0</contract-version>\n<protocol-version>om-v2</protocol-version>\nkeep concise summary for future turns"
        }
    });
    let parsed = parse_llm_reflector_response(
        &payload,
        "line-1\nline-2\nline-3",
        DEFAULT_OM_REFLECTOR_MAX_CHARS,
    )
    .expect("parsed");
    assert_eq!(parsed.reflection, "keep concise summary for future turns");
}

#[test]
fn parse_llm_reflector_response_rejects_xml_without_contract_marker() {
    let payload = serde_json::json!({
        "message": {
            "content": "<observations>\nline-a\nline-b\n</observations>"
        }
    });
    let err = parse_llm_reflector_response(
        &payload,
        "line-1\nline-2\nline-3",
        DEFAULT_OM_REFLECTOR_MAX_CHARS,
    )
    .expect_err("must reject xml fallback without contract marker");
    match err {
        AxiomError::OmInference {
            inference_source,
            kind,
            ..
        } => {
            assert_eq!(inference_source, OmInferenceSource::Reflector);
            assert_eq!(kind, OmInferenceFailureKind::Schema);
        }
        other => panic!("unexpected error type: {other}"),
    }
}

#[test]
fn parse_llm_reflector_response_rejects_xml_without_protocol_marker() {
    let payload = serde_json::json!({
        "message": {
            "content": "<contract-name>axiomsync.om.prompt</contract-name>\n<contract-version>2.0.0</contract-version>\n<observations>\nline-a\nline-b\n</observations>"
        }
    });
    let err = parse_llm_reflector_response(
        &payload,
        "line-1\nline-2\nline-3",
        DEFAULT_OM_REFLECTOR_MAX_CHARS,
    )
    .expect_err("must reject xml fallback without protocol marker");
    match err {
        AxiomError::OmInference {
            inference_source,
            kind,
            ..
        } => {
            assert_eq!(inference_source, OmInferenceSource::Reflector);
            assert_eq!(kind, OmInferenceFailureKind::Schema);
        }
        other => panic!("unexpected error type: {other}"),
    }
}

#[test]
fn parse_llm_reflector_response_rejects_marker_like_plain_text_without_structured_contract() {
    let payload = serde_json::json!({
        "message": {
            "content": "summary: contract_name axiomsync.om.prompt contract_version 2.0.0 protocol_version om-v2\n- line-a\n- line-b"
        }
    });
    let err = parse_llm_reflector_response(
        &payload,
        "line-1\nline-2\nline-3",
        DEFAULT_OM_REFLECTOR_MAX_CHARS,
    )
    .expect_err("must reject non-structured contract marker text");
    assert!(matches!(
        err,
        AxiomError::OmInference {
            inference_source: OmInferenceSource::Reflector,
            kind: OmInferenceFailureKind::Schema,
            ..
        }
    ));
}

#[test]
fn parse_llm_reflector_response_rejects_contract_version_mismatch() {
    let payload = serde_json::json!({
        "header": {
            "contract_name": "axiomsync.om.prompt",
            "contract_version": "9.9.9",
            "protocol_version": "om-v2"
        },
        "reflection": "compact summary",
        "reflected_observation_line_count": 2
    });
    let err =
        parse_llm_reflector_response(&payload, "line-1\nline-2", DEFAULT_OM_REFLECTOR_MAX_CHARS)
            .expect_err("must reject mismatched contract version");
    match err {
        AxiomError::OmInference {
            inference_source,
            kind,
            message,
        } => {
            assert_eq!(inference_source, OmInferenceSource::Reflector);
            assert_eq!(kind, OmInferenceFailureKind::Schema);
            assert!(
                message.contains("contract_version mismatch"),
                "unexpected message: {message}"
            );
        }
        other => panic!("unexpected error type: {other}"),
    }
}

#[test]
fn parse_llm_reflector_response_rejects_protocol_version_mismatch() {
    let payload = serde_json::json!({
        "header": {
            "contract_name": "axiomsync.om.prompt",
            "contract_version": "2.0.0",
            "protocol_version": "om-v999"
        },
        "reflection": "compact summary",
        "reflected_observation_line_count": 2
    });
    let err =
        parse_llm_reflector_response(&payload, "line-1\nline-2", DEFAULT_OM_REFLECTOR_MAX_CHARS)
            .expect_err("must reject mismatched protocol version");
    match err {
        AxiomError::OmInference {
            inference_source,
            kind,
            message,
        } => {
            assert_eq!(inference_source, OmInferenceSource::Reflector);
            assert_eq!(kind, OmInferenceFailureKind::Schema);
            assert!(
                message.contains("protocol_version mismatch"),
                "unexpected message: {message}"
            );
        }
        other => panic!("unexpected error type: {other}"),
    }
}

#[test]
fn parse_llm_reflector_response_rejects_xml_protocol_version_mismatch_marker() {
    let payload = serde_json::json!({
        "message": {
            "content": "<contract-name>axiomsync.om.prompt</contract-name>\n<contract-version>2.0.0</contract-version>\n<protocol-version>om-v999</protocol-version>\n<observations>\n- compact summary\n</observations>"
        }
    });
    let err =
        parse_llm_reflector_response(&payload, "line-1\nline-2", DEFAULT_OM_REFLECTOR_MAX_CHARS)
            .expect_err("must reject xml contract marker with protocol mismatch");
    match err {
        AxiomError::OmInference {
            inference_source,
            kind,
            message,
        } => {
            assert_eq!(inference_source, OmInferenceSource::Reflector);
            assert_eq!(kind, OmInferenceFailureKind::Schema);
            assert!(
                message.contains("missing contract marker"),
                "unexpected message: {message}"
            );
        }
        other => panic!("unexpected error type: {other}"),
    }
}

#[test]
fn parse_llm_reflector_response_returns_schema_taxonomy_for_invalid_payload() {
    let payload = serde_json::json!({"unexpected": "shape"});
    let err = parse_llm_reflector_response(
        &payload,
        "line-1\nline-2\nline-3",
        DEFAULT_OM_REFLECTOR_MAX_CHARS,
    )
    .expect_err("must fail");
    match err {
        AxiomError::OmInference {
            inference_source,
            kind,
            ..
        } => {
            assert_eq!(inference_source, OmInferenceSource::Reflector);
            assert_eq!(kind, OmInferenceFailureKind::Schema);
        }
        other => panic!("unexpected error type: {other}"),
    }
}

#[test]
fn parse_memory_section_xml_returns_inner_observations_text() {
    let content = "<observations>\nline-a\nline-b\n</observations>\n<current-task>x</current-task>";
    let parsed = crate::om::parse_memory_section_xml(content, crate::om::OmParseMode::Strict);
    assert_eq!(parsed.observations, "line-a\nline-b");
}

#[test]
fn parse_memory_section_xml_joins_multiple_observation_blocks() {
    let content =
        "<observations>\nline-a\n</observations>\nnoise\n<observations>\nline-b\n</observations>";
    let parsed = crate::om::parse_memory_section_xml(content, crate::om::OmParseMode::Strict);
    assert_eq!(parsed.observations, "line-a\nline-b");
}

#[test]
fn parse_memory_section_xml_ignores_inline_tag_mentions() {
    let content = "literal <observations>inline</observations> mention\n<observations>\nline-a\n</observations>";
    let parsed = crate::om::parse_memory_section_xml(content, crate::om::OmParseMode::Strict);
    assert_eq!(parsed.observations, "line-a");
}

#[test]
fn deterministic_reflector_response_handles_empty_input() {
    let parsed = deterministic_reflector_response(" \n \n", DEFAULT_OM_REFLECTOR_MAX_CHARS);
    assert!(parsed.reflection.is_empty());
    assert_eq!(parsed.reflection_token_count, 0);
}

#[test]
fn reflector_model_feature_flag_off_forces_deterministic_output() {
    let record = om_record("line-1\nline-2");
    let config = reflector_config(OmReflectorMode::Llm, false);

    let resolved = resolve_reflector_response_with_config(
        &record,
        "session:reflector-test",
        0,
        OmReflectorCallOptions::DEFAULT,
        &config,
        &[],
    )
    .expect("resolve");
    let expected = deterministic_reflector_response(
        &record.active_observations,
        DEFAULT_OM_REFLECTOR_MAX_CHARS,
    );

    assert_eq!(resolved.reflection, expected.reflection);
    assert_eq!(
        resolved.reflection_token_count,
        expected.reflection_token_count
    );
}

#[test]
fn reflector_rollout_profile_overrides_model_enabled_flag() {
    assert!(!resolve_reflector_model_enabled(
        true,
        Some("observer_only")
    ));
    assert!(resolve_reflector_model_enabled(false, Some("full_model")));
    assert!(!resolve_reflector_model_enabled(true, Some("baseline")));
    assert!(resolve_reflector_model_enabled(true, Some("unknown")));
    assert!(!resolve_reflector_model_enabled(false, Some("unknown")));
}

#[test]
fn prepare_reflector_attempt_input_buffered_uses_slice_plan() {
    let mut record = om_record("l1\nl2\nl3\nl4");
    record.observation_token_count = 100;
    let mut config = reflector_config(OmReflectorMode::Deterministic, true);
    config.llm_target_observation_tokens = 80;
    config.llm_buffer_activation = 0.5;

    let prepared =
        prepare_reflector_attempt_input(&record, OmReflectorCallOptions::BUFFERED, &config, &[]);
    assert_eq!(prepared.active_observations, "");
    assert_eq!(prepared.target_threshold_tokens, 40);
    assert_eq!(prepared.reflection_input_tokens_override, Some(0));
}

#[test]
fn prepare_reflector_attempt_input_default_uses_full_observations() {
    let record = om_record("line-1\nline-2");
    let config = reflector_config(OmReflectorMode::Deterministic, true);

    let prepared =
        prepare_reflector_attempt_input(&record, OmReflectorCallOptions::DEFAULT, &config, &[]);
    assert_eq!(prepared.active_observations, "line-1\nline-2");
    assert_eq!(
        prepared.target_threshold_tokens,
        DEFAULT_REFLECTOR_OBSERVATION_TOKENS
    );
    assert_eq!(prepared.reflection_input_tokens_override, None);
}

#[test]
fn resolve_reflection_cover_entry_ids_default_uses_all_entries_in_oldest_first_order() {
    let record = om_record("line-1\nline-2");
    let entries = vec![
        active_entry("entry-new", "line-2", "2026-01-01T00:00:02Z"),
        active_entry("entry-old", "line-1", "2026-01-01T00:00:01Z"),
    ];
    let covers = resolve_reflection_cover_entry_ids(
        &record,
        OmReflectorCallOptions::DEFAULT,
        &OmReflectorConfigSnapshot::default(),
        &entries,
    );
    assert_eq!(
        covers,
        vec!["entry-old".to_string(), "entry-new".to_string()]
    );
}

#[test]
fn resolve_reflection_cover_entry_ids_buffered_selects_oldest_entry_when_first_entry_exceeds_target()
 {
    let mut record = om_record("l1\nl2\nl3\nl4");
    record.observation_token_count = 100;
    record.buffered_reflection = Some("buffered summary".to_string());

    let snapshot = OmReflectorConfigSnapshot {
        llm_target_observation_tokens: Some(80),
        llm_buffer_activation: Some(0.5),
        ..OmReflectorConfigSnapshot::default()
    };

    let entries = vec![
        active_entry("entry-old", "l1\nl2", "2026-01-01T00:00:01Z"),
        active_entry("entry-new", "l3\nl4", "2026-01-01T00:00:02Z"),
    ];
    let covers = resolve_reflection_cover_entry_ids(
        &record,
        OmReflectorCallOptions::DEFAULT,
        &snapshot,
        &entries,
    );
    assert_eq!(covers, vec!["entry-old".to_string()]);
}

#[test]
fn resolve_reflection_cover_entry_ids_buffered_selects_entry_on_boundary_match() {
    let mut record = om_record("l1\nl2\nl3\nl4");
    record.observation_token_count = 80;
    record.buffered_reflection = Some("buffered summary".to_string());

    let snapshot = OmReflectorConfigSnapshot {
        llm_target_observation_tokens: Some(80),
        llm_buffer_activation: Some(0.5),
        ..OmReflectorConfigSnapshot::default()
    };

    let entries = vec![
        active_entry("entry-old", "l1\nl2", "2026-01-01T00:00:01Z"),
        active_entry("entry-new", "l3\nl4", "2026-01-01T00:00:02Z"),
    ];
    let covers = resolve_reflection_cover_entry_ids(
        &record,
        OmReflectorCallOptions::DEFAULT,
        &snapshot,
        &entries,
    );
    assert_eq!(covers, vec!["entry-old".to_string()]);
}

#[test]
fn prepare_reflector_attempt_input_buffered_aligns_with_cover_selection() {
    let mut record = om_record("l1\nl2\nl3\nl4");
    record.observation_token_count = 80;
    record.buffered_reflection = Some("buffered summary".to_string());
    let snapshot = OmReflectorConfigSnapshot {
        llm_target_observation_tokens: Some(80),
        llm_buffer_activation: Some(0.5),
        ..OmReflectorConfigSnapshot::default()
    };
    let config = OmReflectorConfig::from_snapshot(&snapshot);
    let entries = vec![
        active_entry("entry-old", "l1\nl2", "2026-01-01T00:00:01Z"),
        active_entry("entry-new", "l3\nl4", "2026-01-01T00:00:02Z"),
    ];

    let attempt = prepare_reflector_attempt_input(
        &record,
        OmReflectorCallOptions::BUFFERED,
        &config,
        &entries,
    );
    let covers = resolve_reflection_cover_entry_ids(
        &record,
        OmReflectorCallOptions::DEFAULT,
        &snapshot,
        &entries,
    );

    assert_eq!(attempt.active_observations, "l1\nl2");
    assert_eq!(attempt.reflection_input_tokens_override, Some(40));
    assert_eq!(covers, vec!["entry-old".to_string()]);
}

#[test]
fn resolve_reflection_cover_entry_ids_buffered_selects_all_entries_on_full_slice() {
    let mut record = om_record("l1\nl2\nl3\nl4");
    record.observation_token_count = 80;
    record.buffered_reflection = Some("buffered summary".to_string());

    let snapshot = OmReflectorConfigSnapshot {
        llm_target_observation_tokens: Some(200),
        llm_buffer_activation: Some(0.5),
        ..OmReflectorConfigSnapshot::default()
    };

    let entries = vec![
        active_entry("entry-old", "l1\nl2", "2026-01-01T00:00:01Z"),
        active_entry("entry-new", "l3\nl4", "2026-01-01T00:00:02Z"),
    ];
    let covers = resolve_reflection_cover_entry_ids(
        &record,
        OmReflectorCallOptions::DEFAULT,
        &snapshot,
        &entries,
    );
    assert_eq!(
        covers,
        vec!["entry-old".to_string(), "entry-new".to_string()]
    );
}

#[test]
fn validate_reflection_compression_is_strictly_less_than_target() {
    assert!(validate_reflection_compression(39_999, 40_000));
    assert!(!validate_reflection_compression(40_000, 40_000));
    assert!(!validate_reflection_compression(40_001, 40_000));
}

#[test]
fn build_reflector_user_prompt_includes_guidance_and_skip_sections() {
    let prompt = build_reflector_user_prompt(OmReflectorPromptInput {
        observations: "* High user prefers direct answers",
        request_json: Some("{}"),
        manual_prompt: None,
        compression_level: 2,
        skip_continuation_hints: true,
    });
    assert!(prompt.contains("AGGRESSIVE COMPRESSION REQUIRED"));
    assert!(prompt.contains("Do NOT include <current-task> or <suggested-response>"));
}
