//! Integration tests for acknowledgment expiry behavior.
//!
//! Tests the expiry parsing module and the AcknowledgmentInfo struct behavior.
//! Full end-to-end tests with database persistence are complex due to DB locking,
//! so we focus on unit-level integration testing of the expiry logic.

use crate::*;

/// Test expiry parsing module directly - duration format.
#[test]
fn test_expiry_module_parse_duration() {
    let result = crate::expiry::parse_expiry("90d");
    assert!(result.is_ok(), "Should parse 90d");

    let timestamp = result.expect("should parse");
    let now = chrono::Utc::now().timestamp() as u64;

    // Should be approximately 90 days from now
    let expected_min = now + (89 * 24 * 60 * 60);
    let expected_max = now + (91 * 24 * 60 * 60);
    assert!(timestamp >= expected_min && timestamp <= expected_max);
}

/// Test expiry parsing rejects past dates.
#[test]
fn test_expiry_rejects_past_date() {
    let result = crate::expiry::parse_expiry("2020-01-01");
    assert!(result.is_err(), "Should reject past date");

    let err = result.unwrap_err();
    assert!(matches!(err, AphoriaError::InvalidExpiry(msg) if msg.contains("past")));
}

/// Test is_expired function.
#[test]
fn test_is_expired_function() {
    let now = chrono::Utc::now().timestamp() as u64;

    // Past timestamp should be expired
    assert!(crate::expiry::is_expired(now - 1000));

    // Future timestamp should not be expired
    assert!(!crate::expiry::is_expired(now + 1000));
}

/// Test format_expiry function.
#[test]
fn test_format_expiry_function() {
    // Use a known timestamp
    let date = chrono::NaiveDate::from_ymd_opt(2099, 6, 15).expect("valid date");
    let datetime = date.and_hms_opt(0, 0, 0).expect("valid time");
    let dt = chrono::Utc.from_utc_datetime(&datetime);
    let ts = dt.timestamp() as u64;

    assert_eq!(crate::expiry::format_expiry(ts), "2099-06-15");
}

/// Test AcknowledgmentInfo struct fields.
#[test]
fn test_acknowledgment_info_struct() {
    use crate::types::AcknowledgmentInfo;

    // Test with expiry set
    let ack_with_expiry = AcknowledgmentInfo {
        timestamp: "2026-01-15".to_string(),
        by: "aphoria".to_string(),
        reason: "Integration test".to_string(),
        expires_at: Some("2026-04-15".to_string()),
        expired: false,
    };
    assert!(!ack_with_expiry.expired);
    assert_eq!(ack_with_expiry.expires_at, Some("2026-04-15".to_string()));

    // Test with expired ack
    let expired_ack = AcknowledgmentInfo {
        timestamp: "2025-01-15".to_string(),
        by: "aphoria".to_string(),
        reason: "Old migration".to_string(),
        expires_at: Some("2025-04-15".to_string()),
        expired: true,
    };
    assert!(expired_ack.expired);

    // Test permanent ack (no expiry)
    let permanent_ack = AcknowledgmentInfo {
        timestamp: "2025-01-15".to_string(),
        by: "aphoria".to_string(),
        reason: "Permanent exception".to_string(),
        expires_at: None,
        expired: false,
    };
    assert!(permanent_ack.expires_at.is_none());
    assert!(!permanent_ack.expired);
}

/// Test JSON payload parsing for ack with expiry.
#[test]
fn test_ack_json_payload_parsing() {
    // Simulate the JSON payload format used in acknowledge()
    let expires_at = chrono::Utc::now().timestamp() as u64 + (90 * 24 * 60 * 60);
    let ack_payload = serde_json::json!({
        "reason": "Integration test environment",
        "expires_at": expires_at,
    });

    let payload_str = ack_payload.to_string();

    // Parse it back
    let parsed: serde_json::Value = serde_json::from_str(&payload_str).expect("valid json");

    let reason = parsed.get("reason").and_then(|v| v.as_str()).expect("reason");
    assert_eq!(reason, "Integration test environment");

    let parsed_expiry = parsed.get("expires_at").and_then(|v| v.as_u64()).expect("expires_at");
    assert_eq!(parsed_expiry, expires_at);

    // Test expiry status
    assert!(!crate::expiry::is_expired(parsed_expiry));
}

/// Test JSON payload parsing for expired ack.
#[test]
fn test_expired_ack_json_payload_parsing() {
    // Simulate an expired ack
    let expires_at = chrono::Utc::now().timestamp() as u64 - (24 * 60 * 60); // Yesterday
    let ack_payload = serde_json::json!({
        "reason": "Legacy migration - already ended",
        "expires_at": expires_at,
    });

    let payload_str = ack_payload.to_string();
    let parsed: serde_json::Value = serde_json::from_str(&payload_str).expect("valid json");

    let parsed_expiry = parsed.get("expires_at").and_then(|v| v.as_u64()).expect("expires_at");

    // Should be expired
    assert!(crate::expiry::is_expired(parsed_expiry));
}

/// Test legacy plain-text ack parsing (backwards compatibility).
#[test]
fn test_legacy_ack_payload_parsing() {
    // Legacy format is just plain text reason
    let legacy_reason = "Internal service - legacy format";

    // Try to parse as JSON - should fail
    let parse_result = serde_json::from_str::<serde_json::Value>(legacy_reason);
    assert!(parse_result.is_err(), "Legacy plain text should not parse as JSON");

    // In this case, the code falls back to treating it as the reason with no expiry
    // This is the backwards compatibility behavior
}

/// Test AcknowledgeArgs struct includes expires field.
#[test]
fn test_acknowledge_args_has_expires() {
    let args = AcknowledgeArgs {
        concept_path: "code://rust/test/tls".to_string(),
        reason: "Test reason".to_string(),
        expires: Some("90d".to_string()),
    };

    assert_eq!(args.expires, Some("90d".to_string()));

    let args_no_expiry = AcknowledgeArgs {
        concept_path: "code://rust/test/tls".to_string(),
        reason: "Permanent".to_string(),
        expires: None,
    };

    assert!(args_no_expiry.expires.is_none());
}

/// Test various duration formats.
#[test]
fn test_duration_formats() {
    // 1 day
    assert!(crate::expiry::parse_expiry("1d").is_ok());

    // 7 days (1 week)
    assert!(crate::expiry::parse_expiry("7d").is_ok());

    // 30 days (1 month-ish)
    assert!(crate::expiry::parse_expiry("30d").is_ok());

    // 365 days (1 year)
    assert!(crate::expiry::parse_expiry("365d").is_ok());

    // 0 days should fail
    assert!(crate::expiry::parse_expiry("0d").is_err());

    // Negative should fail (won't parse as u32)
    assert!(crate::expiry::parse_expiry("-5d").is_err());
}

/// Test ISO 8601 date format.
#[test]
fn test_iso_date_format() {
    // Future date should work
    assert!(crate::expiry::parse_expiry("2099-12-31").is_ok());

    // Past date should fail
    assert!(crate::expiry::parse_expiry("2020-01-01").is_err());

    // Invalid format should fail
    assert!(crate::expiry::parse_expiry("12-31-2099").is_err());
    assert!(crate::expiry::parse_expiry("2099/12/31").is_err());
}

use chrono::TimeZone;
