//! Integration tests: drive the `ApprovalStore` workflow engine and assert
//! stage-rule evaluation, multi-stage advancement, rejection, change requests,
//! delegation, quorum, and audit.

use mcp_approval::store::ApprovalStore;
use mcp_approval::types::*;

fn store() -> ApprovalStore {
    ApprovalStore::new()
}

fn new_request(s: &ApprovalStore, rt: RequestType) -> ApprovalRequest {
    s.create_request(
        Domain::Legal, rt, "t".into(), "d".into(), "req".into(), "subj:1".into(),
        Priority::Medium, None, Some(72),
    ).unwrap()
}

#[test]
fn default_templates_seeded() {
    let s = store();
    // One per request type in the two domains (8 seeded).
    assert!(s.list_templates(None).len() >= 8);
    assert!(s.get_template("WF-CONTRACT").is_some());
}

#[test]
fn contract_review_advances_through_three_stages() {
    let s = store();
    let r = new_request(&s, RequestType::ContractReview);
    let r = s.submit(&r.id, "req").unwrap();
    assert_eq!(r.status, RequestStatus::InReview);
    assert_eq!(r.current_stage, Some(0));

    // Stage 0: Legal (all_of, single approver) → advances to stage 1.
    let r = s.decide(&r.id, "legal.counsel", Decision::Approve, None).unwrap();
    assert_eq!(r.current_stage, Some(1));
    assert_eq!(r.stages[0].status, StageStatus::Approved);

    // Stage 1: Finance (any_of) → advances to stage 2.
    let r = s.decide(&r.id, "finance.controller", Decision::Approve, None).unwrap();
    assert_eq!(r.current_stage, Some(2));

    // Stage 2: Exec (any_of) → fully approved.
    let r = s.decide(&r.id, "exec.ceo", Decision::Approve, None).unwrap();
    assert_eq!(r.status, RequestStatus::Approved);
    assert!(r.decided_at.is_some());
}

#[test]
fn reject_at_any_stage_rejects_request() {
    let s = store();
    let r = new_request(&s, RequestType::ContractReview);
    let r = s.submit(&r.id, "req").unwrap();
    let r = s.decide(&r.id, "legal.counsel", Decision::Reject, Some("unacceptable indemnity".into())).unwrap();
    assert_eq!(r.status, RequestStatus::Rejected);
    assert_eq!(r.stages[0].status, StageStatus::Rejected);
}

#[test]
fn request_changes_bounces_back() {
    let s = store();
    let r = new_request(&s, RequestType::ContractReview);
    let r = s.submit(&r.id, "req").unwrap();
    let r = s.decide(&r.id, "legal.counsel", Decision::RequestChanges, Some("fix clause 7".into())).unwrap();
    assert_eq!(r.status, RequestStatus::ChangesRequested);
    // Requester can resubmit; stages reset.
    let r = s.submit(&r.id, "req").unwrap();
    assert_eq!(r.status, RequestStatus::InReview);
    assert_eq!(r.current_stage, Some(0));
}

#[test]
fn board_consent_requires_quorum() {
    let s = store();
    let r = s.create_request(Domain::Legal, RequestType::BoardConsent, "Consent".into(), "d".into(), "secretary".into(), "consent:1".into(), Priority::High, None, Some(168)).unwrap();
    let r = s.submit(&r.id, "secretary").unwrap();
    // Quorum is 3.
    let r = s.decide(&r.id, "dir.1", Decision::Approve, None).unwrap();
    assert_eq!(r.status, RequestStatus::InReview, "1 of 3 — not yet");
    let r = s.decide(&r.id, "dir.2", Decision::Approve, None).unwrap();
    assert_eq!(r.status, RequestStatus::InReview, "2 of 3 — not yet");
    let r = s.decide(&r.id, "dir.3", Decision::Approve, None).unwrap();
    assert_eq!(r.status, RequestStatus::Approved, "3 of 3 — quorum met");
}

#[test]
fn double_vote_rejected() {
    let s = store();
    let r = new_request(&s, RequestType::ContractReview);
    let r = s.submit(&r.id, "req").unwrap();
    s.decide(&r.id, "legal.counsel", Decision::Approve, None).unwrap();
    // legal.counsel already moved the request to stage 1; voting again there is ineligible.
    let err = s.decide(&r.id, "legal.counsel", Decision::Approve, None);
    assert!(err.is_err());
}

#[test]
fn ineligible_approver_rejected() {
    let s = store();
    let r = new_request(&s, RequestType::ContractReview);
    let r = s.submit(&r.id, "req").unwrap();
    let err = s.decide(&r.id, "random.person", Decision::Approve, None);
    assert!(err.is_err(), "non-approver must be rejected");
}

#[test]
fn delegation_allows_delegate_to_decide() {
    let s = store();
    let r = new_request(&s, RequestType::ContractReview);
    let r = s.submit(&r.id, "req").unwrap();
    // legal.counsel delegates to legal.associate
    s.delegate(&r.id, "legal.counsel", "legal.associate", Some("OOO".into())).unwrap();
    let r = s.decide(&r.id, "legal.associate", Decision::Approve, None).unwrap();
    // Vote recorded on behalf of legal.counsel; stage advances.
    assert_eq!(r.current_stage, Some(1));
    assert_eq!(r.stages[0].votes[0].on_behalf_of.as_deref(), Some("legal.counsel"));
}

#[test]
fn my_queue_lists_pending_for_approver() {
    let s = store();
    let r = new_request(&s, RequestType::ContractReview);
    s.submit(&r.id, "req").unwrap();
    let q = s.pending_for_approver("legal.counsel");
    assert!(q.iter().any(|x| x.id == r.id));
    // Finance isn't active yet.
    assert!(!s.pending_for_approver("finance.cfo").iter().any(|x| x.id == r.id));
}

#[test]
fn cancel_then_no_decisions() {
    let s = store();
    let r = new_request(&s, RequestType::ContractReview);
    let r = s.submit(&r.id, "req").unwrap();
    let r = s.cancel(&r.id, "req", "no longer needed").unwrap();
    assert_eq!(r.status, RequestStatus::Cancelled);
    assert!(s.decide(&r.id, "legal.counsel", Decision::Approve, None).is_err());
}

#[test]
fn audit_trail_accumulates() {
    let s = store();
    let r = new_request(&s, RequestType::ContractReview);
    s.submit(&r.id, "req").unwrap();
    s.add_comment(&r.id, "req", "FYI").unwrap();
    s.decide(&r.id, "legal.counsel", Decision::Approve, None).unwrap();
    let trail = s.get_audit_trail(&r.id).unwrap();
    let actions: Vec<&str> = trail.iter().map(|e| e.action.as_str()).collect();
    assert!(actions.contains(&"created"));
    assert!(actions.contains(&"submitted"));
    assert!(actions.contains(&"decision"));
    assert!(actions.contains(&"stage_approved"));
}
