//! Break-Glass Integration Tests
//!
//! Comprehensive tests for break-glass emergency access system

#![allow(clippy::field_reassign_with_default)]

use std::net::{IpAddr, Ipv4Addr};

use chrono::Duration;
use provisioning_orchestrator::break_glass::*;

fn create_test_user(id: &str, team: &str, role: Role) -> User {
    User {
        id: id.to_string(),
        email: format!("{}@example.com", id),
        name: id.to_string(),
        teams: vec![team.to_string()],
        roles: vec![role],
    }
}

#[tokio::test]
#[ignore] // Hangs: service.start() spawns background tasks that don't shut down cleanly
async fn test_complete_break_glass_workflow() {
    // Setup
    let config = BreakGlassConfig::default();
    let service = BreakGlassService::new(config);
    service.start().await.unwrap();

    // 1. Create request
    let requester = create_test_user("requester", "operations", Role::Operator);
    let request = BreakGlassRequest::new(
        requester,
        "Production database outage".to_string(),
        "PostgreSQL cluster unresponsive, need emergency access to restart".to_string(),
        vec!["database/*".to_string()],
        vec![Permission {
            resource: "database".to_string(),
            action: "admin".to_string(),
            scope: Some("production".to_string()),
        }],
        Duration::hours(2),
    );

    let request_id = service
        .approval_manager()
        .submit_request(request)
        .await
        .unwrap();

    // 2. First approval
    let approver1 = create_test_user("security_officer_1", "security", Role::SecurityOfficer);
    let result = service
        .approval_manager()
        .approve_request(
            &request_id,
            approver1,
            "Emergency confirmed, approving access".to_string(),
            IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)),
        )
        .await
        .unwrap();

    assert!(matches!(result, ApprovalResult::PartiallyApproved { .. }));

    // 3. Second approval from different team
    let approver2 = create_test_user("admin_1", "platform", Role::Admin);
    let result = service
        .approval_manager()
        .approve_request(
            &request_id,
            approver2,
            "Verified emergency, granting access".to_string(),
            IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2)),
        )
        .await
        .unwrap();

    assert!(matches!(result, ApprovalResult::Approved { .. }));

    // 4. Create and activate session
    let (approved_request, approvals) = service
        .approval_manager()
        .get_request(&request_id)
        .await
        .unwrap();

    let session_id = service
        .session_manager()
        .create_session(approved_request, approvals)
        .await
        .unwrap();

    let token = service
        .session_manager()
        .activate_session(&session_id)
        .await
        .unwrap();

    assert!(!token.access_token.is_empty());
    assert_eq!(token.session_id, session_id);

    // 5. Verify session is active
    let active_sessions = service.session_manager().list_active_sessions().await;
    assert_eq!(active_sessions.len(), 1);

    // 6. Record action
    let action = EmergencyAction::new(
        "database_restart".to_string(),
        "postgres-prod-01".to_string(),
        serde_json::json!({"force": true, "wait_for_recovery": true}),
    );

    service
        .session_manager()
        .record_action(&session_id, action)
        .await
        .unwrap();

    // 7. Verify action was recorded
    let session = service
        .session_manager()
        .get_session(&session_id)
        .await
        .unwrap();
    assert_eq!(session.actions.len(), 1);

    // 8. Revoke session
    service
        .session_manager()
        .revoke_session(&session_id, "Emergency resolved".to_string())
        .await
        .unwrap();

    // 9. Verify session is revoked
    let session = service
        .session_manager()
        .get_session(&session_id)
        .await
        .unwrap();
    assert_eq!(session.status, SessionStatus::Revoked);

    // Cleanup
    service.shutdown().await.unwrap();
}

#[tokio::test]
async fn test_approval_validation() {
    let config = BreakGlassConfig::default();
    let service = BreakGlassService::new(config);

    // Create request
    let requester = create_test_user("user1", "operations", Role::Operator);
    let request = BreakGlassRequest::new(
        requester.clone(),
        "Test".to_string(),
        "Test".to_string(),
        vec![],
        vec![],
        Duration::hours(1),
    );

    let request_id = service
        .approval_manager()
        .submit_request(request)
        .await
        .unwrap();

    // Test 1: Self-approval should fail
    let result = service
        .approval_manager()
        .approve_request(
            &request_id,
            requester,
            "Self approval".to_string(),
            IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
        )
        .await;

    assert!(result.is_err());

    // Test 2: Insufficient role should fail
    let insufficient_approver = create_test_user("dev1", "development", Role::Developer);
    let result = service
        .approval_manager()
        .approve_request(
            &request_id,
            insufficient_approver,
            "Approved".to_string(),
            IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
        )
        .await;

    assert!(result.is_err());
}

#[tokio::test]
#[ignore] // Hangs due to background task spawned by service.start() not completing
async fn test_auto_revocation_expired() {
    let mut config = BreakGlassConfig::default();
    config.max_session_duration_hours = 0; // Immediate expiration
    config.auto_revoke_config.check_interval_seconds = 1;

    let service = BreakGlassService::new(config);
    service.start().await.unwrap();

    // Create and activate session
    let requester = create_test_user("user", "ops", Role::Operator);
    let mut request = BreakGlassRequest::new(
        requester,
        "Test".to_string(),
        "Test".to_string(),
        vec![],
        vec![],
        Duration::hours(1),
    );
    request.status = RequestStatus::Approved;

    let approver1 = create_test_user("admin1", "security", Role::Admin);
    let approver2 = create_test_user("admin2", "platform", Role::Admin);

    let approval1 = Approval::new(
        approver1,
        "Approved".to_string(),
        IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
    );

    let approval2 = Approval::new(
        approver2,
        "Approved".to_string(),
        IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)),
    );

    let session_id = service
        .session_manager()
        .create_session(request, vec![approval1, approval2])
        .await
        .unwrap();

    service
        .session_manager()
        .activate_session(&session_id)
        .await
        .unwrap();

    // Wait for auto-revocation
    tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;

    // Trigger manual check
    let revoked = service.revocation_monitor().trigger_check().await.unwrap();

    assert!(revoked > 0);

    // Verify session is revoked
    let session = service
        .session_manager()
        .get_session(&session_id)
        .await
        .unwrap();
    assert_eq!(session.status, SessionStatus::Revoked);

    service.shutdown().await.unwrap();
}

#[tokio::test]
async fn test_statistics() {
    let config = BreakGlassConfig::default();
    let service = BreakGlassService::new(config);

    // Create a few requests
    for i in 0..3 {
        let requester = create_test_user(&format!("user{}", i), "ops", Role::Operator);
        let request = BreakGlassRequest::new(
            requester,
            format!("Emergency {}", i),
            format!("Test {}", i),
            vec![],
            vec![],
            Duration::hours(1),
        );

        service
            .approval_manager()
            .submit_request(request)
            .await
            .unwrap();
    }

    // Get statistics
    let approval_stats = service.approval_manager().get_statistics().await;
    assert_eq!(approval_stats.total_requests, 3);
    assert_eq!(approval_stats.pending_requests, 3);

    let session_stats = service.session_manager().get_statistics().await;
    assert_eq!(session_stats.total_sessions, 0);
}

#[tokio::test]
async fn test_denial_workflow() {
    let config = BreakGlassConfig::default();
    let service = BreakGlassService::new(config);

    // Create request
    let requester = create_test_user("user", "ops", Role::Operator);
    let request = BreakGlassRequest::new(
        requester,
        "Test".to_string(),
        "Test".to_string(),
        vec![],
        vec![],
        Duration::hours(1),
    );

    let request_id = service
        .approval_manager()
        .submit_request(request)
        .await
        .unwrap();

    // Deny request
    let denier = create_test_user("admin", "security", Role::Admin);
    service
        .approval_manager()
        .deny_request(&request_id, denier, "Not a valid emergency".to_string())
        .await
        .unwrap();

    // Verify request is denied
    let (request, _) = service
        .approval_manager()
        .get_request(&request_id)
        .await
        .unwrap();

    assert_eq!(request.status, RequestStatus::Denied);

    // Cannot activate denied request
    let result = service
        .session_manager()
        .create_session(request, vec![])
        .await;

    assert!(result.is_err());
}
