//! Tests for governance workflows.

use tempfile::TempDir;
use uuid::Uuid;

use crate::config::GovernanceConfig;
use crate::evidence::{EvidenceLevel, EvidenceSource, PatternEvidence};
use crate::governance::{
    ApprovalDecision, ApprovalRequest, ApprovalStage, ApprovalStatus, ApprovalWorkflow, AuditEvent,
    AuditEventType, AuditTrail, Decision, GovernanceStateMachine, GovernanceStore,
};
use crate::learning::{ClaimTemplate, LearnedPattern, ValueType};
use crate::types::Language;

/// Create a test governance config with a simple workflow.
fn create_test_config() -> GovernanceConfig {
    GovernanceConfig {
        enabled: true,
        governance_dir: None,
        default_workflow: Some("test_workflow".to_string()),
        workflows: vec![ApprovalWorkflow::new("test_workflow", "Test workflow")
            .add_stage(ApprovalStage::new("security_review", "Security Review"))
            .add_stage(ApprovalStage::new("arch_review", "Architecture Review"))],
        check_timeouts_on_access: false,
    }
}

/// Create a test pattern for governance tests.
fn create_test_pattern() -> LearnedPattern {
    LearnedPattern::new(
        "verify_ssl = false",
        "verify_ssl = <boolean>",
        ClaimTemplate::new("ssl/verify", "enabled", ValueType::Boolean, "SSL verification"),
        Language::Python,
        "project_hash",
        0.9,
    )
}

/// Create a test pattern with evidence.
fn create_pattern_with_evidence(evidence_level: EvidenceLevel) -> LearnedPattern {
    let mut pattern = create_test_pattern();

    let source = match evidence_level {
        EvidenceLevel::ProductSpec => {
            EvidenceSource::Spec { path: "spec.md".into(), requirement_id: Some("REQ-001".into()) }
        }
        EvidenceLevel::Standard => EvidenceSource::Rfc { number: 7519, section: None },
        EvidenceLevel::Research => EvidenceSource::Adr { id: "042".into(), path: None },
        EvidenceLevel::Commit => {
            EvidenceSource::Commit { hash: "abc123".into(), message_excerpt: None }
        }
    };

    pattern.evidence = PatternEvidence::from_sources(vec![source]);
    pattern
}

// ==================== ApprovalRequest Tests ====================

#[test]
fn test_approval_request_creation() {
    let pattern_id = Uuid::new_v4();
    let request = ApprovalRequest::new(
        pattern_id,
        "test_pattern",
        "standard_review",
        "security_review",
        "test_user",
    );

    assert_eq!(request.pattern_id, pattern_id);
    assert_eq!(request.pattern_name, "test_pattern");
    assert_eq!(request.workflow_name, "standard_review");
    assert!(request.status.is_pending());
    assert_eq!(request.status.current_stage(), Some("security_review"));
    assert_eq!(request.current_stage_index, 0);
    assert!(request.decisions.is_empty());
}

#[test]
fn test_approval_request_advance_stage() {
    let mut request =
        ApprovalRequest::new(Uuid::new_v4(), "test_pattern", "workflow", "stage1", "user");

    request.advance_to_stage("stage2");

    assert_eq!(request.current_stage_index, 1);
    assert_eq!(request.status.current_stage(), Some("stage2"));
    assert!(request.updated_at > request.created_at);
}

#[test]
fn test_approval_request_mark_approved() {
    let mut request =
        ApprovalRequest::new(Uuid::new_v4(), "test_pattern", "workflow", "stage1", "user");

    request.mark_approved();

    assert!(request.status.is_approved());
    assert!(request.status.is_terminal());
}

#[test]
fn test_approval_request_mark_rejected() {
    let mut request =
        ApprovalRequest::new(Uuid::new_v4(), "test_pattern", "workflow", "stage1", "user");

    request.mark_rejected("stage1", "Too broad");

    assert!(request.status.is_rejected());
    if let ApprovalStatus::Rejected { stage, reason } = &request.status {
        assert_eq!(stage, "stage1");
        assert_eq!(reason, "Too broad");
    } else {
        panic!("Expected Rejected status");
    }
}

#[test]
fn test_approval_request_add_decision() {
    let mut request =
        ApprovalRequest::new(Uuid::new_v4(), "test_pattern", "workflow", "stage1", "user");

    let decision = ApprovalDecision::new(
        request.id,
        "stage1",
        Decision::Approved,
        "approver",
        Some("LGTM".to_string()),
    );

    request.add_decision(decision);

    assert_eq!(request.decisions.len(), 1);
    assert_eq!(request.decisions[0].approver, "approver");
}

#[test]
fn test_approval_request_current_stage_approval_count() {
    let mut request =
        ApprovalRequest::new(Uuid::new_v4(), "test_pattern", "workflow", "stage1", "user");

    // Add approvals for current stage
    request.add_decision(ApprovalDecision::new(
        request.id,
        "stage1",
        Decision::Approved,
        "approver1",
        None,
    ));
    request.add_decision(ApprovalDecision::new(
        request.id,
        "stage1",
        Decision::Approved,
        "approver2",
        None,
    ));

    assert_eq!(request.current_stage_approval_count(), 2);

    // Advance to next stage - count should reset
    request.advance_to_stage("stage2");
    assert_eq!(request.current_stage_approval_count(), 0);
}

// ==================== ApprovalWorkflow Tests ====================

#[test]
fn test_workflow_stage_navigation() {
    let workflow = ApprovalWorkflow::new("test", "Test")
        .add_stage(ApprovalStage::new("s1", "Stage 1"))
        .add_stage(ApprovalStage::new("s2", "Stage 2"))
        .add_stage(ApprovalStage::new("s3", "Stage 3"));

    assert_eq!(workflow.first_stage().unwrap().name, "s1");
    assert_eq!(workflow.next_stage(0).unwrap().name, "s2");
    assert_eq!(workflow.next_stage(1).unwrap().name, "s3");
    assert!(workflow.next_stage(2).is_none());

    assert!(!workflow.is_last_stage(0));
    assert!(!workflow.is_last_stage(1));
    assert!(workflow.is_last_stage(2));
}

#[test]
fn test_workflow_get_stage_by_name() {
    let workflow = ApprovalWorkflow::new("test", "Test")
        .add_stage(ApprovalStage::new("security", "Security"))
        .add_stage(ApprovalStage::new("arch", "Architecture"));

    let (index, stage) = workflow.get_stage_by_name("arch").unwrap();
    assert_eq!(index, 1);
    assert_eq!(stage.name, "arch");

    assert!(workflow.get_stage_by_name("nonexistent").is_none());
}

#[test]
fn test_workflow_auto_approve() {
    let workflow = ApprovalWorkflow::new("test", "Test").add_stage(
        ApprovalStage::new("review", "Review").with_auto_approve_level(EvidenceLevel::Standard),
    );

    assert!(workflow.should_auto_approve(0, EvidenceLevel::Standard));
    assert!(workflow.should_auto_approve(0, EvidenceLevel::ProductSpec));
    assert!(!workflow.should_auto_approve(0, EvidenceLevel::Research));
    assert!(!workflow.should_auto_approve(0, EvidenceLevel::Commit));
}

#[test]
fn test_workflow_validation() {
    // Empty stages - should fail
    let workflow = ApprovalWorkflow { name: "test".into(), stages: vec![], ..Default::default() };
    assert!(workflow.validate().is_err());

    // Empty name - should fail
    let workflow = ApprovalWorkflow::new("", "").add_stage(ApprovalStage::new("s1", "S1"));
    assert!(workflow.validate().is_err());

    // Invalid escalation target - should fail
    let workflow = ApprovalWorkflow::new("test", "Test")
        .add_stage(ApprovalStage::new("s1", "S1").with_escalation("nonexistent"));
    assert!(workflow.validate().is_err());

    // Valid workflow
    let workflow = ApprovalWorkflow::new("test", "Test").add_stage(ApprovalStage::new("s1", "S1"));
    assert!(workflow.validate().is_ok());
}

// ==================== GovernanceStore Tests ====================

#[test]
fn test_store_save_and_get_request() {
    let temp = TempDir::new().expect("temp dir");
    let store = GovernanceStore::new(temp.path()).expect("create store");

    let request =
        ApprovalRequest::new(Uuid::new_v4(), "test_pattern", "workflow", "stage1", "user");

    store.save_request(&request).expect("save");

    let loaded = store.get_request(&request.id).expect("get").unwrap();
    assert_eq!(loaded.id, request.id);
    assert_eq!(loaded.pattern_name, "test_pattern");
}

#[test]
fn test_store_update_request() {
    let temp = TempDir::new().expect("temp dir");
    let store = GovernanceStore::new(temp.path()).expect("create store");

    let mut request =
        ApprovalRequest::new(Uuid::new_v4(), "test_pattern", "workflow", "stage1", "user");

    store.save_request(&request).expect("save initial");

    // Update the request
    request.advance_to_stage("stage2");
    store.save_request(&request).expect("save updated");

    // Verify update
    let loaded = store.get_request(&request.id).expect("get").unwrap();
    assert_eq!(loaded.current_stage_index, 1);
    assert_eq!(loaded.status.current_stage(), Some("stage2"));

    // Verify only one request exists
    let all = store.list_all().expect("list all");
    assert_eq!(all.len(), 1);
}

#[test]
fn test_store_list_pending() {
    let temp = TempDir::new().expect("temp dir");
    let store = GovernanceStore::new(temp.path()).expect("create store");

    // Create pending request
    let pending = ApprovalRequest::new(Uuid::new_v4(), "pending", "workflow", "stage1", "user");
    store.save_request(&pending).expect("save pending");

    // Create approved request
    let mut approved =
        ApprovalRequest::new(Uuid::new_v4(), "approved", "workflow", "stage1", "user");
    approved.mark_approved();
    store.save_request(&approved).expect("save approved");

    let pending_list = store.list_pending().expect("list pending");
    assert_eq!(pending_list.len(), 1);
    assert_eq!(pending_list[0].pattern_name, "pending");
}

#[test]
fn test_store_log_and_get_decisions() {
    let temp = TempDir::new().expect("temp dir");
    let store = GovernanceStore::new(temp.path()).expect("create store");

    let request_id = Uuid::new_v4();
    let decision = ApprovalDecision::new(
        request_id,
        "security_review",
        Decision::Approved,
        "alice",
        Some("LGTM".to_string()),
    );

    store.log_decision(&decision).expect("log");

    let decisions = store.get_decisions(&request_id).expect("get");
    assert_eq!(decisions.len(), 1);
    assert_eq!(decisions[0].approver, "alice");
    assert_eq!(decisions[0].comment, Some("LGTM".to_string()));
}

#[test]
fn test_store_get_request_by_pattern() {
    let temp = TempDir::new().expect("temp dir");
    let store = GovernanceStore::new(temp.path()).expect("create store");

    let pattern_id = Uuid::new_v4();
    let request = ApprovalRequest::new(pattern_id, "test", "workflow", "stage1", "user");

    store.save_request(&request).expect("save");

    let loaded = store.get_request_by_pattern(&pattern_id).expect("get").unwrap();
    assert_eq!(loaded.pattern_id, pattern_id);
}

// ==================== GovernanceStateMachine Tests ====================

#[test]
fn test_state_machine_create_request() {
    let temp = TempDir::new().expect("temp dir");
    let store = GovernanceStore::new(temp.path()).expect("create store");
    let config = create_test_config();
    let sm = GovernanceStateMachine::new(store, config);

    let pattern = create_test_pattern();
    let workflow = sm.config.workflows.first().unwrap();

    let request = sm.create_request(&pattern, workflow, "test_user").expect("create");

    assert_eq!(request.pattern_id, pattern.id);
    assert_eq!(request.workflow_name, "test_workflow");
    assert!(request.status.is_pending());
    assert_eq!(request.status.current_stage(), Some("security_review"));
}

#[test]
fn test_state_machine_approve_single_stage() {
    let temp = TempDir::new().expect("temp dir");
    let store = GovernanceStore::new(temp.path()).expect("create store");
    let config = create_test_config();
    let sm = GovernanceStateMachine::new(store, config);

    let pattern = create_test_pattern();
    let workflow = sm.config.workflows.first().unwrap();
    let request = sm.create_request(&pattern, workflow, "test_user").expect("create");

    // Approve first stage
    let updated = sm.approve(request.id, "approver", Some("LGTM".to_string())).expect("approve");

    // Should advance to second stage
    assert!(updated.status.is_pending());
    assert_eq!(updated.status.current_stage(), Some("arch_review"));
    assert_eq!(updated.decisions.len(), 1);
}

#[test]
fn test_state_machine_full_approval() {
    let temp = TempDir::new().expect("temp dir");
    let store = GovernanceStore::new(temp.path()).expect("create store");
    let config = create_test_config();
    let sm = GovernanceStateMachine::new(store, config);

    let pattern = create_test_pattern();
    let workflow = sm.config.workflows.first().unwrap();
    let request = sm.create_request(&pattern, workflow, "test_user").expect("create");

    // Approve first stage
    let updated = sm.approve(request.id, "approver1", None).expect("approve 1");
    assert_eq!(updated.status.current_stage(), Some("arch_review"));

    // Approve second stage
    let final_req = sm.approve(request.id, "approver2", None).expect("approve 2");
    assert!(final_req.status.is_approved());
}

#[test]
fn test_state_machine_reject() {
    let temp = TempDir::new().expect("temp dir");
    let store = GovernanceStore::new(temp.path()).expect("create store");
    let config = create_test_config();
    let sm = GovernanceStateMachine::new(store, config);

    let pattern = create_test_pattern();
    let workflow = sm.config.workflows.first().unwrap();
    let request = sm.create_request(&pattern, workflow, "test_user").expect("create");

    let rejected = sm.reject(request.id, "reviewer", "Too broad".to_string()).expect("reject");

    assert!(rejected.status.is_rejected());
    if let ApprovalStatus::Rejected { stage, reason } = &rejected.status {
        assert_eq!(stage, "security_review");
        assert_eq!(reason, "Too broad");
    }
}

#[test]
fn test_state_machine_list_pending() {
    let temp = TempDir::new().expect("temp dir");
    let store = GovernanceStore::new(temp.path()).expect("create store");
    let config = create_test_config();
    let sm = GovernanceStateMachine::new(store, config);

    let pattern = create_test_pattern();
    let workflow = sm.config.workflows.first().unwrap();
    sm.create_request(&pattern, workflow, "test_user").expect("create");

    let pending = sm.list_pending().expect("list");
    assert_eq!(pending.len(), 1);
}

// ==================== AuditTrail Tests ====================

#[test]
fn test_audit_log_and_retrieve() {
    let temp = TempDir::new().expect("temp dir");
    let trail = AuditTrail::new(temp.path()).expect("create trail");

    let event = AuditEvent::new(
        AuditEventType::RequestCreated,
        Uuid::new_v4(),
        Uuid::new_v4(),
        "test_user".to_string(),
        serde_json::json!({"workflow": "test"}),
    );

    trail.log_event(event.clone()).expect("log");

    let events = trail.get_all_events().expect("get all");
    assert_eq!(events.len(), 1);
    assert_eq!(events[0].id, event.id);
}

#[test]
fn test_audit_pattern_timeline() {
    let temp = TempDir::new().expect("temp dir");
    let trail = AuditTrail::new(temp.path()).expect("create trail");

    let pattern_id = Uuid::new_v4();
    let other_pattern = Uuid::new_v4();

    // Log events for our pattern
    trail
        .log_event(AuditEvent::new(
            AuditEventType::RequestCreated,
            Uuid::new_v4(),
            pattern_id,
            "user".to_string(),
            serde_json::json!({}),
        ))
        .expect("log 1");

    trail
        .log_event(AuditEvent::new(
            AuditEventType::StageApproved,
            Uuid::new_v4(),
            pattern_id,
            "user".to_string(),
            serde_json::json!({}),
        ))
        .expect("log 2");

    // Log event for other pattern
    trail
        .log_event(AuditEvent::new(
            AuditEventType::RequestCreated,
            Uuid::new_v4(),
            other_pattern,
            "user".to_string(),
            serde_json::json!({}),
        ))
        .expect("log 3");

    let timeline = trail.get_pattern_timeline(&pattern_id).expect("get timeline");
    assert_eq!(timeline.len(), 2);
}

// ==================== GovernanceConfig Tests ====================

#[test]
fn test_governance_config_validation() {
    // Enabled with no workflows - should fail
    let config = GovernanceConfig { enabled: true, ..Default::default() };
    assert!(config.validate().is_err());

    // Enabled with valid workflow - should pass
    let config = create_test_config();
    assert!(config.validate().is_ok());

    // Invalid default workflow - should fail
    let config = GovernanceConfig {
        enabled: true,
        default_workflow: Some("nonexistent".to_string()),
        workflows: vec![
            ApprovalWorkflow::new("other", "Other").add_stage(ApprovalStage::new("s1", "S1"))
        ],
        ..Default::default()
    };
    assert!(config.validate().is_err());
}

#[test]
fn test_governance_config_get_workflow() {
    let config = create_test_config();

    assert!(config.get_workflow("test_workflow").is_some());
    assert!(config.get_workflow("nonexistent").is_none());
    assert!(config.get_default_workflow().is_some());
}

// ==================== Integration Tests ====================

#[test]
fn test_full_governance_workflow() {
    let temp = TempDir::new().expect("temp dir");
    let store = GovernanceStore::new(temp.path()).expect("create store");
    let config = create_test_config();
    let sm = GovernanceStateMachine::new(store, config);

    // Create pattern and request
    let pattern = create_test_pattern();
    let workflow = sm.config.workflows.first().unwrap();
    let request = sm.create_request(&pattern, workflow, "developer").expect("create");

    // Verify initial state
    assert!(request.status.is_pending());
    assert_eq!(request.status.current_stage(), Some("security_review"));

    // Security review approves
    let after_security = sm
        .approve(request.id, "security_lead", Some("Looks secure".to_string()))
        .expect("approve security");
    assert_eq!(after_security.status.current_stage(), Some("arch_review"));

    // Architecture review approves
    let final_req = sm
        .approve(request.id, "architect", Some("Good pattern".to_string()))
        .expect("approve arch");
    assert!(final_req.status.is_approved());
    assert_eq!(final_req.decisions.len(), 2);

    // Verify persisted state
    let loaded = sm.get_request(&request.id).expect("get").unwrap();
    assert!(loaded.status.is_approved());
}

#[test]
fn test_auto_approve_with_evidence() {
    let temp = TempDir::new().expect("temp dir");
    let store = GovernanceStore::new(temp.path()).expect("create store");

    // Create config with auto-approve at Standard evidence level
    let config = GovernanceConfig {
        enabled: true,
        default_workflow: Some("fast_track".to_string()),
        workflows: vec![ApprovalWorkflow::new("fast_track", "Fast track").add_stage(
            ApprovalStage::new("review", "Review").with_auto_approve_level(EvidenceLevel::Standard),
        )],
        ..Default::default()
    };

    let sm = GovernanceStateMachine::new(store, config);

    // Create pattern with Standard evidence
    let pattern = create_pattern_with_evidence(EvidenceLevel::Standard);
    let workflow = sm.config.workflows.first().unwrap();

    // Request should auto-approve at creation
    let request = sm.create_request(&pattern, workflow, "developer").expect("create");
    assert!(request.status.is_approved());
}
