use super::*;
use crate::ntlm::messages::test::*;
use crate::ntlm::*;

#[test]
fn write_negotiate_writes_correct_signature() {
    let mut context = Ntlm::new();
    context.set_version(LOCAL_NEGOTIATE_VERSION);
    context.state = NtlmState::Negotiate;

    let mut buff = Vec::new();
    write_negotiate(&mut context, &mut buff).unwrap();

    assert_eq!(NTLM_SIGNATURE, buff[SIGNATURE_START..MESSAGE_TYPE_START]);
}

#[test]
fn write_negotiate_writes_correct_message_type() {
    let mut context = Ntlm::new();
    context.set_version(LOCAL_NEGOTIATE_VERSION);
    context.state = NtlmState::Negotiate;

    let mut buff = Vec::new();
    write_negotiate(&mut context, &mut buff).unwrap();

    assert_eq!(NEGOTIATE_MESSAGE_TYPE, buff[MESSAGE_TYPE_START..NEGOTIATE_FLAGS_START]);
}

#[test]
fn write_negotiate_writes_flags() {
    let mut context = Ntlm::new();
    context.set_version(LOCAL_NEGOTIATE_VERSION);
    context.state = NtlmState::Negotiate;

    let mut buff = Vec::new();
    write_negotiate(&mut context, &mut buff).unwrap();

    assert_eq!(
        LOCAL_NEGOTIATE_FLAGS.to_le_bytes(),
        buff[NEGOTIATE_FLAGS_START..NEGOTIATE_DOMAIN_NAME_START]
    );
    assert_eq!(NegotiateFlags::from_bits(LOCAL_NEGOTIATE_FLAGS).unwrap(), context.flags);
}

#[test]
fn write_negotiate_writes_domain_name() {
    let mut context = Ntlm::new();
    context.set_version(LOCAL_NEGOTIATE_VERSION);
    context.state = NtlmState::Negotiate;

    let mut buff = Vec::new();
    write_negotiate(&mut context, &mut buff).unwrap();

    assert_eq!(
        LOCAL_NEGOTIATE_DOMAIN,
        buff[NEGOTIATE_DOMAIN_NAME_START..NEGOTIATE_WORKSTATION_START]
    );
}

#[test]
fn write_negotiate_writes_workstation() {
    let mut context = Ntlm::new();
    context.set_version(LOCAL_NEGOTIATE_VERSION);
    context.state = NtlmState::Negotiate;

    let mut buff = Vec::new();
    write_negotiate(&mut context, &mut buff).unwrap();

    assert_eq!(
        LOCAL_NEGOTIATE_WORKSTATION,
        buff[NEGOTIATE_WORKSTATION_START..NEGOTIATE_VERSION_START]
    );
}

#[test]
fn write_negotiate_writes_version() {
    let mut context = Ntlm::new();
    context.set_version(LOCAL_NEGOTIATE_VERSION);
    context.state = NtlmState::Negotiate;

    let mut buff = Vec::new();
    write_negotiate(&mut context, &mut buff).unwrap();

    assert_eq!(
        LOCAL_NEGOTIATE_VERSION,
        buff[NEGOTIATE_VERSION_START..NEGOTIATE_VERSION_START + NTLM_VERSION_SIZE]
    );
}

#[test]
fn write_negotiate_writes_buffer_to_context() {
    let mut context = Ntlm::new();
    context.set_version(LOCAL_NEGOTIATE_VERSION);
    context.state = NtlmState::Negotiate;

    let mut buff = Vec::new();
    write_negotiate(&mut context, &mut buff).unwrap();

    assert_eq!((*LOCAL_NEGOTIATE_MESSAGE).as_ref(), buff.as_slice());
}

#[test]
fn write_negotiate_changes_context_state_on_success() {
    let mut context = Ntlm::new();
    context.set_version(LOCAL_NEGOTIATE_VERSION);
    context.state = NtlmState::Negotiate;

    let expected_state = NtlmState::Challenge;

    let mut buff = Vec::new();
    write_negotiate(&mut context, &mut buff).unwrap();

    assert_eq!(expected_state, context.state);
}

#[test]
fn write_negotiate_failed_on_incorrect_state() {
    let mut context = Ntlm::new();
    context.set_version(LOCAL_NEGOTIATE_VERSION);
    context.state = NtlmState::Challenge;

    let mut buff = Vec::new();
    assert!(write_negotiate(&mut context, &mut buff).is_err());
}

#[test]
fn read_challenge_does_not_fail_with_correct_header() {
    let mut context = Ntlm::new();
    context.set_version(LOCAL_NEGOTIATE_VERSION);
    context.state = NtlmState::Challenge;
    context.negotiate_message = Some(NegotiateMessage::new(LOCAL_NEGOTIATE_MESSAGE.to_vec()));
    context.flags = NegotiateFlags::from_bits(LOCAL_NEGOTIATE_FLAGS).unwrap();

    let buff = *LOCAL_CHALLENGE_MESSAGE;
    read_challenge(&mut context, buff.as_ref()).unwrap();
}

#[test]
fn read_challenge_fails_with_incorrect_signature() {
    let mut context = Ntlm::new();
    context.set_version(LOCAL_NEGOTIATE_VERSION);
    context.state = NtlmState::Challenge;
    context.negotiate_message = Some(NegotiateMessage::new(LOCAL_NEGOTIATE_MESSAGE.to_vec()));
    context.flags = NegotiateFlags::from_bits(LOCAL_NEGOTIATE_FLAGS).unwrap();

    let mut buff = LOCAL_CHALLENGE_MESSAGE.to_vec();
    buff[1] += 1;
    assert!(read_challenge(&mut context, buff.as_slice()).is_err());
}

#[test]
fn read_challenge_fails_with_incorrect_message_type() {
    let mut context = Ntlm::new();
    context.set_version(LOCAL_NEGOTIATE_VERSION);
    context.state = NtlmState::Challenge;
    context.negotiate_message = Some(NegotiateMessage::new(LOCAL_NEGOTIATE_MESSAGE.to_vec()));
    context.flags = NegotiateFlags::from_bits(LOCAL_NEGOTIATE_FLAGS).unwrap();

    let mut buff = LOCAL_CHALLENGE_MESSAGE.to_vec();
    buff[8] = 3;
    assert!(read_challenge(&mut context, buff.as_slice()).is_err());
}

#[test]
fn read_challenge_reads_correct_flags() {
    let mut context = Ntlm::new();
    context.set_version(LOCAL_NEGOTIATE_VERSION);
    context.state = NtlmState::Challenge;
    context.negotiate_message = Some(NegotiateMessage::new(LOCAL_NEGOTIATE_MESSAGE.to_vec()));
    context.flags = NegotiateFlags::from_bits(LOCAL_NEGOTIATE_FLAGS).unwrap();

    let buff = *LOCAL_CHALLENGE_MESSAGE;
    read_challenge(&mut context, buff.as_ref()).unwrap();

    assert_eq!(
        LOCAL_CHALLENGE_FLAGS.to_le_bytes(),
        buff[CHALLENGE_FLAGS_START..CHALLENGE_SERVER_CHALLENGE_START]
    );
    assert_eq!(NegotiateFlags::from_bits(LOCAL_CHALLENGE_FLAGS).unwrap(), context.flags);
}

#[test]
fn read_challenge_reads_correct_target_info() {
    let mut context = Ntlm::new();
    context.set_version(LOCAL_NEGOTIATE_VERSION);
    context.state = NtlmState::Challenge;
    context.negotiate_message = Some(NegotiateMessage::new(LOCAL_NEGOTIATE_MESSAGE.to_vec()));
    context.flags = NegotiateFlags::from_bits(LOCAL_NEGOTIATE_FLAGS).unwrap();

    let buff = *LOCAL_CHALLENGE_MESSAGE;
    read_challenge(&mut context, buff.as_ref()).unwrap();

    assert_eq!(
        LOCAL_CHALLENGE_TARGET_INFO_BUFFER.as_ref(),
        context.challenge_message.unwrap().target_info.as_slice()
    );
}

#[test]
fn read_challenge_reads_correct_server_challenge() {
    let mut context = Ntlm::new();
    context.set_version(LOCAL_NEGOTIATE_VERSION);
    context.state = NtlmState::Challenge;
    context.negotiate_message = Some(NegotiateMessage::new(LOCAL_NEGOTIATE_MESSAGE.to_vec()));
    context.flags = NegotiateFlags::from_bits(LOCAL_NEGOTIATE_FLAGS).unwrap();

    let buff = *LOCAL_CHALLENGE_MESSAGE;
    read_challenge(&mut context, buff.as_ref()).unwrap();

    assert_eq!(
        LOCAL_CHALLENGE_SERVER_CHALLENGE,
        context.challenge_message.unwrap().server_challenge
    );
}

#[test]
fn read_challenge_reads_correct_timestamp() {
    let mut context = Ntlm::new();
    context.set_version(LOCAL_NEGOTIATE_VERSION);
    context.state = NtlmState::Challenge;
    context.negotiate_message = Some(NegotiateMessage::new(LOCAL_NEGOTIATE_MESSAGE.to_vec()));
    context.flags = NegotiateFlags::from_bits(LOCAL_NEGOTIATE_FLAGS).unwrap();

    let buff = *LOCAL_CHALLENGE_MESSAGE;
    read_challenge(&mut context, buff.as_ref()).unwrap();

    assert_eq!(LOCAL_CHALLENGE_TIMESTAMP, context.challenge_message.unwrap().timestamp);
}

#[test]
fn read_challenge_writes_buffer_to_context() {
    let mut context = Ntlm::new();
    context.set_version(LOCAL_NEGOTIATE_VERSION);
    context.state = NtlmState::Challenge;
    context.negotiate_message = Some(NegotiateMessage::new(LOCAL_NEGOTIATE_MESSAGE.to_vec()));
    context.flags = NegotiateFlags::from_bits(LOCAL_NEGOTIATE_FLAGS).unwrap();

    let buff = *LOCAL_CHALLENGE_MESSAGE;
    read_challenge(&mut context, buff.as_ref()).unwrap();

    assert_eq!(
        (*LOCAL_CHALLENGE_MESSAGE).as_ref(),
        context.challenge_message.unwrap().message.as_slice()
    );
}

#[test]
fn read_challenge_fails_on_incorrect_state() {
    let mut context = Ntlm::new();
    context.set_version(LOCAL_NEGOTIATE_VERSION);
    context.state = NtlmState::Authenticate;
    context.negotiate_message = Some(NegotiateMessage::new(LOCAL_NEGOTIATE_MESSAGE.to_vec()));
    context.flags = NegotiateFlags::from_bits(LOCAL_NEGOTIATE_FLAGS).unwrap();

    let buff = *LOCAL_CHALLENGE_MESSAGE;
    assert!(read_challenge(&mut context, buff.as_ref()).is_err());
}

#[test]
fn write_authenticate_writes_correct_header() {
    let mut context = Ntlm::new();
    context.set_version(NTLM_VERSION);
    context.state = NtlmState::Authenticate;
    context.negotiate_message = Some(NegotiateMessage::new(vec![0x01, 0x02, 0x03]));
    context.challenge_message = Some(ChallengeMessage::new(
        vec![0x04, 0x05, 0x06],
        Vec::new(),
        [0x00; CHALLENGE_SIZE],
        0,
    ));
    let mut buff = Vec::new();
    let expected = [0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x03, 0x00, 0x00, 0x00];

    write_authenticate(&mut context, &TEST_CREDENTIALS, &mut buff).unwrap();

    assert_eq!(
        buff[SIGNATURE_START..AUTHENTICATE_LM_CHALLENGE_RESPONSE_START],
        expected
    );
}

#[test]
fn write_authenticate_changes_context_state_on_success() {
    let mut context = Ntlm::new();
    context.set_version(NTLM_VERSION);
    let mut buff = Vec::new();
    context.negotiate_message = Some(NegotiateMessage::new(vec![0x01, 0x02, 0x03]));
    context.challenge_message = Some(ChallengeMessage::new(
        vec![0x04, 0x05, 0x06],
        Vec::new(),
        [0x00; CHALLENGE_SIZE],
        0,
    ));
    context.state = NtlmState::Authenticate;
    let expected_state = NtlmState::Final;

    write_authenticate(&mut context, &TEST_CREDENTIALS, &mut buff).unwrap();

    assert_eq!(context.state, expected_state);
}

#[test]
fn write_authenticate_correct_writes_domain_name() {
    let expected = [0x0c, 0x00, 0x0c, 0x00, 0x58, 0x00, 0x00, 0x00];
    let expected_buffer = [0x44, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00];

    let mut context = Ntlm::new();
    context.set_version(NTLM_VERSION);
    context.state = NtlmState::Authenticate;
    context.negotiate_message = Some(NegotiateMessage::new(vec![0x01, 0x02, 0x03]));
    context.challenge_message = Some(ChallengeMessage::new(
        vec![0x04, 0x05, 0x06],
        vec![
            0x2, 0x0, 0x8, 0x0, 0x48, 0x4f, 0x53, 0x54, 0x4e, 0x41, 0x4d, 0x45, 0x1, 0x0, 0x8, 0x0, 0x48, 0x4f, 0x53,
            0x54, 0x4e, 0x41, 0x4d, 0x45, 0x4, 0x0, 0x8, 0x0, 0x48, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x3, 0x0,
            0x8, 0x0, 0x48, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x7, 0x0, 0x8, 0x0, 0x33, 0x57, 0xbd, 0xb1, 0x7,
            0x8b, 0xcf, 0x1, 0x6, 0x0, 0x4, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
        ],
        SERVER_CHALLENGE,
        TIMESTAMP,
    ));
    context.flags = NegotiateFlags::NTLM_SSP_NEGOTIATE_KEY_EXCH;

    let mut buff = Vec::new();
    write_authenticate(&mut context, &TEST_CREDENTIALS, &mut buff).unwrap();

    assert_eq!(
        buff[AUTHENTICATE_DOMAIN_NAME_START..AUTHENTICATE_USER_NAME_START],
        expected
    );
    assert_eq!(
        buff[AUTHENTICATE_OFFSET_WITH_MIC..AUTHENTICATE_OFFSET_WITH_MIC + TEST_CREDENTIALS.domain.len()],
        expected_buffer[..]
    );
}

#[test]
fn write_authenticate_correct_writes_user_name() {
    let expected = [0x08, 0x00, 0x08, 0x00, 0x64, 0x00, 0x00, 0x00];
    let expected_buffer = [0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00];

    let mut context = Ntlm::new();
    context.set_version(NTLM_VERSION);
    context.state = NtlmState::Authenticate;
    context.negotiate_message = Some(NegotiateMessage::new(vec![0x01, 0x02, 0x03]));
    context.challenge_message = Some(ChallengeMessage::new(
        vec![0x04, 0x05, 0x06],
        vec![
            0x2, 0x0, 0x8, 0x0, 0x48, 0x4f, 0x53, 0x54, 0x4e, 0x41, 0x4d, 0x45, 0x1, 0x0, 0x8, 0x0, 0x48, 0x4f, 0x53,
            0x54, 0x4e, 0x41, 0x4d, 0x45, 0x4, 0x0, 0x8, 0x0, 0x48, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x3, 0x0,
            0x8, 0x0, 0x48, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x7, 0x0, 0x8, 0x0, 0x33, 0x57, 0xbd, 0xb1, 0x7,
            0x8b, 0xcf, 0x1, 0x6, 0x0, 0x4, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
        ],
        SERVER_CHALLENGE,
        TIMESTAMP,
    ));
    context.flags = NegotiateFlags::NTLM_SSP_NEGOTIATE_KEY_EXCH;

    let mut buff = Vec::new();
    write_authenticate(&mut context, &TEST_CREDENTIALS, &mut buff).unwrap();

    assert_eq!(
        buff[AUTHENTICATE_USER_NAME_START..AUTHENTICATE_WORKSTATION_START],
        expected
    );
    let offset = AUTHENTICATE_OFFSET_WITH_MIC + TEST_CREDENTIALS.domain.len();
    assert_eq!(buff[offset..offset + TEST_CREDENTIALS.user.len()], expected_buffer[..]);
}

#[test]
fn write_authenticate_fails_on_incorrect_state() {
    let mut context = Ntlm::new();
    context.set_version(NTLM_VERSION);
    context.state = NtlmState::Final;
    context.negotiate_message = Some(NegotiateMessage::new(vec![0x01, 0x02, 0x03]));
    context.challenge_message = Some(ChallengeMessage::new(
        vec![0x04, 0x05, 0x06],
        vec![
            0x2, 0x0, 0x8, 0x0, 0x48, 0x4f, 0x53, 0x54, 0x4e, 0x41, 0x4d, 0x45, 0x1, 0x0, 0x8, 0x0, 0x48, 0x4f, 0x53,
            0x54, 0x4e, 0x41, 0x4d, 0x45, 0x4, 0x0, 0x8, 0x0, 0x48, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x3, 0x0,
            0x8, 0x0, 0x48, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x7, 0x0, 0x8, 0x0, 0x33, 0x57, 0xbd, 0xb1, 0x7,
            0x8b, 0xcf, 0x1, 0x6, 0x0, 0x4, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
        ],
        SERVER_CHALLENGE,
        TIMESTAMP,
    ));
    context.flags = NegotiateFlags::NTLM_SSP_NEGOTIATE_KEY_EXCH;

    let mut buff = Vec::new();
    assert!(write_authenticate(&mut context, &TEST_CREDENTIALS, &mut buff).is_err());
}
