use chrono::{DateTime, Utc};
use serde_json::{json, Value};
use tsafe_mobile_core::envelopes::{
    parse_encrypted_qr_chunk_envelope, parse_enrollment_response_envelope,
    parse_enrollment_start_envelope, parse_public_qr_receive_envelope, parse_qr_receive_envelope,
    parse_qr_share_payload_envelope,
};
use tsafe_mobile_core::SafeError;

const ENROLLMENT_START: &[u8] =
    include_bytes!("../../../contracts/mobile/enrollment-start-request.v1.example.json");
const ENROLLMENT_ENVELOPE: &[u8] =
    include_bytes!("../../../contracts/mobile/examples/enrollment-envelope.v1.json");
const ENROLLMENT_RESPONSE: &[u8] =
    include_bytes!("../../../contracts/mobile/enrollment-response.v1.example.json");
const IOS_ENROLLMENT_RESPONSE: &[u8] =
    include_bytes!("../../../contracts/mobile/examples/enrollment-response.v1.json");
const QR_RECEIVE_PUBLIC: &[u8] =
    include_bytes!("../../../contracts/mobile/qr-receive-public-envelope.v1.example.json");
const QR_RECEIVE_ENVELOPE: &[u8] =
    include_bytes!("../../../contracts/mobile/examples/qr-receive-envelope.v1.json");
const QR_SHARE_PAYLOAD: &[u8] =
    include_bytes!("../../../contracts/mobile/examples/qr-share-payload.v1.json");
const QR_ENCRYPTED_CHUNK: &[u8] =
    include_bytes!("../../../contracts/mobile/qr-encrypted-payload-chunk.v1.example.json");

#[test]
fn accepts_mobile_contract_examples() {
    let start = parse_enrollment_start_envelope(ENROLLMENT_START, at("2026-05-24T12:05:00Z"))
        .expect("enrollment start request contract example should parse");
    assert_eq!(start.schema, "tsafe/mobile/enrollment-start-request/v1");
    assert_eq!(start.version, "1");
    assert_eq!(start.session_id, "sess_mobile_demo_20260524_0001");
    assert_eq!(start.profile, "team-mobile-demo");
    assert_eq!(start.repo.owner, "example-org");
    assert_eq!(start.repo.name, "tsafe-mobile-fixture");
    assert_eq!(start.branch.as_deref(), Some("main"));
    assert_eq!(
        start.desktop_public_key,
        "ed25519-pub-demo-mobile-enroll-0001"
    );
    assert!(start
        .requested_capabilities
        .contains(&"vault_ciphertext_pull".to_string()));
    assert_eq!(
        start.vault_identity.as_ref().unwrap().origin_platform,
        "windows"
    );

    let envelope = parse_enrollment_start_envelope(ENROLLMENT_ENVELOPE, at("2026-05-24T12:05:00Z"))
        .expect("enrollment envelope contract example should parse");
    assert_eq!(envelope.schema, "tsafe/mobile/enrollment-envelope/v1");
    assert_eq!(envelope.version, "1.0");
    assert_eq!(envelope.session_id, "7d3e3f6a-6d97-4d6a-89aa-06b6f6f2a7a1");
    assert_eq!(envelope.profile, "team-mobile");
    assert_eq!(envelope.repo.owner, "example-org");
    assert_eq!(envelope.repo.name, "example-vault");
    assert_eq!(envelope.branch.as_deref(), Some("main"));
    assert_eq!(
        envelope.desktop_public_key,
        "DesktopPublicKeyExample001234567890abcd"
    );
    assert_eq!(
        envelope.vault_identity.as_ref().unwrap().vault_id,
        "tsafe-vault-macos-team-mobile-001"
    );

    let response =
        parse_enrollment_response_envelope(ENROLLMENT_RESPONSE, at("2026-05-24T12:05:00Z"))
            .expect("enrollment response contract example should parse");
    assert_eq!(response.schema, "tsafe/mobile/enrollment-response/v1");
    assert_eq!(response.version, "1");
    assert_eq!(response.session_id, "sess_mobile_demo_20260524_0001");
    assert_eq!(response.profile, "team-mobile-demo");
    assert_eq!(response.repo.owner, "example-org");
    assert_eq!(
        response.mobile_recipient,
        "age1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"
    );
    assert_eq!(response.device_label.as_deref(), Some("Demo Android"));
    assert_eq!(
        response.vault_identity.as_ref().unwrap().origin_platform,
        "linux"
    );

    let ios_response =
        parse_enrollment_response_envelope(IOS_ENROLLMENT_RESPONSE, at("2026-05-24T12:05:00Z"))
            .expect("iOS app-shell enrollment response example should parse");
    assert_eq!(ios_response.schema, "tsafe/mobile/enrollment-response/v1");
    assert_eq!(ios_response.version, "1.0");
    assert_eq!(
        ios_response.session_id,
        "7d3e3f6a-6d97-4d6a-89aa-06b6f6f2a7a1"
    );
    assert_eq!(ios_response.profile, "team-mobile");
    assert_eq!(ios_response.repo.owner, "example-org");
    assert_eq!(ios_response.repo.name, "example-vault");
    assert_eq!(
        ios_response.mobile_recipient,
        "age1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"
    );
    assert_eq!(
        ios_response.device_label.as_deref(),
        Some("Example mobile device")
    );
    assert_eq!(
        ios_response.vault_identity.as_ref().unwrap().vault_id,
        "tsafe-vault-macos-team-mobile-001"
    );

    let receive = parse_public_qr_receive_envelope(QR_RECEIVE_PUBLIC, at("2026-05-24T12:16:00Z"))
        .expect("QR receive public contract example should parse");
    assert_eq!(receive.schema, "tsafe/mobile/qr-receive-public-envelope/v1");
    assert_eq!(receive.version, "1");
    assert_eq!(receive.session_id, "sess_qr_demo_20260524_0001");
    assert_eq!(receive.profile, "team-mobile-demo");
    assert_eq!(receive.public_key_id, "qrkey_demo_receiver_0001");
    assert_eq!(
        receive.recipient_public_key,
        "age1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"
    );
    assert!(receive
        .requested_capabilities
        .contains(&"encrypted_qr_receive".to_string()));

    let receive_envelope =
        parse_qr_receive_envelope(QR_RECEIVE_ENVELOPE, at("2026-05-24T12:21:00Z"))
            .expect("QR receive envelope contract example should parse");
    assert_eq!(
        receive_envelope.schema,
        "tsafe/mobile/qr-receive-envelope/v1"
    );
    assert_eq!(receive_envelope.version, "1.0");
    assert_eq!(
        receive_envelope.session_id,
        "46a53da7-a80e-4456-b765-348cf18843d4"
    );
    assert_eq!(receive_envelope.challenge, "receiveChallengeExample001");
    assert_eq!(receive_envelope.recipient_key_id, "recv-key-example-001");
    assert_eq!(receive_envelope.frame_count, 1);
    assert_eq!(receive_envelope.frame_index, 0);
    assert_eq!(
        receive_envelope.digest,
        "sha256:2222222222222222222222222222222222222222222222222222222222222222"
    );

    let share_payload =
        parse_qr_share_payload_envelope(QR_SHARE_PAYLOAD, at("2026-05-24T12:22:00Z"))
            .expect("QR share payload contract example should parse");
    assert_eq!(share_payload.schema, "tsafe/mobile/qr-share-payload/v1");
    assert_eq!(share_payload.version, "1.0");
    assert_eq!(
        share_payload.session_id,
        "46a53da7-a80e-4456-b765-348cf18843d4"
    );
    assert_eq!(share_payload.recipient_key_id, "recv-key-example-001");
    assert_eq!(share_payload.kem, "x25519-hkdf-sha256");
    assert_eq!(share_payload.aead, "xchacha20-poly1305");
    assert_eq!(
        share_payload.key_name_suggestion.as_deref(),
        Some("shared-login")
    );
    assert_eq!(share_payload.frame_count, 1);
    assert_eq!(share_payload.frame_index, 0);

    let chunk = parse_encrypted_qr_chunk_envelope(QR_ENCRYPTED_CHUNK, at("2026-05-24T12:17:00Z"))
        .expect("encrypted QR chunk contract example should parse");
    assert_eq!(chunk.schema, "tsafe/mobile/qr-encrypted-payload-chunk/v1");
    assert_eq!(chunk.version, "1");
    assert_eq!(chunk.session_id, "sess_qr_demo_20260524_0001");
    assert_eq!(chunk.profile, "team-mobile-demo");
    assert_eq!(chunk.nonce, "cXItcmVjZWl2ZS1jaGFsbGVuZ2UtMDAx");
    assert_eq!(chunk.frame_count, 2);
    assert_eq!(chunk.frame_index, 0);
    assert_eq!(
        chunk.payload_digest,
        "sha256:6666666666666666666666666666666666666666666666666666666666666666"
    );
    assert_eq!(
        chunk.chunk,
        "QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB"
    );
}

#[test]
fn rejects_expired_contract_example() {
    let mut value: Value = serde_json::from_slice(ENROLLMENT_START).unwrap();
    value["expires_at"] = json!("2026-05-24T12:04:59Z");

    assert_invalid(
        parse_enrollment_start_envelope(
            serde_json::to_vec(&value).unwrap(),
            at("2026-05-24T12:05:00Z"),
        ),
        "expired",
    );
}

fn at(value: &str) -> DateTime<Utc> {
    DateTime::parse_from_rfc3339(value)
        .unwrap()
        .with_timezone(&Utc)
}

fn assert_invalid<T>(result: Result<T, SafeError>, expected: &str) {
    match result {
        Err(SafeError::InvalidVault { reason }) => {
            assert!(
                reason.contains(expected),
                "expected reason to contain {expected:?}, got {reason:?}"
            );
        }
        Err(other) => panic!("expected InvalidVault, got {other:?}"),
        Ok(_) => panic!("expected validation failure"),
    }
}
