//! Integration tests for Gap 1 and Gap 5 fixes.
//!
//! Gap 1: Observations should use confidence-based tiers (4 or 5), not Tier 3
//! Gap 5: Superseding claims should auto-deprecate old claims, warn on duplicates

use aphoria::claims_file::ClaimsFile;
use aphoria::{AuthoredClaim, AuthoredValue, ClaimStatus, ComparisonMode};
use stemedb_core::types::SourceClass;
use tempfile::TempDir;

/// Test Gap 1: Observations use confidence-based tiers (not Tier 3 Expert)
#[test]
fn test_gap1_observation_tiers() {
    // High confidence observation should be Tier 4 (Community)
    let high_confidence_tier = aphoria::bridge::observation_to_tier(0.95);
    assert_eq!(high_confidence_tier, SourceClass::Community);
    assert_eq!(high_confidence_tier.tier(), 4);
    assert!((high_confidence_tier.authority_weight() - 0.3).abs() < f32::EPSILON);

    // Low confidence observation should be Tier 5 (Anecdotal)
    let low_confidence_tier = aphoria::bridge::observation_to_tier(0.7);
    assert_eq!(low_confidence_tier, SourceClass::Anecdotal);
    assert_eq!(low_confidence_tier.tier(), 5);
    assert!((low_confidence_tier.authority_weight() - 0.1).abs() < f32::EPSILON);

    // Boundary case: exactly 0.9 should be Tier 4
    let boundary_tier = aphoria::bridge::observation_to_tier(0.9);
    assert_eq!(boundary_tier, SourceClass::Community);
    assert_eq!(boundary_tier.tier(), 4);
}

/// Test Gap 5: Supersede auto-deprecates old claims
#[test]
fn test_gap5_supersede_auto_deprecates() {
    let temp_dir = TempDir::new().expect("create temp dir");
    let claims_path = temp_dir.path().join("claims.toml");

    let mut claims_file = ClaimsFile::new();

    // Create initial claim
    let claim_v1 = AuthoredClaim {
        id: "test-001".to_string(),
        concept_path: "test/feature/enabled".to_string(),
        predicate: "value".to_string(),
        value: AuthoredValue::Bool(true),
        comparison: ComparisonMode::Equals,
        provenance: "Initial implementation".to_string(),
        invariant: "Feature should be enabled".to_string(),
        consequence: "Feature disabled".to_string(),
        authority_tier: "expert".to_string(),
        evidence: vec![],
        category: "feature".to_string(),
        status: ClaimStatus::Active,
        supersedes: None,
        created_by: "dev".to_string(),
        created_at: "2026-02-08T10:00:00Z".to_string(),
        updated_at: None,
    };

    claims_file.add(claim_v1);
    assert_eq!(claims_file.len(), 1);
    assert_eq!(claims_file.find_by_id("test-001").map(|c| &c.status), Some(&ClaimStatus::Active));

    // Supersede with v2
    let claim_v2 = AuthoredClaim {
        id: "test-002".to_string(),
        concept_path: "test/feature/enabled".to_string(),
        predicate: "value".to_string(),
        value: AuthoredValue::Bool(false),
        comparison: ComparisonMode::Equals,
        provenance: "Updated after review".to_string(),
        invariant: "Feature should be disabled".to_string(),
        consequence: "Feature enabled".to_string(),
        authority_tier: "expert".to_string(),
        evidence: vec!["Review notes".to_string()],
        category: "feature".to_string(),
        status: ClaimStatus::Active,
        supersedes: Some("test-001".to_string()),
        created_by: "lead".to_string(),
        created_at: "2026-02-08T11:00:00Z".to_string(),
        updated_at: None,
    };

    claims_file.supersede("test-001", claim_v2).expect("supersede");

    // Verify old claim is superseded
    assert_eq!(
        claims_file.find_by_id("test-001").map(|c| &c.status),
        Some(&ClaimStatus::Superseded)
    );

    // Verify new claim is active
    assert_eq!(claims_file.find_by_id("test-002").map(|c| &c.status), Some(&ClaimStatus::Active));

    // Verify lineage link
    assert_eq!(
        claims_file.find_by_id("test-002").and_then(|c| c.supersedes.as_deref()),
        Some("test-001")
    );

    // Verify persistence
    claims_file.save(&claims_path).expect("save");
    let loaded = ClaimsFile::load(&claims_path).expect("load");
    assert_eq!(loaded.len(), 2);
    assert_eq!(loaded.find_by_id("test-001").map(|c| &c.status), Some(&ClaimStatus::Superseded));
}

/// Test Gap 5: Duplicate validation warns when creating duplicate active claims
#[test]
fn test_gap5_duplicate_validation_warning() {
    let mut claims_file = ClaimsFile::new();

    // Create first claim
    let claim1 = AuthoredClaim {
        id: "dup-001".to_string(),
        concept_path: "test/config/timeout".to_string(),
        predicate: "value".to_string(),
        value: AuthoredValue::Number(30.0),
        comparison: ComparisonMode::Equals,
        provenance: "Initial config".to_string(),
        invariant: "Timeout must be 30s".to_string(),
        consequence: "Requests timeout too fast".to_string(),
        authority_tier: "team_policy".to_string(),
        evidence: vec![],
        category: "config".to_string(),
        status: ClaimStatus::Active,
        supersedes: None,
        created_by: "dev1".to_string(),
        created_at: "2026-02-08T10:00:00Z".to_string(),
        updated_at: None,
    };

    claims_file.add(claim1);

    // Create duplicate (same concept_path + predicate, different ID)
    let claim2 = AuthoredClaim {
        id: "dup-002".to_string(),
        concept_path: "test/config/timeout".to_string(), // Same
        predicate: "value".to_string(),                  // Same
        value: AuthoredValue::Number(60.0),              // Different value
        comparison: ComparisonMode::Equals,
        provenance: "Updated config".to_string(),
        invariant: "Timeout must be 60s".to_string(),
        consequence: "Requests timeout too slow".to_string(),
        authority_tier: "team_policy".to_string(),
        evidence: vec![],
        category: "config".to_string(),
        status: ClaimStatus::Active,
        supersedes: None,
        created_by: "dev2".to_string(),
        created_at: "2026-02-08T11:00:00Z".to_string(),
        updated_at: None,
    };

    // This should print a warning (captured in test output)
    // but still add the claim
    claims_file.add(claim2);

    assert_eq!(claims_file.len(), 2);
    assert_eq!(claims_file.find_by_status(&ClaimStatus::Active).len(), 2);
}

/// Test Gap 5: No warning when duplicate is deprecated
#[test]
fn test_gap5_no_warning_for_deprecated_duplicate() {
    let mut claims_file = ClaimsFile::new();

    // Create and deprecate first claim
    let claim1 = AuthoredClaim {
        id: "old-001".to_string(),
        concept_path: "test/feature/mode".to_string(),
        predicate: "value".to_string(),
        value: AuthoredValue::Text("legacy".to_string()),
        comparison: ComparisonMode::Equals,
        provenance: "Old implementation".to_string(),
        invariant: "Mode should be legacy".to_string(),
        consequence: "Mode incorrect".to_string(),
        authority_tier: "expert".to_string(),
        evidence: vec![],
        category: "feature".to_string(),
        status: ClaimStatus::Active,
        supersedes: None,
        created_by: "dev".to_string(),
        created_at: "2026-02-08T10:00:00Z".to_string(),
        updated_at: None,
    };

    claims_file.add(claim1);
    claims_file.deprecate("old-001", "2026-02-08T11:00:00Z").expect("deprecate");

    // Now add new claim with same concept_path/predicate
    // Should NOT warn because the first is deprecated
    let claim2 = AuthoredClaim {
        id: "new-001".to_string(),
        concept_path: "test/feature/mode".to_string(), // Same
        predicate: "value".to_string(),                // Same
        value: AuthoredValue::Text("modern".to_string()),
        comparison: ComparisonMode::Equals,
        provenance: "New implementation".to_string(),
        invariant: "Mode should be modern".to_string(),
        consequence: "Mode incorrect".to_string(),
        authority_tier: "expert".to_string(),
        evidence: vec![],
        category: "feature".to_string(),
        status: ClaimStatus::Active,
        supersedes: None,
        created_by: "dev".to_string(),
        created_at: "2026-02-08T12:00:00Z".to_string(),
        updated_at: None,
    };

    // Should NOT print warning
    claims_file.add(claim2);

    assert_eq!(claims_file.len(), 2);
    assert_eq!(claims_file.find_by_status(&ClaimStatus::Active).len(), 1);
    assert_eq!(claims_file.find_by_status(&ClaimStatus::Deprecated).len(), 1);
}
