/* Copyright (C) 2022-2025 Open Information Security Foundation
 *
 * You can copy, redistribute or modify this Program under the terms of
 * the GNU General Public License version 2 as published by the Free
 * Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * version 2 along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */

// Author: Juliana Fajardini <jufajardini@oisf.net>

//! PostgreSQL nom parsers

use crate::common::nom7::take_until_and_consume;
use nom7::branch::alt;
use nom7::bytes::streaming::{tag, take, take_until, take_until1};
use nom7::character::streaming::{alphanumeric1, char};
use nom7::combinator::{all_consuming, cond, eof, map_parser, opt, peek, verify};
use nom7::error::{make_error, ErrorKind, ParseError};
use nom7::multi::{many1, many_m_n, many_till};
use nom7::number::streaming::{be_i16, be_i32};
use nom7::number::streaming::{be_u16, be_u32, be_u8};
use nom7::sequence::terminated;
use nom7::{Err, IResult, ToUsize};

const PGSQL_LENGTH_FIELD: u32 = 4;

const PGSQL_DUMMY_PROTO_MAJOR: u16 = 1234; // 0x04d2
const PGSQL_DUMMY_PROTO_CANCEL_REQUEST: u16 = 5678; // 0x162e
const PGSQL_DUMMY_PROTO_MINOR_SSL: u16 = 5679; //0x162f
const _PGSQL_DUMMY_PROTO_MINOR_GSSAPI: u16 = 5680; // 0x1630

#[derive(Debug, PartialEq, Eq)]
pub(crate) enum PgsqlParseError<I> {
    InvalidLength,

    NomError(I, ErrorKind),
}

impl<I> ParseError<I> for PgsqlParseError<I> {
    fn from_error_kind(input: I, kind: ErrorKind) -> Self {
        PgsqlParseError::NomError(input, kind)
    }

    fn append(input: I, kind: ErrorKind, _other: Self) -> Self {
        PgsqlParseError::NomError(input, kind)
    }
}

fn parse_gte_length(i: &[u8], expected_length: u32) -> IResult<&[u8], u32, PgsqlParseError<&[u8]>> {
    let res = verify(be_u32::<&[u8], nom7::error::Error<_>>, |&x| {
        x >= expected_length
    })(i);
    match res {
        Ok(result) => Ok((result.0, result.1)),
        Err(nom7::Err::Incomplete(needed)) => Err(Err::Incomplete(needed)),
        Err(_) => Err(Err::Error(PgsqlParseError::InvalidLength)),
    }
}

fn parse_exact_length(
    i: &[u8], expected_length: u32,
) -> IResult<&[u8], u32, PgsqlParseError<&[u8]>> {
    let res = verify(be_u32::<&[u8], nom7::error::Error<_>>, |&x| {
        x == expected_length
    })(i);
    match res {
        Ok(result) => Ok((result.0, result.1)),
        Err(nom7::Err::Incomplete(needed)) => Err(Err::Incomplete(needed)),
        Err(_) => Err(Err::Error(PgsqlParseError::InvalidLength)),
    }
}

#[derive(Debug, PartialEq, Eq)]
pub(crate) enum PgsqlParameters {
    // startup parameters
    User,
    Database,
    Options,
    Replication,
    // runtime parameters
    ServerVersion,
    ServerEncoding,
    ClientEncoding,
    ApplicationName,
    DefaultTransactionReadOnly,
    InHotStandby,
    IsSuperuser,
    SessionAuthorization,
    DateStyle,
    IntervalStyle,
    TimeZone,
    IntegerDatetimes,
    StandardConformingStrings,
    UnknownParameter(Vec<u8>),
}

impl PgsqlParameters {
    pub(crate) fn to_str(&self) -> &str {
        match self {
            PgsqlParameters::User => "user",
            PgsqlParameters::Database => "database",
            PgsqlParameters::Options => "options",
            PgsqlParameters::Replication => "replication",
            PgsqlParameters::ServerVersion => "server_version",
            PgsqlParameters::ServerEncoding => "server_encoding",
            PgsqlParameters::ClientEncoding => "client_encoding",
            PgsqlParameters::ApplicationName => "application_name",
            PgsqlParameters::DefaultTransactionReadOnly => "default_transaction_read_only",
            PgsqlParameters::InHotStandby => "in_hot_standby",
            PgsqlParameters::IsSuperuser => "is_superuser",
            PgsqlParameters::SessionAuthorization => "session_authorization",
            PgsqlParameters::DateStyle => "date_style",
            PgsqlParameters::IntervalStyle => "interval_style",
            PgsqlParameters::TimeZone => "time_zone",
            PgsqlParameters::IntegerDatetimes => "integer_datetimes",
            PgsqlParameters::StandardConformingStrings => "standard_conforming_strings",
            PgsqlParameters::UnknownParameter(name) => {
                std::str::from_utf8(name).unwrap_or("unknown_parameter")
            }
        }
    }
}

impl From<&[u8]> for PgsqlParameters {
    fn from(name: &[u8]) -> Self {
        match name {
            br#"user"# => PgsqlParameters::User,
            br#"database"# => PgsqlParameters::Database,
            br#"options"# => PgsqlParameters::Options,
            br#"replication"# => PgsqlParameters::Replication,
            br#"server_version"# => PgsqlParameters::ServerVersion,
            br#"server_encoding"# => PgsqlParameters::ServerEncoding,
            br#"client_encoding"# => PgsqlParameters::ClientEncoding,
            br#"application_name"# => PgsqlParameters::ApplicationName,
            br#"default_transaction_read_only"# => PgsqlParameters::DefaultTransactionReadOnly,
            br#"in_hot_standby"# => PgsqlParameters::InHotStandby,
            br#"is_superuser"# => PgsqlParameters::IsSuperuser,
            br#"session_authorization"# => PgsqlParameters::SessionAuthorization,
            br#"DateStyle"# => PgsqlParameters::DateStyle,
            br#"IntervalStyle"# => PgsqlParameters::IntervalStyle,
            br#"TimeZone"# => PgsqlParameters::TimeZone,
            br#"integer_datetimes"# => PgsqlParameters::IntegerDatetimes,
            br#"standard_conforming_strings"# => PgsqlParameters::StandardConformingStrings,
            _ => PgsqlParameters::UnknownParameter(name.to_vec()),
        }
    }
}

#[derive(Debug, PartialEq, Eq)]
pub(crate) struct PgsqlParameter {
    pub name: PgsqlParameters,
    pub value: Vec<u8>,
}

#[derive(Debug, PartialEq, Eq)]
pub(crate) struct PgsqlStartupParameters {
    pub user: PgsqlParameter,
    pub optional_params: Option<Vec<PgsqlParameter>>,
}

#[derive(Debug, PartialEq, Eq)]
pub(crate) struct DummyStartupPacket {
    length: u32,
    proto_major: u16,
    proto_minor: u16,
}

#[derive(Debug, PartialEq, Eq)]
pub(crate) struct StartupPacket {
    pub length: u32,
    pub proto_major: u16,
    pub proto_minor: u16,
    pub params: PgsqlStartupParameters,
}

#[derive(Debug, PartialEq, Eq)]
pub(crate) struct RegularPacket {
    pub identifier: u8,
    pub length: u32,
    pub payload: Vec<u8>,
}

#[derive(Debug, PartialEq, Eq)]
pub(crate) struct PgsqlErrorNoticeMessageField {
    pub field_type: PgsqlErrorNoticeFieldType,
    pub field_value: Vec<u8>,
}

#[derive(Debug, PartialEq, Eq)]
pub(crate) struct ErrorNoticeMessage {
    pub identifier: u8,
    pub length: u32,
    pub message_body: Vec<PgsqlErrorNoticeMessageField>,
}

#[derive(Debug, PartialEq, Eq)]
pub(crate) enum SSLResponseMessage {
    SSLAccepted,
    SSLRejected,
    InvalidResponse,
}

impl From<u8> for SSLResponseMessage {
    fn from(identifier: u8) -> Self {
        match identifier {
            b'S' => Self::SSLAccepted,
            b'N' => Self::SSLRejected,
            _ => Self::InvalidResponse,
        }
    }
}

impl From<char> for SSLResponseMessage {
    fn from(identifier: char) -> Self {
        match identifier {
            'S' => Self::SSLAccepted,
            'N' => Self::SSLRejected,
            _ => Self::InvalidResponse,
        }
    }
}

#[derive(Debug, PartialEq, Eq)]
pub(crate) struct ParameterStatusMessage {
    pub identifier: u8,
    pub length: u32,
    pub param: PgsqlParameter,
}

#[derive(Debug, PartialEq, Eq)]
pub(crate) struct BackendKeyDataMessage {
    pub identifier: u8,
    pub length: u32,
    pub backend_pid: u32,
    pub secret_key: u32,
}

#[derive(Debug, PartialEq, Eq)]
pub(crate) struct ConsolidatedDataRowPacket {
    pub identifier: u8,
    pub row_cnt: u64, // row or msg cnt
    pub data_size: u64,
}

#[derive(Debug, PartialEq, Eq)]
pub(crate) struct ReadyForQueryMessage {
    pub identifier: u8,
    pub length: u32,
    pub transaction_status: u8,
}

#[derive(Debug, PartialEq, Eq)]
pub(crate) struct NotificationResponse {
    pub identifier: u8,
    pub length: u32,
    pub pid: u32,
    // two str fields, one right after the other
    pub channel_name: Vec<u8>,
    pub payload: Vec<u8>,
}

#[derive(Debug, PartialEq, Eq)]
pub(crate) struct CopyResponse {
    pub identifier: u8,
    pub length: u32,
    pub column_cnt: u16,
    // for each column, there are column_cnt u16 format codes received
    // for now, we're not storing those
}

#[derive(Debug, PartialEq, Eq)]
pub(crate) struct NoPayloadMessage {
    pub identifier: u8,
    pub length: u32,
}

#[derive(Debug, PartialEq, Eq)]
pub(crate) enum PgsqlBEMessage {
    SSLResponse(SSLResponseMessage),
    ErrorResponse(ErrorNoticeMessage),
    NoticeResponse(ErrorNoticeMessage),
    AuthenticationOk(AuthenticationMessage),
    AuthenticationCleartextPassword(AuthenticationMessage),
    AuthenticationMD5Password(AuthenticationMessage),
    AuthenticationSSPI(AuthenticationMessage),
    AuthenticationSASL(AuthenticationSASLMechanismMessage),
    AuthenticationSASLContinue(AuthenticationMessage),
    AuthenticationSASLFinal(AuthenticationMessage),
    ParameterStatus(ParameterStatusMessage),
    BackendKeyData(BackendKeyDataMessage),
    CommandComplete(RegularPacket),
    CopyOutResponse(CopyResponse),
    CopyInResponse(CopyResponse),
    ConsolidatedCopyDataOut(ConsolidatedDataRowPacket),
    CopyDone(NoPayloadMessage),
    ReadyForQuery(ReadyForQueryMessage),
    RowDescription(RowDescriptionMessage),
    ConsolidatedDataRow(ConsolidatedDataRowPacket),
    NotificationResponse(NotificationResponse),
    UnknownMessageType(RegularPacket),
}

impl PgsqlBEMessage {
    pub(crate) fn to_str(&self) -> &'static str {
        match self {
            PgsqlBEMessage::SSLResponse(SSLResponseMessage::SSLAccepted) => "ssl_accepted",
            PgsqlBEMessage::SSLResponse(SSLResponseMessage::SSLRejected) => "ssl_rejected",
            PgsqlBEMessage::ErrorResponse(_) => "error_response",
            PgsqlBEMessage::NoticeResponse(_) => "notice_response",
            PgsqlBEMessage::AuthenticationOk(_) => "authentication_ok",
            PgsqlBEMessage::AuthenticationCleartextPassword(_) => {
                "authentication_cleartext_password"
            }
            PgsqlBEMessage::AuthenticationMD5Password(_) => "authentication_md5_password",
            PgsqlBEMessage::AuthenticationSSPI(_) => "authentication_sspi",
            PgsqlBEMessage::AuthenticationSASL(_) => "authentication_sasl",
            PgsqlBEMessage::AuthenticationSASLContinue(_) => "authentication_sasl_continue",
            PgsqlBEMessage::AuthenticationSASLFinal(_) => "authentication_sasl_final",
            PgsqlBEMessage::ParameterStatus(_) => "parameter_status",
            PgsqlBEMessage::BackendKeyData(_) => "backend_key_data",
            PgsqlBEMessage::CommandComplete(_) => "command_completed",
            PgsqlBEMessage::CopyOutResponse(_) => "copy_out_response",
            PgsqlBEMessage::CopyInResponse(_) => "copy_in_response",
            PgsqlBEMessage::ConsolidatedCopyDataOut(_) => "copy_data_out",
            PgsqlBEMessage::CopyDone(_) => "copy_done",
            PgsqlBEMessage::ReadyForQuery(_) => "ready_for_query",
            PgsqlBEMessage::RowDescription(_) => "row_description",
            PgsqlBEMessage::SSLResponse(SSLResponseMessage::InvalidResponse) => {
                "invalid_be_message"
            }
            PgsqlBEMessage::ConsolidatedDataRow(_) => "data_row",
            PgsqlBEMessage::NotificationResponse(_) => "notification_response",
            PgsqlBEMessage::UnknownMessageType(_) => "unknown_message_type",
        }
    }

    pub fn get_backendkey_info(&self) -> (u32, u32) {
        match self {
            PgsqlBEMessage::BackendKeyData(message) => {
                return (message.backend_pid, message.secret_key);
            }
            _ => (0, 0),
        }
    }
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub(crate) enum SASLAuthenticationMechanism {
    ScramSha256,
    ScramSha256Plus,
    // UnknownMechanism,
}

impl SASLAuthenticationMechanism {
    pub(crate) fn to_str(&self) -> &'static str {
        match self {
            SASLAuthenticationMechanism::ScramSha256 => "scram_SHA256",
            SASLAuthenticationMechanism::ScramSha256Plus => "scram_SHA256_plus",
        }
    }
}

type SASLInitialResponse = (SASLAuthenticationMechanism, u32, Vec<u8>);

#[derive(Debug, PartialEq, Eq)]
pub(crate) struct CancelRequestMessage {
    pub pid: u32,
    pub backend_key: u32,
}

#[derive(Debug, PartialEq, Eq)]
pub(crate) enum PgsqlFEMessage {
    SSLRequest(DummyStartupPacket),
    StartupMessage(StartupPacket),
    PasswordMessage(RegularPacket),
    SASLInitialResponse(SASLInitialResponsePacket),
    SASLResponse(RegularPacket),
    SimpleQuery(RegularPacket),
    ConsolidatedCopyDataIn(ConsolidatedDataRowPacket),
    CopyDone(NoPayloadMessage),
    CopyFail(RegularPacket),
    CancelRequest(CancelRequestMessage),
    Terminate(NoPayloadMessage),
    UnknownMessageType(RegularPacket),
}

impl PgsqlFEMessage {
    pub(crate) fn to_str(&self) -> &'static str {
        match self {
            PgsqlFEMessage::StartupMessage(_) => "startup_message",
            PgsqlFEMessage::SSLRequest(_) => "ssl_request",
            PgsqlFEMessage::PasswordMessage(_) => "password",
            PgsqlFEMessage::SASLInitialResponse(_) => "sasl_initial_response",
            PgsqlFEMessage::SASLResponse(_) => "sasl_response",
            PgsqlFEMessage::SimpleQuery(_) => "simple_query",
            PgsqlFEMessage::ConsolidatedCopyDataIn(_) => "copy_data_in",
            PgsqlFEMessage::CopyDone(_) => "copy_done",
            PgsqlFEMessage::CopyFail(_) => "copy_fail",
            PgsqlFEMessage::CancelRequest(_) => "cancel_request",
            PgsqlFEMessage::Terminate(_) => "termination_message",
            PgsqlFEMessage::UnknownMessageType(_) => "unknown_message_type",
        }
    }
}

#[derive(Debug, PartialEq, Eq)]
pub(crate) struct AuthenticationMessage {
    pub identifier: u8,
    pub length: u32,
    pub auth_type: u32,
    pub payload: Vec<u8>,
}

#[derive(Debug, PartialEq, Eq)]
pub(crate) struct SASLInitialResponsePacket {
    pub identifier: u8,
    pub length: u32,
    pub auth_mechanism: SASLAuthenticationMechanism,
    pub param_length: u32,
    pub sasl_param: Vec<u8>,
}

#[derive(Debug, PartialEq, Eq)]
pub(crate) struct AuthenticationSASLMechanismMessage {
    identifier: u8,
    length: u32,
    auth_type: u32,
    auth_mechanisms: Vec<SASLAuthenticationMechanism>,
}

#[derive(Debug, PartialEq, Eq)]
pub(crate) struct RowField {
    pub field_name: Vec<u8>,
    pub table_oid: u32,
    pub column_index: u16,
    pub data_type_oid: u32,
    // "see pg_type.typlen. Note that negative values denote variable-width types"
    pub data_type_size: i16,
    // "The value will generally be -1 for types that do not need pg_attribute.atttypmod."
    pub type_modifier: i32,
    // "The format code being used for the field. Currently will be zero (text) or one (binary). In a RowDescription returned from the variant of Describe, will always be zero"
    pub format_code: u16,
}

#[derive(Debug, PartialEq, Eq)]
pub(crate) struct RowDescriptionMessage {
    pub identifier: u8,
    pub length: u32,
    pub field_count: u16,
    pub fields: Vec<RowField>,
}

#[derive(Debug, PartialEq, Eq)]
pub(crate) struct ColumnFieldValue {
    // Can be 0, or -1 as a special NULL column value
    pub value_length: i32,
    pub value: Vec<u8>,
}

#[derive(Debug, PartialEq, Eq)]
pub(crate) enum PgsqlErrorNoticeFieldType {
    SeverityLocalizable,
    SeverityNonLocalizable,
    CodeSqlStateCode,
    Message,
    Detail,
    Hint,
    Position,
    InternalPosition,
    InternalQuery,
    Where,
    SchemaName,
    TableName,
    ColumnName,
    DataType,
    ConstraintName,
    File,
    Line,
    Routine,
    // Indicates end of message
    TerminatorToken,
    // From the documentation: "Since more field types might be added in future, frontends should silently ignore fields of unrecognized type." For us, then, I think the best option is actually to print it as we parse it, so it is readable?
    UnknownFieldType,
}

impl PgsqlErrorNoticeFieldType {
    pub(crate) fn to_str(&self) -> &'static str {
        match self {
            PgsqlErrorNoticeFieldType::SeverityLocalizable => "severity_localizable",
            PgsqlErrorNoticeFieldType::SeverityNonLocalizable => "severity_non_localizable",
            PgsqlErrorNoticeFieldType::CodeSqlStateCode => "code",
            PgsqlErrorNoticeFieldType::Message => "message",
            PgsqlErrorNoticeFieldType::Detail => "detail",
            PgsqlErrorNoticeFieldType::Hint => "hint",
            PgsqlErrorNoticeFieldType::Position => "position",
            PgsqlErrorNoticeFieldType::InternalPosition => "internal_position",
            PgsqlErrorNoticeFieldType::InternalQuery => "internal_query",
            PgsqlErrorNoticeFieldType::Where => "where",
            PgsqlErrorNoticeFieldType::SchemaName => "schema_name",
            PgsqlErrorNoticeFieldType::TableName => "table_name",
            PgsqlErrorNoticeFieldType::ColumnName => "column_name",
            PgsqlErrorNoticeFieldType::DataType => "data_type",
            PgsqlErrorNoticeFieldType::ConstraintName => "constraint_name",
            PgsqlErrorNoticeFieldType::File => "file",
            PgsqlErrorNoticeFieldType::Line => "line",
            PgsqlErrorNoticeFieldType::Routine => "routine",
            PgsqlErrorNoticeFieldType::TerminatorToken => "",
            PgsqlErrorNoticeFieldType::UnknownFieldType => "unknown_field_type",
        }
    }
}

impl From<char> for PgsqlErrorNoticeFieldType {
    fn from(identifier: char) -> PgsqlErrorNoticeFieldType {
        match identifier {
            'S' => PgsqlErrorNoticeFieldType::SeverityLocalizable,
            'V' => PgsqlErrorNoticeFieldType::SeverityNonLocalizable,
            'C' => PgsqlErrorNoticeFieldType::CodeSqlStateCode,
            'M' => PgsqlErrorNoticeFieldType::Message,
            'D' => PgsqlErrorNoticeFieldType::Detail,
            'H' => PgsqlErrorNoticeFieldType::Hint,
            'P' => PgsqlErrorNoticeFieldType::Position,
            'p' => PgsqlErrorNoticeFieldType::InternalPosition,
            'q' => PgsqlErrorNoticeFieldType::InternalQuery,
            'W' => PgsqlErrorNoticeFieldType::Where,
            's' => PgsqlErrorNoticeFieldType::SchemaName,
            't' => PgsqlErrorNoticeFieldType::TableName,
            'c' => PgsqlErrorNoticeFieldType::ColumnName,
            'd' => PgsqlErrorNoticeFieldType::DataType,
            'n' => PgsqlErrorNoticeFieldType::ConstraintName,
            'F' => PgsqlErrorNoticeFieldType::File,
            'L' => PgsqlErrorNoticeFieldType::Line,
            'R' => PgsqlErrorNoticeFieldType::Routine,
            '\u{0}' => PgsqlErrorNoticeFieldType::TerminatorToken,
            // Pgsql documentation says "frontends should silently ignore fields of unrecognized type."
            _ => PgsqlErrorNoticeFieldType::UnknownFieldType,
        }
    }
}

impl From<u8> for PgsqlErrorNoticeFieldType {
    fn from(identifier: u8) -> PgsqlErrorNoticeFieldType {
        match identifier {
            b'S' => PgsqlErrorNoticeFieldType::SeverityLocalizable,
            b'V' => PgsqlErrorNoticeFieldType::SeverityNonLocalizable,
            b'C' => PgsqlErrorNoticeFieldType::CodeSqlStateCode,
            b'M' => PgsqlErrorNoticeFieldType::Message,
            b'D' => PgsqlErrorNoticeFieldType::Detail,
            b'H' => PgsqlErrorNoticeFieldType::Hint,
            b'P' => PgsqlErrorNoticeFieldType::Position,
            b'p' => PgsqlErrorNoticeFieldType::InternalPosition,
            b'q' => PgsqlErrorNoticeFieldType::InternalQuery,
            b'W' => PgsqlErrorNoticeFieldType::Where,
            b's' => PgsqlErrorNoticeFieldType::SchemaName,
            b't' => PgsqlErrorNoticeFieldType::TableName,
            b'c' => PgsqlErrorNoticeFieldType::ColumnName,
            b'd' => PgsqlErrorNoticeFieldType::DataType,
            b'n' => PgsqlErrorNoticeFieldType::ConstraintName,
            b'F' => PgsqlErrorNoticeFieldType::File,
            b'L' => PgsqlErrorNoticeFieldType::Line,
            b'R' => PgsqlErrorNoticeFieldType::Routine,
            b'\0' => PgsqlErrorNoticeFieldType::TerminatorToken,
            // Pgsql documentation says "frontends should silently ignore fields of unrecognized type."
            _ => PgsqlErrorNoticeFieldType::UnknownFieldType,
        }
    }
}

// Currently the set of parameters that could trigger a ParameterStatus message is fixed:
// server_version
// server_encoding
// client_encoding
// application_name
// default_transaction_read_only
// in_hot_standby
// is_superuser
// session_authorization
// DateStyle
// IntervalStyle
// TimeZone
// integer_datetimes
// standard_conforming_strings
// (source: PostgreSQL documentation)
// We may be interested, then, in controling this, somehow, to prevent weird things?
fn pgsql_parse_generic_parameter(
    i: &[u8],
) -> IResult<&[u8], PgsqlParameter, PgsqlParseError<&[u8]>> {
    let (i, param_name) = take_until1("\x00")(i)?;
    let (i, _) = tag("\x00")(i)?;
    let (i, param_value) = take_until("\x00")(i)?;
    let (i, _) = tag("\x00")(i)?;
    Ok((
        i,
        PgsqlParameter {
            name: PgsqlParameters::from(param_name),
            value: param_value.to_vec(),
        },
    ))
}

fn pgsql_parse_startup_parameters(
    i: &[u8],
) -> IResult<&[u8], PgsqlStartupParameters, PgsqlParseError<&[u8]>> {
    let (i, mut optional) = opt(terminated(
        many1(pgsql_parse_generic_parameter),
        tag("\x00"),
    ))(i)?;
    if let Some(ref mut params) = optional {
        let mut user = PgsqlParameter {
            name: PgsqlParameters::User,
            value: Vec::new(),
        };
        let mut index: usize = 0;
        for (j, p) in params.iter().enumerate() {
            if p.name == PgsqlParameters::User {
                user.value.extend_from_slice(&p.value);
                index = j;
            }
        }
        params.remove(index);
        if user.value.is_empty() {
            return Err(Err::Error(make_error(i, ErrorKind::Tag)));
        }
        return Ok((
            i,
            PgsqlStartupParameters {
                user,
                optional_params: if !params.is_empty() { optional } else { None },
            },
        ));
    }
    return Err(Err::Error(make_error(i, ErrorKind::Tag)));
}

fn parse_sasl_initial_response_payload(
    i: &[u8],
) -> IResult<&[u8], SASLInitialResponse, PgsqlParseError<&[u8]>> {
    let (i, sasl_mechanism) = parse_sasl_mechanism(i)?;
    let (i, param_length) = be_u32(i)?;
    // From RFC 5802 - the client-first-message will always start w/
    // 'n', 'y' or 'p', otherwise it's invalid, I think we should check that, at some point
    let (i, param) = terminated(take(param_length), eof)(i)?;
    Ok((i, (sasl_mechanism, param_length, param.to_vec())))
}

pub(crate) fn parse_sasl_initial_response(
    i: &[u8],
) -> IResult<&[u8], PgsqlFEMessage, PgsqlParseError<&[u8]>> {
    let (i, identifier) = verify(be_u8, |&x| x == b'p')(i)?;
    let (i, length) = parse_gte_length(i, PGSQL_LENGTH_FIELD)?;
    let (i, payload) = map_parser(
        take(length - PGSQL_LENGTH_FIELD),
        parse_sasl_initial_response_payload,
    )(i)?;
    Ok((
        i,
        PgsqlFEMessage::SASLInitialResponse(SASLInitialResponsePacket {
            identifier,
            length,
            auth_mechanism: payload.0,
            param_length: payload.1,
            sasl_param: payload.2,
        }),
    ))
}

pub(crate) fn parse_sasl_response(i: &[u8]) -> IResult<&[u8], PgsqlFEMessage, PgsqlParseError<&[u8]>> {
    let (i, identifier) = verify(be_u8, |&x| x == b'p')(i)?;
    let (i, length) = parse_gte_length(i, PGSQL_LENGTH_FIELD)?;
    let (i, payload) = take(length - PGSQL_LENGTH_FIELD)(i)?;
    let resp = PgsqlFEMessage::SASLResponse(RegularPacket {
        identifier,
        length,
        payload: payload.to_vec(),
    });
    Ok((i, resp))
}

fn pgsql_parse_startup_packet(
    i: &[u8],
) -> IResult<&[u8], PgsqlFEMessage, PgsqlParseError<&[u8]>> {
    let (i, length) = parse_gte_length(i, 8)?;
    let (i, proto_major) = peek(be_u16)(i)?;
    let (i, b) = take(length - PGSQL_LENGTH_FIELD)(i)?;
    let (_, message) = match proto_major {
        1..=3 => {
            let (b, proto_major) = be_u16(b)?;
            let (b, proto_minor) = be_u16(b)?;
            let (b, params) = pgsql_parse_startup_parameters(b)?;
            (
                b,
                PgsqlFEMessage::StartupMessage(StartupPacket {
                    length,
                    proto_major,
                    proto_minor,
                    params,
                }),
            )
        }
        PGSQL_DUMMY_PROTO_MAJOR => {
            let (b, proto_major) = be_u16(b)?;
            let (b, proto_minor) = be_u16(b)?;
            let (b, message) = match proto_minor {
                PGSQL_DUMMY_PROTO_CANCEL_REQUEST => parse_cancel_request(b)?,
                PGSQL_DUMMY_PROTO_MINOR_SSL => (
                    b,
                    PgsqlFEMessage::SSLRequest(DummyStartupPacket {
                        length,
                        proto_major,
                        proto_minor,
                    }),
                ),
                _ => return Err(Err::Error(make_error(b, ErrorKind::Switch))),
            };

            (b, message)
        }
        _ => return Err(Err::Error(make_error(b, ErrorKind::Switch))),
    };
    Ok((i, message))
}

// TODO Decide if it's a good idea to offer GSS encryption support right now, as the documentation seems to have conflicting information...
// If we do:
// To initiate a GSSAPI-encrypted connection, the frontend initially sends a GSSENCRequest message rather than a
// StartupMessage. The server then responds with a single byte containing G or N, indicating that it is willing or unwilling to perform GSSAPI encryption, respectively. The frontend might close the connection at this point if it is
// dissatisfied with the response. To continue after G, using the GSSAPI C bindings as discussed in RFC2744 or equivalent,
// perform a GSSAPI initialization by calling gss_init_sec_context() in a loop and sending the result to the server,
// starting with an empty input and then with each result from the server, until it returns no output. When sending the
// results of gss_init_sec_context() to the server, prepend the length of the message as a four byte integer in network
// byte order. To continue after N, send the usual StartupMessage and proceed without encryption. (Alternatively, it is
// permissible to issue an SSLRequest message after an N response to try to use SSL encryption instead of GSSAPI.)
// Source: https://www.postgresql.org/docs/13/protocol-flow.html#id-1.10.5.7.11, GSSAPI Session Encryption

// Password can be encrypted or in cleartext
pub(crate) fn parse_password_message(i: &[u8]) -> IResult<&[u8], PgsqlFEMessage, PgsqlParseError<&[u8]>> {
    let (i, identifier) = verify(be_u8, |&x| x == b'p')(i)?;
    let (i, length) = parse_gte_length(i, PGSQL_LENGTH_FIELD)?;
    let (i, password) = map_parser(take(length - PGSQL_LENGTH_FIELD), take_until1("\x00"))(i)?;
    Ok((
        i,
        PgsqlFEMessage::PasswordMessage(RegularPacket {
            identifier,
            length,
            payload: password.to_vec(),
        }),
    ))
}

fn parse_simple_query(i: &[u8]) -> IResult<&[u8], PgsqlFEMessage, PgsqlParseError<&[u8]>> {
    let (i, identifier) = verify(be_u8, |&x| x == b'Q')(i)?;
    let (i, length) = parse_gte_length(i, PGSQL_LENGTH_FIELD)?;
    let (i, query) = map_parser(take(length - PGSQL_LENGTH_FIELD), take_until1("\x00"))(i)?;
    Ok((
        i,
        PgsqlFEMessage::SimpleQuery(RegularPacket {
            identifier,
            length,
            payload: query.to_vec(),
        }),
    ))
}

fn parse_cancel_request(i: &[u8]) -> IResult<&[u8], PgsqlFEMessage, PgsqlParseError<&[u8]>> {
    let (i, pid) = be_u32(i)?;
    let (i, backend_key) = be_u32(i)?;
    Ok((
        i,
        PgsqlFEMessage::CancelRequest(CancelRequestMessage { pid, backend_key }),
    ))
}

fn parse_terminate_message(i: &[u8]) -> IResult<&[u8], PgsqlFEMessage, PgsqlParseError<&[u8]>> {
    let (i, identifier) = verify(be_u8, |&x| x == b'X')(i)?;
    let (i, length) = parse_exact_length(i, PGSQL_LENGTH_FIELD)?;
    Ok((
        i,
        PgsqlFEMessage::Terminate(NoPayloadMessage { identifier, length }),
    ))
}

// Messages that begin with 'p' but are not password ones are not parsed here
pub(crate) fn parse_request(i: &[u8]) -> IResult<&[u8], PgsqlFEMessage, PgsqlParseError<&[u8]>> {
    let (i, tag) = peek(be_u8)(i)?;
    let (i, message) = match tag {
        b'\0' => pgsql_parse_startup_packet(i)?,
        b'Q' => parse_simple_query(i)?,
        b'X' => parse_terminate_message(i)?,
        b'd' => parse_consolidated_copy_data_in(i)?,
        b'c' => parse_copy_in_done(i)?,
        b'f' => parse_copy_fail(i)?,
        _ => {
            let (i, identifier) = be_u8(i)?;
            let (i, length) = parse_gte_length(i, PGSQL_LENGTH_FIELD)?;
            let (i, payload) = take(length - PGSQL_LENGTH_FIELD)(i)?;
            let unknown = PgsqlFEMessage::UnknownMessageType(RegularPacket {
                identifier,
                length,
                payload: payload.to_vec(),
            });
            SCLogDebug!("parsed unknown FE message, identifier is {:?}", identifier);
            (i, unknown)
        }
    };
    Ok((i, message))
}

fn pgsql_parse_authentication_message<'a>(
    i: &'a [u8],
) -> IResult<&'a [u8], PgsqlBEMessage, PgsqlParseError<&'a [u8]>> {
    let (i, identifier) = verify(be_u8, |&x| x == b'R')(i)?;
    let (i, length) = parse_gte_length(i, 8)?;
    let (i, auth_type) = be_u32(i)?;
    let (i, message) = map_parser(take(length - 8), |b: &'a [u8]| {
        match auth_type {
            0 => Ok((
                b,
                PgsqlBEMessage::AuthenticationOk(AuthenticationMessage {
                    identifier,
                    length,
                    auth_type,
                    payload: b.to_vec(),
                }),
            )),
            3 => Ok((
                b,
                PgsqlBEMessage::AuthenticationCleartextPassword(AuthenticationMessage {
                    identifier,
                    length,
                    auth_type,
                    payload: b.to_vec(),
                }),
            )),
            5 => {
                let (b, salt) = all_consuming(take(4_usize))(b)?;
                Ok((
                    b,
                    PgsqlBEMessage::AuthenticationMD5Password(AuthenticationMessage {
                        identifier,
                        length,
                        auth_type,
                        payload: salt.to_vec(),
                    }),
                ))
            }
            9 => Ok((
                b,
                PgsqlBEMessage::AuthenticationSSPI(AuthenticationMessage {
                    identifier,
                    length,
                    auth_type,
                    payload: b.to_vec(),
                }),
            )),
            // TODO - For SASL, should we parse specific details of the challenge itself? (as seen in: https://github.com/launchbadge/sqlx/blob/master/sqlx-core/src/postgres/message/authentication.rs )
            10 => {
                let (b, auth_mechanisms) = parse_sasl_mechanisms(b)?;
                Ok((
                    b,
                    PgsqlBEMessage::AuthenticationSASL(AuthenticationSASLMechanismMessage {
                        identifier,
                        length,
                        auth_type,
                        auth_mechanisms,
                    }),
                ))
            }
            11 => Ok((
                b,
                PgsqlBEMessage::AuthenticationSASLContinue(AuthenticationMessage {
                    identifier,
                    length,
                    auth_type,
                    payload: b.to_vec(),
                }),
            )),
            12 => Ok((
                b,
                PgsqlBEMessage::AuthenticationSASLFinal(AuthenticationMessage {
                    identifier,
                    length,
                    auth_type,
                    payload: b.to_vec(),
                }),
            )),
            // TODO add other authentication messages
            _ => return Err(Err::Error(make_error(i, ErrorKind::Switch))),
        }
    })(i)?;
    Ok((i, message))
}

fn parse_parameter_status_message(
    i: &[u8],
) -> IResult<&[u8], PgsqlBEMessage, PgsqlParseError<&[u8]>> {
    let (i, identifier) = verify(be_u8, |&x| x == b'S')(i)?;
    let (i, length) = parse_gte_length(i, PGSQL_LENGTH_FIELD)?;
    let (i, param) = map_parser(
        take(length - PGSQL_LENGTH_FIELD),
        pgsql_parse_generic_parameter,
    )(i)?;
    Ok((
        i,
        PgsqlBEMessage::ParameterStatus(ParameterStatusMessage {
            identifier,
            length,
            param,
        }),
    ))
}

pub(crate) fn parse_ssl_response(i: &[u8]) -> IResult<&[u8], PgsqlBEMessage, PgsqlParseError<&[u8]>> {
    let (i, tag) = alt((char('N'), char('S')))(i)?;
    Ok((
        i,
        PgsqlBEMessage::SSLResponse(SSLResponseMessage::from(tag)),
    ))
}

fn parse_backend_key_data_message(
    i: &[u8],
) -> IResult<&[u8], PgsqlBEMessage, PgsqlParseError<&[u8]>> {
    let (i, identifier) = verify(be_u8, |&x| x == b'K')(i)?;
    let (i, length) = parse_exact_length(i, 12)?;
    let (i, pid) = be_u32(i)?;
    let (i, secret_key) = be_u32(i)?;
    Ok((
        i,
        PgsqlBEMessage::BackendKeyData(BackendKeyDataMessage {
            identifier,
            length,
            backend_pid: pid,
            secret_key,
        }),
    ))
}

fn parse_command_complete(i: &[u8]) -> IResult<&[u8], PgsqlBEMessage, PgsqlParseError<&[u8]>> {
    let (i, identifier) = verify(be_u8, |&x| x == b'C')(i)?;
    let (i, length) = parse_gte_length(i, PGSQL_LENGTH_FIELD)?;
    let (i, payload) = map_parser(take(length - PGSQL_LENGTH_FIELD), take_until("\x00"))(i)?;
    Ok((
        i,
        PgsqlBEMessage::CommandComplete(RegularPacket {
            identifier,
            length,
            payload: payload.to_vec(),
        }),
    ))
}

fn parse_ready_for_query(i: &[u8]) -> IResult<&[u8], PgsqlBEMessage, PgsqlParseError<&[u8]>> {
    let (i, identifier) = verify(be_u8, |&x| x == b'Z')(i)?;
    let (i, length) = parse_exact_length(i, 5)?;
    let (i, status) = verify(be_u8, |&x| x == b'I' || x == b'T' || x == b'E')(i)?;
    Ok((
        i,
        PgsqlBEMessage::ReadyForQuery(ReadyForQueryMessage {
            identifier,
            length,
            transaction_status: status,
        }),
    ))
}

fn parse_row_field(i: &[u8]) -> IResult<&[u8], RowField, PgsqlParseError<&[u8]>> {
    let (i, field_name) = take_until1("\x00")(i)?;
    let (i, _) = tag("\x00")(i)?;
    let (i, table_oid) = be_u32(i)?;
    let (i, column_index) = be_u16(i)?;
    let (i, data_type_oid) = be_u32(i)?;
    let (i, data_type_size) = be_i16(i)?;
    let (i, type_modifier) = be_i32(i)?;
    let (i, format_code) = be_u16(i)?;
    Ok((
        i,
        RowField {
            field_name: field_name.to_vec(),
            table_oid,
            column_index,
            data_type_oid,
            data_type_size,
            type_modifier,
            format_code,
        },
    ))
}

fn parse_row_description(i: &[u8]) -> IResult<&[u8], PgsqlBEMessage, PgsqlParseError<&[u8]>> {
    let (i, identifier) = verify(be_u8, |&x| x == b'T')(i)?;
    let (i, length) = parse_gte_length(i, 7)?;
    let (i, field_count) = be_u16(i)?;
    let (i, fields) = map_parser(
        take(length - 6),
        many_m_n(0, field_count.into(), parse_row_field),
    )(i)?;
    Ok((
        i,
        PgsqlBEMessage::RowDescription(RowDescriptionMessage {
            identifier,
            length,
            field_count,
            fields,
        }),
    ))
}

fn parse_data_row_value(i: &[u8]) -> IResult<&[u8], ColumnFieldValue, PgsqlParseError<&[u8]>> {
    let (i, value_length) = be_i32(i)?;
    let (i, value) = cond(value_length >= 0, take(value_length as usize))(i)?;
    Ok((
        i,
        ColumnFieldValue {
            value_length,
            value: {
                match value {
                    Some(data) => data.to_vec(),
                    None => [].to_vec(),
                }
            },
        },
    ))
}

/// For each column, add up the data size. Return the total
fn add_up_data_size(columns: Vec<ColumnFieldValue>) -> u64 {
    let mut data_size: u64 = 0;
    for field in columns {
        // -1 value means data value is NULL, let's not add that up
        if field.value_length > 0 {
            data_size += field.value_length as u64;
        }
    }
    data_size
}

fn parse_copy_out_response(i: &[u8]) -> IResult<&[u8], PgsqlBEMessage, PgsqlParseError<&[u8]>> {
    let (i, identifier) = verify(be_u8, |&x| x == b'H')(i)?;
    // copy out message : identifier (u8), length (u32), format (u8), cols (u16), formats (u16*cols)
    let (i, length) = parse_gte_length(i, 8)?;
    let (i, _format) = be_u8(i)?;
    let (i, columns) = be_u16(i)?;
    let (i, _formats) = many_m_n(0, columns.to_usize(), be_u16)(i)?;
    Ok((
        i,
        PgsqlBEMessage::CopyOutResponse(CopyResponse {
            identifier,
            length,
            column_cnt: columns,
        })
    ))
}

fn parse_copy_in_response(i: &[u8]) -> IResult<&[u8], PgsqlBEMessage, PgsqlParseError<&[u8]>> {
    let (i, identifier) = verify(be_u8, |&x| x == b'G')(i)?;
    let (i, length) = parse_gte_length(i, 8)?;
    let (i, _format) = be_u8(i)?;
    let (i, columns) = be_u16(i)?;
    let (i, _formats) = many_m_n(0, columns.to_usize(), be_u16)(i)?;
    Ok((
        i,
        PgsqlBEMessage::CopyInResponse(CopyResponse {
            identifier,
            length,
            column_cnt: columns,
        })
    ))
}

fn parse_consolidated_copy_data_out(i: &[u8]) -> IResult<&[u8], PgsqlBEMessage, PgsqlParseError<&[u8]>> {
    let (i, identifier) = verify(be_u8, |&x| x == b'd')(i)?;
    let (i, length) = parse_gte_length(i, 5)?;
    let (i, _data) = take(length - PGSQL_LENGTH_FIELD)(i)?;
    SCLogDebug!("data_size is {:?}", _data);
    Ok((
        i, PgsqlBEMessage::ConsolidatedCopyDataOut(ConsolidatedDataRowPacket {
            identifier,
            row_cnt: 1,
            data_size: (length - PGSQL_LENGTH_FIELD) as u64 })
    ))
}

fn parse_consolidated_copy_data_in(i: &[u8]) -> IResult<&[u8], PgsqlFEMessage, PgsqlParseError<&[u8]>> {
    let (i, identifier) = verify(be_u8, |&x| x == b'd')(i)?;
    let (i, length) = parse_gte_length(i, 5)?;
    let (i, _data) = take(length - PGSQL_LENGTH_FIELD)(i)?;
    SCLogDebug!("data size is {:?}", _data);
    Ok((
        i, PgsqlFEMessage::ConsolidatedCopyDataIn(ConsolidatedDataRowPacket {
            identifier,
            row_cnt: 1,
            data_size: (length - PGSQL_LENGTH_FIELD) as u64 })
    ))
}

fn parse_copy_in_done(i: &[u8]) -> IResult<&[u8], PgsqlFEMessage, PgsqlParseError<&[u8]>> {
    let (i, identifier) = verify(be_u8, |&x| x == b'c')(i)?;
    let (i, length) = parse_exact_length(i, PGSQL_LENGTH_FIELD)?;
    Ok((
        i, PgsqlFEMessage::CopyDone(NoPayloadMessage {
            identifier,
            length
        })
    ))
}

fn parse_copy_out_done(i: &[u8]) -> IResult<&[u8], PgsqlBEMessage, PgsqlParseError<&[u8]>> {
    let (i, identifier) = verify(be_u8, |&x| x == b'c')(i)?;
    let (i, length) = parse_exact_length(i, PGSQL_LENGTH_FIELD)?;
    Ok((
        i, PgsqlBEMessage::CopyDone(NoPayloadMessage {
            identifier,
            length
        })
    ))
}

fn parse_copy_fail(i: &[u8]) -> IResult<&[u8], PgsqlFEMessage, PgsqlParseError<&[u8]>> {
    let (i, identifier) = verify(be_u8, |&x| x == b'f')(i)?;
    let (i, length) = parse_gte_length(i, 5)?;
    let (i, data) = take(length - PGSQL_LENGTH_FIELD)(i)?;
    Ok((
        i, PgsqlFEMessage::CopyFail(RegularPacket {
            identifier,
            length,
            payload: data.to_vec(),
        })
    ))
}

// Currently, we don't store the actual DataRow messages, as those could easily become a burden, memory-wise
// We use ConsolidatedDataRow to store info we still want to log: message size.
// Later on, we calculate the number of lines the command actually returned by counting ConsolidatedDataRow messages
fn parse_consolidated_data_row(
    i: &[u8],
) -> IResult<&[u8], PgsqlBEMessage, PgsqlParseError<&[u8]>> {
    let (i, identifier) = verify(be_u8, |&x| x == b'D')(i)?;
    let (i, length) = parse_gte_length(i, 7)?;
    let (i, field_count) = be_u16(i)?;
    // 6 here is for skipping length + field_count
    let (i, rows) = map_parser(
        take(length - 6),
        many_m_n(0, field_count.into(), parse_data_row_value),
    )(i)?;
    Ok((
        i,
        PgsqlBEMessage::ConsolidatedDataRow(ConsolidatedDataRowPacket {
            identifier,
            row_cnt: 1,
            data_size: add_up_data_size(rows),
        }),
    ))
}

fn parse_sasl_mechanism(
    i: &[u8],
) -> IResult<&[u8], SASLAuthenticationMechanism, PgsqlParseError<&[u8]>> {
    let res: IResult<_, _, ()> = terminated(tag("SCRAM-SHA-256-PLUS"), tag("\x00"))(i);
    if let Ok((i, _)) = res {
        return Ok((i, SASLAuthenticationMechanism::ScramSha256Plus));
    }
    let res: IResult<_, _, ()> = terminated(tag("SCRAM-SHA-256"), tag("\x00"))(i);
    if let Ok((i, _)) = res {
        return Ok((i, SASLAuthenticationMechanism::ScramSha256));
    }
    return Err(Err::Error(make_error(i, ErrorKind::Alt)));
}

fn parse_sasl_mechanisms(
    i: &[u8],
) -> IResult<&[u8], Vec<SASLAuthenticationMechanism>, PgsqlParseError<&[u8]>> {
    terminated(many1(parse_sasl_mechanism), tag("\x00"))(i)
}

fn parse_error_response_code(
    i: &[u8],
) -> IResult<&[u8], PgsqlErrorNoticeMessageField, PgsqlParseError<&[u8]>> {
    let (i, _field_type) = char('C')(i)?;
    let (i, field_value) = map_parser(take(6_usize), alphanumeric1)(i)?;
    Ok((
        i,
        PgsqlErrorNoticeMessageField {
            field_type: PgsqlErrorNoticeFieldType::CodeSqlStateCode,
            field_value: field_value.to_vec(),
        },
    ))
}

// Parse an error response with non-localizeable severity message.
// Possible values: ERROR, FATAL, or PANIC
fn parse_error_response_severity(
    i: &[u8],
) -> IResult<&[u8], PgsqlErrorNoticeMessageField, PgsqlParseError<&[u8]>> {
    let (i, field_type) = char('V')(i)?;
    let (i, field_value) = alt((tag("ERROR"), tag("FATAL"), tag("PANIC")))(i)?;
    let (i, _) = tag("\x00")(i)?;
    Ok((
        i,
        PgsqlErrorNoticeMessageField {
            field_type: PgsqlErrorNoticeFieldType::from(field_type),
            field_value: field_value.to_vec(),
        },
    ))
}

// The non-localizable version of Severity field has different values,
// in case of a notice: 'WARNING', 'NOTICE', 'DEBUG', 'INFO' or 'LOG'
fn parse_notice_response_severity(
    i: &[u8],
) -> IResult<&[u8], PgsqlErrorNoticeMessageField, PgsqlParseError<&[u8]>> {
    let (i, field_type) = char('V')(i)?;
    let (i, field_value) = alt((
        tag("WARNING"),
        tag("NOTICE"),
        tag("DEBUG"),
        tag("INFO"),
        tag("LOG"),
    ))(i)?;
    let (i, _) = tag("\x00")(i)?;
    Ok((
        i,
        PgsqlErrorNoticeMessageField {
            field_type: PgsqlErrorNoticeFieldType::from(field_type),
            field_value: field_value.to_vec(),
        },
    ))
}

fn parse_error_response_field(
    i: &[u8], is_err_msg: bool,
) -> IResult<&[u8], PgsqlErrorNoticeMessageField, PgsqlParseError<&[u8]>> {
    let (i, field_type) = peek(be_u8)(i)?;
    let (i, data) = match field_type {
        b'V' => {
            if is_err_msg {
                parse_error_response_severity(i)?
            } else {
                parse_notice_response_severity(i)?
            }
        }
        b'C' => parse_error_response_code(i)?,
        _ => {
            let (i, field_type) = be_u8(i)?;
            let (i, field_value) = take_until("\x00")(i)?;
            let (i, _just_tag) = tag("\x00")(i)?;
            let message = PgsqlErrorNoticeMessageField {
                field_type: PgsqlErrorNoticeFieldType::from(field_type),
                field_value: field_value.to_vec(),
            };
            return Ok((i, message));
        }
    };
    Ok((i, data))
}

fn parse_error_notice_fields(
    i: &[u8], is_err_msg: bool,
) -> IResult<&[u8], Vec<PgsqlErrorNoticeMessageField>, PgsqlParseError<&[u8]>> {
    let (i, data) = many_till(|b| parse_error_response_field(b, is_err_msg), tag("\x00"))(i)?;
    Ok((i, data.0))
}

fn pgsql_parse_error_response(i: &[u8]) -> IResult<&[u8], PgsqlBEMessage, PgsqlParseError<&[u8]>> {
    let (i, identifier) = verify(be_u8, |&x| x == b'E')(i)?;
    let (i, length) = parse_gte_length(i, 11)?;
    let (i, message_body) = map_parser(take(length - PGSQL_LENGTH_FIELD), |b| {
        parse_error_notice_fields(b, true)
    })(i)?;

    Ok((
        i,
        PgsqlBEMessage::ErrorResponse(ErrorNoticeMessage {
            identifier,
            length,
            message_body,
        }),
    ))
}

fn pgsql_parse_notice_response(i: &[u8]) -> IResult<&[u8], PgsqlBEMessage, PgsqlParseError<&[u8]>> {
    let (i, identifier) = verify(be_u8, |&x| x == b'N')(i)?;
    let (i, length) = parse_gte_length(i, 11)?;
    let (i, message_body) = map_parser(take(length - PGSQL_LENGTH_FIELD), |b| {
        parse_error_notice_fields(b, false)
    })(i)?;
    Ok((
        i,
        PgsqlBEMessage::NoticeResponse(ErrorNoticeMessage {
            identifier,
            length,
            message_body,
        }),
    ))
}

fn parse_notification_response(i: &[u8]) -> IResult<&[u8], PgsqlBEMessage, PgsqlParseError<&[u8]>> {
    let (i, identifier) = verify(be_u8, |&x| x == b'A')(i)?;
    // length (u32) + pid (u32) + at least one byte, for we have two str fields
    let (i, length) = parse_gte_length(i, 10)?;
    let (i, data) = map_parser(take(length - PGSQL_LENGTH_FIELD), |b| {
        let (b, pid) = be_u32(b)?;
        let (b, channel_name) = take_until_and_consume(b"\x00")(b)?;
        let (b, payload) = take_until_and_consume(b"\x00")(b)?;
        Ok((b, (pid, channel_name, payload)))
    })(i)?;
    let msg = PgsqlBEMessage::NotificationResponse(NotificationResponse {
        identifier,
        length,
        pid: data.0,
        channel_name: data.1.to_vec(),
        payload: data.2.to_vec(),
    });
    Ok((i, msg))
}

pub(crate) fn pgsql_parse_response(i: &[u8]) -> IResult<&[u8], PgsqlBEMessage, PgsqlParseError<&[u8]>> {
    let (i, tag) = peek(be_u8)(i)?;
    let (i, message) = match tag {
        b'E' => pgsql_parse_error_response(i)?,
        b'K' => parse_backend_key_data_message(i)?,
        b'N' => pgsql_parse_notice_response(i)?,
        b'R' => pgsql_parse_authentication_message(i)?,
        b'S' => parse_parameter_status_message(i)?,
        b'C' => parse_command_complete(i)?,
        b'c' => parse_copy_out_done(i)?,
        b'Z' => parse_ready_for_query(i)?,
        b'T' => parse_row_description(i)?,
        b'A' => parse_notification_response(i)?,
        b'D' => parse_consolidated_data_row(i)?,
        b'd' => parse_consolidated_copy_data_out(i)?,
        b'H' => parse_copy_out_response(i)?,
        b'G' => parse_copy_in_response(i)?,
        _ => {
            let (i, identifier) = be_u8(i)?;
            let (i, length) = parse_gte_length(i, PGSQL_LENGTH_FIELD)?;
            let (i, payload) = take(length - PGSQL_LENGTH_FIELD)(i)?;
            let unknown = PgsqlBEMessage::UnknownMessageType(RegularPacket {
                identifier,
                length,
                payload: payload.to_vec(),
            });
            SCLogDebug!("parsed unknown BE message, identifier is {:?}", identifier);
            (i, unknown)
        }
    };
    Ok((i, message))
}

#[cfg(test)]
mod tests {

    use super::*;
    use nom7::Needed;

    impl ErrorNoticeMessage {
        fn new(identifier: u8, length: u32) -> Self {
            ErrorNoticeMessage {
                identifier,
                length,
                message_body: Vec::new(),
            }
        }
    }

    #[test]
    fn test_parse_request() {
        // An SSLRequest
        let buf: &[u8] = &[0x00, 0x00, 0x00, 0x08, 0x04, 0xd2, 0x16, 0x2f];
        let ssl_request = DummyStartupPacket {
            length: 8,
            proto_major: PGSQL_DUMMY_PROTO_MAJOR,
            proto_minor: PGSQL_DUMMY_PROTO_MINOR_SSL,
        };
        let request_ok = PgsqlFEMessage::SSLRequest(ssl_request);

        let (_remainder, result) = parse_request(buf).unwrap();
        assert_eq!(result, request_ok);

        // incomplete message
        let result = parse_request(&buf[0..7]);
        assert!(result.is_err());

        // Same request, but length is wrong
        let buf: &[u8] = &[0x00, 0x00, 0x00, 0x07, 0x04, 0xd2, 0x16, 0x2f];
        let result = parse_request(buf);
        assert!(result.is_err());

        let buf: &[u8] = &[
            /* Length 85 */ 0x00, 0x00, 0x00, 0x55, /* Proto version */ 0x00, 0x03, 0x00,
            0x00, /* user */ 0x75, 0x73, 0x65, 0x72, 0x00, /* [value] rep */ 0x72, 0x65,
            0x70, 0x00, /* database */ 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x00,
            /* [optional] */ 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63,
            /* replication replication true application_name walreceiver */
            0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74,
            0x69, 0x6f, 0x6e, 0x00, 0x74, 0x72, 0x75, 0x65, 0x00, 0x61, 0x70, 0x70, 0x6c, 0x69,
            0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x77, 0x61,
            0x6c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x00, 0x00,
        ];
        let result = parse_request(buf);
        match result {
            Ok((remainder, _message)) => {
                // there should be nothing left
                assert_eq!(remainder.len(), 0);
            }
            Err(Err::Error(err)) => {
                panic!("Result should not be an error: {:?}.", err);
            }
            Err(Err::Incomplete(_)) => {
                panic!("Result should not have been incomplete.");
            }
            _ => {
                panic!("Unexpected behavior!");
            }
        }

        // A valid startup message/request without optional parameters
        // ...&....user.oryx.database.mailstore..
        let buf: &[u8] = &[
            0x00, 0x00, 0x00, 0x26, 0x00, 0x03, 0x00, 0x00, 0x75, 0x73, 0x65, 0x72, 0x00, 0x6f,
            0x72, 0x79, 0x78, 0x00, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x00, 0x6d,
            0x61, 0x69, 0x6c, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x00, 0x00,
        ];
        let user = PgsqlParameter {
            name: PgsqlParameters::User,
            value: br#"oryx"#.to_vec(),
        };
        let database = PgsqlParameter {
            name: PgsqlParameters::Database,
            value: br#"mailstore"#.to_vec(),
        };
        let database_param: Vec<PgsqlParameter> = vec![database];
        let params = PgsqlStartupParameters {
            user,
            optional_params: Some(database_param),
        };
        let expected_result = PgsqlFEMessage::StartupMessage(StartupPacket {
            length: 38,
            proto_major: 3,
            proto_minor: 0,
            params,
        });
        let result = parse_request(buf);
        match result {
            Ok((remainder, message)) => {
                assert_eq!(message, expected_result);
                assert_eq!(remainder.len(), 0);
            }
            Err(Err::Error(err)) => {
                panic!("Shouldn't be error: {:?}", err);
            }
            Err(Err::Incomplete(needed)) => {
                panic!("Should not be Incomplete! Needed: {:?}", needed);
            }
            _ => {
                panic!("Unexpected behavior");
            }
        }

        // A valid startup message/request without any optional parameters
        // ........user.oryx..
        let buf: &[u8] = &[
            0x00, 0x00, 0x00, 0x13, 0x00, 0x03, 0x00, 0x00, 0x75, 0x73, 0x65, 0x72, 0x00, 0x6f,
            0x72, 0x79, 0x78, 0x00, 0x00,
        ];
        let user = PgsqlParameter {
            name: PgsqlParameters::User,
            value: br#"oryx"#.to_vec(),
        };
        let params = PgsqlStartupParameters {
            user,
            optional_params: None,
        };
        let expected_result = PgsqlFEMessage::StartupMessage(StartupPacket {
            length: 19,
            proto_major: 3,
            proto_minor: 0,
            params,
        });
        let result = parse_request(buf);
        match result {
            Ok((remainder, message)) => {
                assert_eq!(message, expected_result);
                assert_eq!(remainder.len(), 0);
            }
            Err(Err::Error(err)) => {
                panic!("Shouldn't be error: {:?}", err);
            }
            Err(Err::Incomplete(needed)) => {
                panic!("Should not be Incomplete! Needed: {:?}", needed);
            }
            _ => {
                panic!("Unexpected behavior");
            }
        }

        // A startup message/request with length off by one
        let buf: &[u8] = &[
            0x00, 0x00, 0x00, 0x12, 0x00, 0x03, 0x00, 0x00, 0x75, 0x73, 0x65, 0x72, 0x00, 0x6f,
            0x72, 0x79, 0x78, 0x00, 0x00,
        ];
        let result = parse_request(buf);
        assert!(result.is_err());

        // A startup message/request with bad length
        let buf: &[u8] = &[
            0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x00, 0x00, 0x75, 0x73, 0x65, 0x72, 0x00, 0x6f,
            0x72, 0x79, 0x78, 0x00, 0x00,
        ];
        let result = parse_request(buf);
        assert!(result.is_err());

        // A startup message/request with corrupted user param
        let buf: &[u8] = &[
            0x00, 0x00, 0x00, 0x013, 0x00, 0x03, 0x00, 0x00, 0x75, 0x73, 0x65, 0x00, 0x6f, 0x72,
            0x79, 0x78, 0x00, 0x00,
        ];
        let result = parse_request(buf);
        assert!(result.is_err());

        // A startup message/request missing the terminator
        let buf: &[u8] = &[
            0x00, 0x00, 0x00, 0x013, 0x00, 0x03, 0x00, 0x00, 0x75, 0x73, 0x65, 0x72, 0x00, 0x6f,
            0x72, 0x79, 0x78, 0x00,
        ];
        let result = parse_request(buf);
        assert!(result.is_err());

        // A termination message
        let buf: &[u8] = &[0x58, 0x00, 0x00, 0x00, 0x04];
        let result = parse_request(buf);
        assert!(result.is_ok());

        let result = parse_request(&buf[0..3]);
        assert!(result.is_err());
    }

    #[test]
    fn test_cancel_request_message() {
        // A cancel request message
        let buf: &[u8] = &[
            0x00, 0x00, 0x00, 0x10, // length: 16 (fixed)
            0x04, 0xd2, 0x16, 0x2e, // 1234.5678 - identifies a cancel request
            0x00, 0x00, 0x76, 0x31, // PID: 30257
            0x23, 0x84, 0xf7, 0x2d,
        ]; // Backend key: 595916589
        let result = parse_cancel_request(buf);
        assert!(result.is_ok());

        let result = parse_cancel_request(&buf[0..3]);
        assert!(result.is_err());

        let result = pgsql_parse_startup_packet(buf);
        assert!(result.is_ok());

        let fail_result = pgsql_parse_startup_packet(&buf[0..3]);
        assert!(fail_result.is_err());

        let result = parse_request(buf);
        assert!(result.is_ok());

        let fail_result = parse_request(&buf[0..3]);
        assert!(fail_result.is_err());
    }

    #[test]
    fn test_parse_error_response_code() {
        let buf: &[u8] = &[0x43, 0x32, 0x38, 0x30, 0x30, 0x30, 0x00];
        let value_str = "28000".as_bytes();
        let ok_res = PgsqlErrorNoticeMessageField {
            field_type: PgsqlErrorNoticeFieldType::CodeSqlStateCode,
            field_value: value_str.to_vec(),
        };
        let result = parse_error_response_code(buf);
        assert!(result.is_ok());

        let (remainder, result) = parse_error_response_code(buf).unwrap();
        assert_eq!(result, ok_res);
        assert_eq!(remainder.len(), 0);

        let result = parse_error_response_code(&buf[0..5]);
        assert!(result.is_err());
    }

    #[test]
    fn test_parse_password_messages() {
        // A password message (MD5)
        let buf: &[u8] = &[
            0x70, 0x00, 0x00, 0x00, 0x28, 0x6d, 0x64, 0x35, 0x63, 0x65, 0x66, 0x66, 0x63, 0x30,
            0x31, 0x64, 0x63, 0x64, 0x65, 0x37, 0x35, 0x34, 0x31, 0x38, 0x32, 0x39, 0x64, 0x65,
            0x65, 0x66, 0x36, 0x62, 0x35, 0x65, 0x39, 0x63, 0x39, 0x31, 0x34, 0x32, 0x00,
        ];
        let ok_result = PgsqlFEMessage::PasswordMessage(RegularPacket {
            identifier: b'p',
            length: 40,
            payload: br#"md5ceffc01dcde7541829deef6b5e9c9142"#.to_vec(),
        });
        let (_remainder, result) = parse_password_message(buf).unwrap();
        assert_eq!(result, ok_result);

        // Length is off by one here
        let buf: &[u8] = &[
            0x70, 0x00, 0x00, 0x00, 0x27, 0x6d, 0x64, 0x35, 0x63, 0x65, 0x66, 0x66, 0x63, 0x30,
            0x31, 0x64, 0x63, 0x64, 0x65, 0x37, 0x35, 0x34, 0x31, 0x38, 0x32, 0x39, 0x64, 0x65,
            0x65, 0x66, 0x36, 0x62, 0x35, 0x65, 0x39, 0x63, 0x39, 0x31, 0x34, 0x32, 0x00,
        ];
        let result = parse_password_message(buf);
        assert!(result.is_err());

        // Length also off by one, but now bigger than it should
        let buf: &[u8] = &[
            0x70, 0x00, 0x00, 0x00, 0x29, 0x6d, 0x64, 0x35, 0x63, 0x65, 0x66, 0x66, 0x63, 0x30,
            0x31, 0x64, 0x63, 0x64, 0x65, 0x37, 0x35, 0x34, 0x31, 0x38, 0x32, 0x39, 0x64, 0x65,
            0x65, 0x66, 0x36, 0x62, 0x35, 0x65, 0x39, 0x63, 0x39, 0x31, 0x34, 0x32, 0x00,
        ];
        let result = parse_password_message(buf);
        assert!(result.is_err());

        // Incomplete payload
        let buf: &[u8] = &[
            0x70, 0x00, 0x00, 0x00, 0x28, 0x6d, 0x64, 0x35, 0x63, 0x65, 0x66, 0x66, 0x63, 0x30,
            0x31, 0x64, 0x63, 0x64, 0x65, 0x37, 0x35, 0x34, 0x31, 0x38, 0x32, 0x39, 0x64, 0x65,
            0x65, 0x66, 0x36, 0x62, 0x35, 0x65, 0x39, 0x63, 0x39, 0x31, 0x34, 0x32,
        ];
        let result = parse_password_message(buf);
        assert!(result.is_err());
    }

    #[test]
    fn test_parse_error_response_field() {
        // VFATAL
        let input: &[u8] = &[0x56, 0x46, 0x41, 0x54, 0x41, 0x4c, 0x00];

        let value_str = "FATAL".as_bytes();
        let ok_res = PgsqlErrorNoticeMessageField {
            field_type: PgsqlErrorNoticeFieldType::SeverityNonLocalizable,
            field_value: value_str.to_vec(),
        };

        let (remainder, result) = parse_error_response_field(input, true).unwrap();
        assert_eq!(result, ok_res);
        assert_eq!(remainder.len(), 0);

        // "Mno pg_hba.conf entry for replication connection from host "192.168.50.11", user "rep", SSL off "
        let input: &[u8] = &[
            0x4d, 0x6e, 0x6f, 0x20, 0x70, 0x67, 0x5f, 0x68, 0x62, 0x61, 0x2e, 0x63, 0x6f, 0x6e,
            0x66, 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x72, 0x65,
            0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x6e, 0x6e,
            0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x68, 0x6f,
            0x73, 0x74, 0x20, 0x22, 0x31, 0x39, 0x32, 0x2e, 0x31, 0x36, 0x38, 0x2e, 0x35, 0x30,
            0x2e, 0x31, 0x31, 0x22, 0x2c, 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x22, 0x72, 0x65,
            0x70, 0x22, 0x2c, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x6f, 0x66, 0x66, 0x00,
        ];

        let value_str = br#"no pg_hba.conf entry for replication connection from host "192.168.50.11", user "rep", SSL off"#;
        let ok_res = PgsqlErrorNoticeMessageField {
            field_type: PgsqlErrorNoticeFieldType::Message,
            field_value: value_str.to_vec(),
        };

        let (remainder, result) = parse_error_response_field(input, true).unwrap();
        assert_eq!(result, ok_res);
        assert_eq!(remainder.len(), 0);

        // if incomplete, here we should get an error
        let result = parse_error_response_field(&input[0..12], true);
        assert!(result.is_err());

        // C28000
        let input: &[u8] = &[0x43, 0x32, 0x38, 0x30, 0x30, 0x30, 0x00];
        let value_str = "28000".as_bytes();
        let ok_res = PgsqlErrorNoticeMessageField {
            field_type: PgsqlErrorNoticeFieldType::CodeSqlStateCode,
            field_value: value_str.to_vec(),
        };
        let (remainder, result) = parse_error_response_field(input, true).unwrap();
        assert_eq!(result, ok_res);
        assert_eq!(remainder.len(), 0);
    }

    // After sending AuthenticationOk, the backend will send a series of messages with parameters, a backend key message, and finally a ready for query message
    #[test]
    fn test_parse_startup_phase_wrapup() {
        // S   .application_name psql
        let buf: &[u8] = &[
            0x53, 0x00, 0x00, 0x00, 0x1a, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69,
            0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x70, 0x73, 0x71, 0x6c, 0x00,
        ];

        let ok_res = PgsqlBEMessage::ParameterStatus(ParameterStatusMessage {
            identifier: b'S',
            length: 26,
            param: PgsqlParameter {
                name: PgsqlParameters::ApplicationName,
                value: br#"psql"#.to_vec(),
            },
        });

        let (_remainder, result) = parse_parameter_status_message(buf).unwrap();
        assert_eq!(result, ok_res);

        let result = pgsql_parse_response(buf);
        match result {
            Ok((_remainder, message)) => {
                assert_eq!(message, ok_res);
            }
            Err(Err::Error(err)) => {
                panic!("Shouldn't be err {:?}, expected Ok(_).", err);
            }
            Err(Err::Incomplete(needed)) => {
                panic!("Should not be incomplete {:?}, expected Ok(_)", needed);
            }
            _ => panic!("Unexpected behavior, expected Ok(_)"),
        }

        // S   .integer_datetimes on
        let buf: &[u8] = &[
            0x53, 0x00, 0x00, 0x00, 0x19, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x5f, 0x64,
            0x61, 0x74, 0x65, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x00, 0x6f, 0x6e, 0x00,
        ];
        let result = parse_parameter_status_message(buf);
        assert!(result.is_ok());

        // K       =.... // PID 61 Key 3152142766
        let buf: &[u8] = &[
            0x4b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3d, 0xbb, 0xe1, 0xe1, 0xae,
        ];

        let result = parse_backend_key_data_message(buf);
        assert!(result.is_ok());

        // Z   .I
        let buf: &[u8] = &[0x5a, 0x00, 0x00, 0x00, 0x05, 0x49];
        let result = parse_ready_for_query(buf);
        assert!(result.is_ok());
    }

    #[test]
    fn test_parse_error_notice_fields() {
        let input: &[u8] = &[0x53, 0x46, 0x41, 0x54, 0x41, 0x4c, 0x00, 0x00];

        let field1 = PgsqlErrorNoticeMessageField {
            field_type: PgsqlErrorNoticeFieldType::SeverityLocalizable,
            field_value: br#"FATAL"#.to_vec(),
        };
        let field2 = PgsqlErrorNoticeMessageField {
            field_type: PgsqlErrorNoticeFieldType::CodeSqlStateCode,
            field_value: br#"28000"#.to_vec(),
        };
        let field3 = PgsqlErrorNoticeMessageField{
            field_type: PgsqlErrorNoticeFieldType::Message,
            field_value: br#"no pg_hba.conf entry for replication connection from host "192.168.50.11", user "rep", SSL off"#.to_vec(),
        };
        // let field4 = PgsqlErrorNoticeMessageField {
        //     field_type: PgsqlErrorNoticeFieldType::TerminatorToken,
        //     field_value: br#""#.to_vec(),
        // };

        let mut ok_res: Vec<PgsqlErrorNoticeMessageField> = Vec::new();
        ok_res.push(field1);
        // ok_res.push(field4);

        let (remainder, result) = parse_error_notice_fields(input, true).unwrap();
        assert_eq!(result, ok_res);
        assert_eq!(remainder.len(), 0);
        // ok_res.pop();

        ok_res.push(field2);
        ok_res.push(field3);

        // let field4 = PgsqlErrorNoticeMessageField {
        //     field_type: PgsqlErrorNoticeFieldType::TerminatorToken,
        //     field_value: br#""#.to_vec(),
        // };

        // ok_res.push(field4);

        let input: &[u8] = &[
            0x53, 0x46, 0x41, 0x54, 0x41, 0x4c, 0x00, 0x43, 0x32, 0x38, 0x30, 0x30, 0x30, 0x00,
            0x4d, 0x6e, 0x6f, 0x20, 0x70, 0x67, 0x5f, 0x68, 0x62, 0x61, 0x2e, 0x63, 0x6f, 0x6e,
            0x66, 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x72, 0x65,
            0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x6e, 0x6e,
            0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x68, 0x6f,
            0x73, 0x74, 0x20, 0x22, 0x31, 0x39, 0x32, 0x2e, 0x31, 0x36, 0x38, 0x2e, 0x35, 0x30,
            0x2e, 0x31, 0x31, 0x22, 0x2c, 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x22, 0x72, 0x65,
            0x70, 0x22, 0x2c, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x6f, 0x66, 0x66, 0x00, 0x00,
        ];

        let (remainder, result) = parse_error_notice_fields(input, true).unwrap();
        assert_eq!(result, ok_res);
        assert_eq!(remainder.len(), 0);

        let input: &[u8] = &[
            0x53, 0x46, 0x41, 0x54, 0x41, 0x4c, 0x00, 0x43, 0x32, 0x38, 0x30, 0x30, 0x30, 0x00,
            0x4d, 0x6e, 0x6f, 0x20, 0x70, 0x67, 0x5f, 0x68, 0x62, 0x61, 0x2e, 0x63, 0x6f, 0x6e,
            0x66, 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x72, 0x65,
            0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x6e, 0x6e,
            0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x68, 0x6f,
            0x73, 0x74, 0x20, 0x22, 0x31, 0x39, 0x32, 0x2e, 0x31, 0x36, 0x38, 0x2e, 0x35, 0x30,
            0x2e, 0x31, 0x31, 0x22, 0x2c, 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x22, 0x72, 0x65,
            0x70, 0x22, 0x2c, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x6f, 0x66, 0x66, 0x00, 0x46, 0x61,
            0x75, 0x74, 0x68, 0x2e, 0x63, 0x00, 0x4c, 0x34, 0x38, 0x31, 0x00, 0x52, 0x43, 0x6c,
            0x69, 0x65, 0x6e, 0x74, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61,
            0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00,
        ];

        let result = parse_error_notice_fields(input, true);
        assert!(result.is_ok());

        let result = parse_error_notice_fields(&input[0..12], true);
        match result {
            Ok((_remainder, _message)) => panic!("Result should not be ok, but incomplete."),

            Err(Err::Error(err)) => {
                panic!("Shouldn't be error: {:?}", err);
            }
            Err(Err::Incomplete(needed)) => {
                assert_eq!(needed, Needed::new(2));
            }
            _ => panic!("Unexpected behavior."),
        }
    }

    #[test]
    fn test_parse_error_notice_response() {
        // test case buffer
        let buf: &[u8] = &[
            /* identifier */ 0x45, /* length */ 0x00, 0x00, 0x00, 0x96,
            /* Severity */ 0x53, 0x46, 0x41, 0x54, 0x41, 0x4c, 0x00, /* Code */ 0x43,
            0x32, 0x38, 0x30, 0x30, 0x30, 0x00, /* Message */ 0x4d, 0x6e, 0x6f, 0x20, 0x70,
            0x67, 0x5f, 0x68, 0x62, 0x61, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x20, 0x65, 0x6e, 0x74,
            0x72, 0x79, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61,
            0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f,
            0x6e, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x68, 0x6f, 0x73, 0x74, 0x20, 0x22, 0x31,
            0x39, 0x32, 0x2e, 0x31, 0x36, 0x38, 0x2e, 0x35, 0x30, 0x2e, 0x31, 0x31, 0x22, 0x2c,
            0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x22, 0x72, 0x65, 0x70, 0x22, 0x2c, 0x20, 0x53,
            0x53, 0x4c, 0x20, 0x6f, 0x66, 0x66, 0x00, /* File */ 0x46, 0x61, 0x75, 0x74, 0x68,
            0x2e, 0x63, 0x00, /* Line */ 0x4c, 0x34, 0x38, 0x31, 0x00,
            /* Routine */ 0x52, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x41, 0x75, 0x74, 0x68,
            0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00,
        ];

        // expected result
        let field1 = PgsqlErrorNoticeMessageField {
            field_type: PgsqlErrorNoticeFieldType::SeverityLocalizable,
            field_value: br#"FATAL"#.to_vec(),
        };
        let field2 = PgsqlErrorNoticeMessageField {
            field_type: PgsqlErrorNoticeFieldType::CodeSqlStateCode,
            field_value: br#"28000"#.to_vec(),
        };
        let field3 = PgsqlErrorNoticeMessageField {
            field_type: PgsqlErrorNoticeFieldType::Message,
            field_value: br#"no pg_hba.conf entry for replication connection from host "192.168.50.11", user "rep", SSL off"#.to_vec(),
        };
        let field4 = PgsqlErrorNoticeMessageField {
            field_type: PgsqlErrorNoticeFieldType::File,
            field_value: br#"auth.c"#.to_vec(),
        };
        let field5 = PgsqlErrorNoticeMessageField {
            field_type: PgsqlErrorNoticeFieldType::Line,
            field_value: br#"481"#.to_vec(),
        };
        let field6 = PgsqlErrorNoticeMessageField {
            field_type: PgsqlErrorNoticeFieldType::Routine,
            field_value: br#"ClientAuthentication"#.to_vec(),
        };

        let mut payload = ErrorNoticeMessage::new(b'E', 150);
        payload.message_body.push(field1);
        payload.message_body.push(field2);
        payload.message_body.push(field3);
        payload.message_body.push(field4);
        payload.message_body.push(field5);
        payload.message_body.push(field6);

        let ok_res = PgsqlBEMessage::ErrorResponse(payload);

        let result = pgsql_parse_response(buf);
        match result {
            Ok((remainder, message)) => {
                assert_eq!(message, ok_res);
                assert_eq!(remainder.len(), 0);
            }
            Err(Err::Error(err)) => {
                panic!("Shouldn't be error: {:?}", err);
            }
            Err(Err::Incomplete(needed)) => {
                panic!("Should not be Incomplete! Needed: {:?}", needed);
            }
            _ => {
                panic!("Unexpected behavior");
            }
        }

        let result_incomplete = pgsql_parse_response(&buf[0..22]);
        match result_incomplete {
            Err(Err::Incomplete(needed)) => {
                // parser first tries to take whole message (length + identifier = 151), but buffer is incomplete
                assert_eq!(needed, Needed::new(129));
            }
            _ => {
                panic!("Unexpected behavior. Should be incomplete.");
            }
        }

        //repeat for different case-scenarios:
        // - message is valid
        // some invalid character
    }

    #[test]
    fn test_parse_notice_response() {
        // N   .SDEBUG VDEBUG C23505 Mduplicate key value violates unique constraint "unique_a" DKey (a)=(mea5) already exists. Fnbtinsert.c L397 R_bt_check_unique
        let buf: &[u8] = &[
            0x4e, 0x00, 0x00, 0x00, 0x99, 0x53, 0x44, 0x45, 0x42, 0x55, 0x47, 0x00, 0x56, 0x44,
            0x45, 0x42, 0x55, 0x47, 0x00, 0x43, 0x32, 0x33, 0x35, 0x30, 0x35, 0x00, 0x4d, 0x64,
            0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x76,
            0x61, 0x6c, 0x75, 0x65, 0x20, 0x76, 0x69, 0x6f, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x20,
            0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61,
            0x69, 0x6e, 0x74, 0x20, 0x22, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x5f, 0x61, 0x22,
            0x00, 0x44, 0x4b, 0x65, 0x79, 0x20, 0x28, 0x61, 0x29, 0x3d, 0x28, 0x6d, 0x65, 0x61,
            0x35, 0x29, 0x20, 0x61, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x20, 0x65, 0x78, 0x69,
            0x73, 0x74, 0x73, 0x2e, 0x00, 0x46, 0x6e, 0x62, 0x74, 0x69, 0x6e, 0x73, 0x65, 0x72,
            0x74, 0x2e, 0x63, 0x00, 0x4c, 0x33, 0x39, 0x37, 0x00, 0x52, 0x5f, 0x62, 0x74, 0x5f,
            0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x00, 0x00,
        ];

        // expected result
        let field1 = PgsqlErrorNoticeMessageField {
            field_type: PgsqlErrorNoticeFieldType::SeverityLocalizable,
            field_value: br#"DEBUG"#.to_vec(),
        };
        let field2 = PgsqlErrorNoticeMessageField {
            field_type: PgsqlErrorNoticeFieldType::SeverityNonLocalizable,
            field_value: br#"DEBUG"#.to_vec(),
        };
        let field3 = PgsqlErrorNoticeMessageField {
            field_type: PgsqlErrorNoticeFieldType::CodeSqlStateCode,
            field_value: br#"23505"#.to_vec(),
        };
        let field4 = PgsqlErrorNoticeMessageField {
            field_type: PgsqlErrorNoticeFieldType::Message,
            field_value: br#"duplicate key value violates unique constraint "unique_a""#.to_vec(),
        };
        let field5 = PgsqlErrorNoticeMessageField {
            field_type: PgsqlErrorNoticeFieldType::Detail,
            field_value: br#"Key (a)=(mea5) already exists."#.to_vec(),
        };
        let field6 = PgsqlErrorNoticeMessageField {
            field_type: PgsqlErrorNoticeFieldType::File,
            field_value: br#"nbtinsert.c"#.to_vec(),
        };
        let field7 = PgsqlErrorNoticeMessageField {
            field_type: PgsqlErrorNoticeFieldType::Line,
            field_value: br#"397"#.to_vec(),
        };
        let field8 = PgsqlErrorNoticeMessageField {
            field_type: PgsqlErrorNoticeFieldType::Routine,
            field_value: br#"_bt_check_unique"#.to_vec(),
        };

        let mut payload = ErrorNoticeMessage::new(b'N', 153);
        payload.message_body.push(field1);
        payload.message_body.push(field2);
        payload.message_body.push(field3);
        payload.message_body.push(field4);
        payload.message_body.push(field5);
        payload.message_body.push(field6);
        payload.message_body.push(field7);
        payload.message_body.push(field8);

        let ok_res = PgsqlBEMessage::NoticeResponse(payload);

        let result = pgsql_parse_response(buf);
        match result {
            Ok((remainder, message)) => {
                assert_eq!(message, ok_res);
                assert_eq!(remainder.len(), 0);
            }
            Err(Err::Error(err)) => {
                panic!("Shouldn't be error: {:?}", err);
            }
            Err(Err::Incomplete(needed)) => {
                panic!("Should not be Incomplete! Needed: {:?} ", needed);
            }
            _ => {
                panic!("Unexpected behavior");
            }
        }
    }

    #[test]
    fn test_parse_sasl_authentication_message() {
        let buf: &[u8] = &[
            /* identifier R */ 0x52, /* length 28 */ 0x00, 0x00, 0x00, 0x1c,
            /* auth_type */ 0x00, 0x00, 0x00, 0x0a, /* SCRAM-SHA-256-PLUS */ 0x53, 0x43,
            0x52, 0x41, 0x4d, 0x2d, 0x53, 0x48, 0x41, 0x2d, 0x32, 0x35, 0x36, 0x2d, 0x50, 0x4c,
            0x55, 0x53, 0x00, 0x00,
        ];
        let mechanism = vec![SASLAuthenticationMechanism::ScramSha256Plus];
        let ok_res = PgsqlBEMessage::AuthenticationSASL(AuthenticationSASLMechanismMessage {
            identifier: b'R',
            length: 28,
            auth_type: 10,
            auth_mechanisms: mechanism,
        });

        let result = pgsql_parse_response(buf);
        match result {
            Ok((remainder, message)) => {
                assert_eq!(message, ok_res);
                assert_eq!(remainder.len(), 0);
            }
            Err(Err::Error(err)) => {
                panic!("Shouldn't be error: {:?}", err);
            }
            Err(Err::Incomplete(needed)) => {
                panic!("Should not be Incomplete! Needed: {:?}", needed);
            }
            _ => {
                panic!("Unexpected behavior");
            }
        }

        let buf: &[u8] = &[
            /* identifier R */ 0x52, /* length 42 */ 0x00, 0x00, 0x00, 0x2a,
            /* auth_type */ 0x00, 0x00, 0x00, 0x0a, /* SCRAM-SHA-256-PLUS */ 0x53, 0x43,
            0x52, 0x41, 0x4d, 0x2d, 0x53, 0x48, 0x41, 0x2d, 0x32, 0x35, 0x36, 0x2d, 0x50, 0x4c,
            0x55, 0x53, 0x00, /* SCRAM-SHA-256 */ 0x53, 0x43, 0x52, 0x41, 0x4d, 0x2d, 0x53,
            0x48, 0x41, 0x2d, 0x32, 0x35, 0x36, 0x00, 0x00,
        ];
        let mechanism = vec![
            SASLAuthenticationMechanism::ScramSha256Plus,
            SASLAuthenticationMechanism::ScramSha256,
        ];
        let ok_res = PgsqlBEMessage::AuthenticationSASL(AuthenticationSASLMechanismMessage {
            identifier: b'R',
            length: 42,
            auth_type: 10,
            auth_mechanisms: mechanism,
        });

        let result = pgsql_parse_response(buf);
        match result {
            Ok((remainder, message)) => {
                assert_eq!(message, ok_res);
                assert_eq!(remainder.len(), 0);
            }
            Err(Err::Error(err)) => {
                panic!("Shouldn't be error: {:?}", err);
            }
            Err(Err::Incomplete(needed)) => {
                panic!("Should not be Incomplete! Needed: {:?}", needed);
            }
            _ => {
                panic!("Unexpected behavior");
            }
        }

        let incomplete_result = pgsql_parse_response(&buf[0..27]);
        match incomplete_result {
            Ok((_remainder, _message)) => panic!("Should not be Ok(_), expected Incomplete!"),
            Err(Err::Error(err)) => {
                panic!("Should not be error {:?}, expected Incomplete!", err)
            }
            Err(Err::Incomplete(needed)) => assert_eq!(needed, Needed::new(16)),
            _ => panic!("Unexpected behavior, expected Incomplete."),
        }
    }

    #[test]
    fn test_parse_sasl_continue_authentication_message() {
        // As found in: https://blog.hackeriet.no/Better-password-hashing-in-PostgreSQL/
        let buf: &[u8] = &[
            /* 'R' */ 0x52, /* 92 */ 0x00, 0x00, 0x00, 0x5c, /* 11 */ 0x00, 0x00,
            0x00, 0x0b, /* challenge data*/ 0x72, 0x3d, 0x2f, 0x7a, 0x2b, 0x67, 0x69, 0x5a,
            0x69, 0x54, 0x78, 0x41, 0x48, 0x37, 0x72, 0x38, 0x73, 0x4e, 0x41, 0x65, 0x48, 0x72,
            0x37, 0x63, 0x76, 0x70, 0x71, 0x56, 0x33, 0x75, 0x6f, 0x37, 0x47, 0x2f, 0x62, 0x4a,
            0x42, 0x49, 0x4a, 0x4f, 0x33, 0x70, 0x6a, 0x56, 0x4d, 0x37, 0x74, 0x33, 0x6e, 0x67,
            0x2c, 0x73, 0x3d, 0x34, 0x55, 0x56, 0x36, 0x38, 0x62, 0x49, 0x6b, 0x43, 0x38, 0x66,
            0x39, 0x2f, 0x58, 0x38, 0x78, 0x48, 0x37, 0x61, 0x50, 0x68, 0x67, 0x3d, 0x3d, 0x2c,
            0x69, 0x3d, 0x34, 0x30, 0x39, 0x36,
        ];

        let ok_res = PgsqlBEMessage::AuthenticationSASLContinue(
            AuthenticationMessage {
                identifier: b'R',
                length: 92,
                auth_type: 11,
                payload: br#"r=/z+giZiTxAH7r8sNAeHr7cvpqV3uo7G/bJBIJO3pjVM7t3ng,s=4UV68bIkC8f9/X8xH7aPhg==,i=4096"#.to_vec(),
        });

        let result = pgsql_parse_response(buf);
        match result {
            Ok((remainder, message)) => {
                assert_eq!(message, ok_res);
                assert_eq!(remainder.len(), 0);
            }
            Err(Err::Error(err)) => {
                panic!("Shouldn't be error {:?} expected Ok(_)", err)
            }
            Err(Err::Incomplete(needed)) => {
                panic!("shouldn't be incomplete {:?}, expected Ok(_)", needed)
            }
            _ => panic!("Unexpected behavior, expected Ok(_)"),
        }

        let result_incomplete = pgsql_parse_response(&buf[0..31]);
        match result_incomplete {
            Ok((_remainder, _message)) => panic!("Should not be Ok(_), expected Incomplete!"),
            Err(Err::Error(err)) => {
                panic!("Shouldn't be error {:?} expected Incomplete!", err)
            }
            Err(Err::Incomplete(needed)) => {
                assert_eq!(needed, Needed::new(62));
            }
            _ => panic!("Unexpected behavior, expected Ok(_)"),
        }
    }

    #[test]
    fn test_parse_sasl_final_authentication_message() {
        let buf: &[u8] = &[
            /* R */ 0x52, /* 54 */ 0x00, 0x00, 0x00, 0x36, /* 12 */ 0x00, 0x00,
            0x00, 0x0c, /* signature */ 0x76, 0x3d, 0x64, 0x31, 0x50, 0x58, 0x61, 0x38, 0x54,
            0x4b, 0x46, 0x50, 0x5a, 0x72, 0x52, 0x33, 0x4d, 0x42, 0x52, 0x6a, 0x4c, 0x79, 0x33,
            0x2b, 0x4a, 0x36, 0x79, 0x78, 0x72, 0x66, 0x77, 0x2f, 0x7a, 0x7a, 0x70, 0x38, 0x59,
            0x54, 0x39, 0x65, 0x78, 0x56, 0x37, 0x73, 0x38, 0x3d,
        ];
        let ok_res = PgsqlBEMessage::AuthenticationSASLFinal(AuthenticationMessage {
            identifier: b'R',
            length: 54,
            auth_type: 12,
            payload: br#"v=d1PXa8TKFPZrR3MBRjLy3+J6yxrfw/zzp8YT9exV7s8="#.to_vec(),
        });

        let result = pgsql_parse_response(buf);
        match result {
            Ok((remainder, message)) => {
                assert_eq!(message, ok_res);
                assert_eq!(remainder.len(), 0);
            }
            Err(Err::Error(err)) => {
                panic!("Shouldn't be error {:?}, expected Ok(_)", err);
            }
            Err(Err::Incomplete(needed)) => {
                panic!("Shouldn't be incomplete {:?}, expected OK(_)", needed);
            }
            _ => panic!("Unexpected behavior, expected Ok(_)"),
        }

        let result_incomplete = pgsql_parse_response(&buf[0..34]);
        match result_incomplete {
            Err(Err::Incomplete(needed)) => {
                assert_eq!(needed, Needed::new(21));
            }
            _ => panic!("Unexpected behavior, expected incomplete."),
        }

        let bad_buf: &[u8] = &[
            /* ` */ 0x60, /* 54 */ 0x00, 0x00, 0x00, 0x36, /* 12 */ 0x00, 0x00,
            0x00, 0x0c, /* signature */ 0x76, 0x3d, 0x64, 0x31, 0x50, 0x58, 0x61, 0x38, 0x54,
            0x4b, 0x46, 0x50, 0x5a, 0x72, 0x52, 0x33, 0x4d, 0x42, 0x52, 0x6a, 0x4c, 0x79, 0x33,
            0x2b, 0x4a, 0x36, 0x79, 0x78, 0x72, 0x66, 0x77, 0x2f, 0x7a, 0x7a, 0x70, 0x38, 0x59,
            0x54, 0x39, 0x65, 0x78, 0x56, 0x37, 0x73, 0x38, 0x3d,
        ];
        let (remainder, result) =
            pgsql_parse_response(bad_buf).expect("parsing sasl final response failed");
        let res = PgsqlBEMessage::UnknownMessageType(RegularPacket {
            identifier: b'`',
            length: 54,
            payload: bad_buf[5..].to_vec(),
        });
        assert_eq!(result, res);
        assert!(remainder.is_empty());
    }

    #[test]
    fn test_parse_sasl_frontend_messages() {
        // SASL Initial Response
        // (as seen in https://blog.hackeriet.no/Better-password-hashing-in-PostgreSQL/)
        let buf: &[u8] = &[
            /* p */ 0x70, /* 54 */ 0x00, 0x00, 0x00, 0x36,
            /* sasl mechanism */ 0x53, 0x43, 0x52, 0x41, 0x4d, 0x2d, 0x53, 0x48, 0x41, 0x2d,
            0x32, 0x35, 0x36, 0x00, /* 32 */ 0x00, 0x00, 0x00, 0x20,
            /* FE 1st msg */ 0x6e, 0x2c, 0x2c, 0x6e, 0x3d, 0x2c, 0x72, 0x3d, 0x2f, 0x7a, 0x2b,
            0x67, 0x69, 0x5a, 0x69, 0x54, 0x78, 0x41, 0x48, 0x37, 0x72, 0x38, 0x73, 0x4e, 0x41,
            0x65, 0x48, 0x72, 0x37, 0x63, 0x76, 0x70,
        ];
        let ok_res = PgsqlFEMessage::SASLInitialResponse(SASLInitialResponsePacket {
            identifier: b'p',
            length: 54,
            auth_mechanism: SASLAuthenticationMechanism::ScramSha256,
            param_length: 32,
            sasl_param: br#"n,,n=,r=/z+giZiTxAH7r8sNAeHr7cvp"#.to_vec(),
        });

        let result = parse_sasl_initial_response(buf);
        match result {
            Ok((remainder, message)) => {
                assert_eq!(message, ok_res);
                assert_eq!(remainder.len(), 0);
            }
            Err(Err::Error(err)) => {
                panic!("Shouldn't be error {:?}, expected Ok(_)", err)
            }
            Err(Err::Incomplete(needed)) => {
                panic!("Shouldn't be incomplete: {:?}, expected Ok(_)", needed)
            }
            _ => panic!("Unexpected behavior, expected Ok(_)"),
        }

        let buf: &[u8] = &[
            /* p */ 0x70, /* 108 */ 0x00, 0x00, 0x00, 0x6c, /* final msg*/ 0x63,
            0x3d, 0x62, 0x69, 0x77, 0x73, 0x2c, 0x72, 0x3d, 0x2f, 0x7a, 0x2b, 0x67, 0x69, 0x5a,
            0x69, 0x54, 0x78, 0x41, 0x48, 0x37, 0x72, 0x38, 0x73, 0x4e, 0x41, 0x65, 0x48, 0x72,
            0x37, 0x63, 0x76, 0x70, 0x71, 0x56, 0x33, 0x75, 0x6f, 0x37, 0x47, 0x2f, 0x62, 0x4a,
            0x42, 0x49, 0x4a, 0x4f, 0x33, 0x70, 0x6a, 0x56, 0x4d, 0x37, 0x74, 0x33, 0x6e, 0x67,
            0x2c, 0x70, 0x3d, 0x41, 0x46, 0x70, 0x53, 0x59, 0x48, 0x2f, 0x4b, 0x2f, 0x38, 0x62,
            0x75, 0x78, 0x31, 0x6d, 0x52, 0x50, 0x55, 0x77, 0x78, 0x54, 0x65, 0x38, 0x6c, 0x42,
            0x75, 0x49, 0x50, 0x45, 0x79, 0x68, 0x69, 0x2f, 0x37, 0x55, 0x46, 0x50, 0x51, 0x70,
            0x53, 0x72, 0x34, 0x41, 0x3d,
        ];

        let ok_res = PgsqlFEMessage::SASLResponse(
            RegularPacket {
                identifier: b'p',
                length: 108,
                payload: br#"c=biws,r=/z+giZiTxAH7r8sNAeHr7cvpqV3uo7G/bJBIJO3pjVM7t3ng,p=AFpSYH/K/8bux1mRPUwxTe8lBuIPEyhi/7UFPQpSr4A="#.to_vec(),
            });

        let result = parse_sasl_response(buf);
        match result {
            Ok((_remainder, message)) => {
                assert_eq!(message, ok_res);
            }
            Err(Err::Error(err)) => {
                panic!("Shouldn't be error: {:?} expected Ok(_)", err)
            }
            Err(Err::Incomplete(needed)) => {
                panic!("Shouldn't be incomplete: {:?}, expected Ok(_)", needed)
            }
            _ => panic!("Unexpected behavior, should be Ok(_)"),
        }
    }

    // Test messages with fixed formats, like AuthenticationSSPI
    #[test]
    fn test_parse_simple_authentication_requests() {
        let buf: &[u8] = &[
            /* R */ 0x52, /* 8 */ 0x00, 0x00, 0x00, 0x08, /* 9 */ 0x00, 0x00, 0x00,
            0x09,
        ];

        let ok_res = PgsqlBEMessage::AuthenticationSSPI(AuthenticationMessage {
            identifier: b'R',
            length: 8,
            auth_type: 9,
            payload: Vec::<u8>::new(),
        });

        let (_remainder, result) = pgsql_parse_response(buf).unwrap();
        assert_eq!(result, ok_res);
    }

    #[test]
    fn test_parse_response() {
        // An SSL response - N
        let buf: &[u8] = &[0x4e];
        let response_ok = PgsqlBEMessage::SSLResponse(SSLResponseMessage::SSLRejected);
        let (_remainder, result) = parse_ssl_response(buf).unwrap();
        assert_eq!(result, response_ok);

        // An SSL response - S
        let buf: &[u8] = &[0x53];
        let response_ok = PgsqlBEMessage::SSLResponse(SSLResponseMessage::SSLAccepted);

        let (_remainder, result) = parse_ssl_response(buf).unwrap();
        assert_eq!(result, response_ok);

        // Not an SSL response
        let buf: &[u8] = &[0x52];
        let result = parse_ssl_response(buf);
        assert!(result.is_err());

        // - auth MD5
        let buf: &[u8] = &[
            0x52, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x05, 0xf2, 0x11, 0xa3, 0xed,
        ];
        let ok_res = PgsqlBEMessage::AuthenticationMD5Password(AuthenticationMessage {
            identifier: b'R',
            length: 12,
            auth_type: 5,
            payload: vec![0xf2, 0x11, 0xa3, 0xed],
        });
        let result = pgsql_parse_response(buf);
        match result {
            Ok((remainder, message)) => {
                assert_eq!(message, ok_res);
                assert_eq!(remainder.len(), 0);
            }
            Err(Err::Error(err)) => {
                panic!("Shouldn't be error: {:?}", err);
            }
            Err(Err::Incomplete(needed)) => {
                panic!("Should not be Incomplete! Needed: {:?}", needed);
            }
            _ => {
                panic!("Unexpected behavior");
            }
        }

        // - auth clear text...
        let buf: &[u8] = &[0x52, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03];
        let ok_res = PgsqlBEMessage::AuthenticationCleartextPassword(AuthenticationMessage {
            identifier: b'R',
            length: 8,
            auth_type: 3,
            payload: Vec::<u8>::new(),
        });
        let result = pgsql_parse_response(buf);
        match result {
            Ok((remainder, message)) => {
                assert_eq!(remainder.len(), 0);
                assert_eq!(message, ok_res);
            }
            Err(Err::Error(err)) => {
                panic!("Shouldn't be error: {:?}", err);
            }
            Err(Err::Incomplete(needed)) => {
                panic!("Should not be incomplete. Needed {:?}", needed);
            }
            _ => {
                panic!("Unexpected behavior");
            }
        }

        let result = pgsql_parse_response(&buf[0..6]);
        assert!(result.is_err());

        let buf: &[u8] = &[0x52, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03];
        let result = pgsql_parse_response(buf);
        assert!(result.is_err());

        // - auth Ok
        let buf: &[u8] = &[0x52, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00];
        let ok_res = PgsqlBEMessage::AuthenticationOk(AuthenticationMessage {
            identifier: b'R',
            length: 8,
            auth_type: 0,
            payload: Vec::<u8>::new(),
        });
        let result = pgsql_parse_response(buf);
        match result {
            Ok((remainder, message)) => {
                assert_eq!(message, ok_res);
                assert_eq!(remainder.len(), 0);
            }
            Err(Err::Error(err)) => {
                panic!("Should not be error {:?}", err);
            }
            Err(Err::Incomplete(needed)) => {
                panic!("Should not be incomplete. Needed: {:?}", needed);
            }
            _ => {
                panic!("Unexpected behavior!");
            }
        }

        //A series of response messages from the backend:
        // R   ·    S   ·application_name
        // S   ·client_encoding UTF8 S   ·DateStyle ISO, MDY
        // S   &default_transaction_read_only off S   ·in_hot_standby off
        // S   ·integer_datetimes on S   ·IntervalStyle postgres
        // S   ·is_superuser off S   ·server_encoding UTF8
        // S   ·server_version 14.5 S   "session_authorization ctfpost
        // S   #standard_conforming_strings on S   ·TimeZone Europe/Paris
        // K      ···O··Z   ·I
        let buf = &[
            0x52, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x16,
            0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61,
            0x6d, 0x65, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x19, 0x63, 0x6c, 0x69, 0x65, 0x6e,
            0x74, 0x5f, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00, 0x55, 0x54, 0x46,
            0x38, 0x00, 0x53, 0x00, 0x00, 0x00, 0x17, 0x44, 0x61, 0x74, 0x65, 0x53, 0x74, 0x79,
            0x6c, 0x65, 0x00, 0x49, 0x53, 0x4f, 0x2c, 0x20, 0x4d, 0x44, 0x59, 0x00, 0x53, 0x00,
            0x00, 0x00, 0x26, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x74, 0x72, 0x61,
            0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x5f,
            0x6f, 0x6e, 0x6c, 0x79, 0x00, 0x6f, 0x66, 0x66, 0x00, 0x53, 0x00, 0x00, 0x00, 0x17,
            0x69, 0x6e, 0x5f, 0x68, 0x6f, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x62, 0x79,
            0x00, 0x6f, 0x66, 0x66, 0x00, 0x53, 0x00, 0x00, 0x00, 0x19, 0x69, 0x6e, 0x74, 0x65,
            0x67, 0x65, 0x72, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x00,
            0x6f, 0x6e, 0x00, 0x53, 0x00, 0x00, 0x00, 0x1b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76,
            0x61, 0x6c, 0x53, 0x74, 0x79, 0x6c, 0x65, 0x00, 0x70, 0x6f, 0x73, 0x74, 0x67, 0x72,
            0x65, 0x73, 0x00, 0x53, 0x00, 0x00, 0x00, 0x15, 0x69, 0x73, 0x5f, 0x73, 0x75, 0x70,
            0x65, 0x72, 0x75, 0x73, 0x65, 0x72, 0x00, 0x6f, 0x66, 0x66, 0x00, 0x53, 0x00, 0x00,
            0x00, 0x19, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x65, 0x6e, 0x63, 0x6f, 0x64,
            0x69, 0x6e, 0x67, 0x00, 0x55, 0x54, 0x46, 0x38, 0x00, 0x53, 0x00, 0x00, 0x00, 0x18,
            0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
            0x00, 0x31, 0x34, 0x2e, 0x35, 0x00, 0x53, 0x00, 0x00, 0x00, 0x22, 0x73, 0x65, 0x73,
            0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61,
            0x74, 0x69, 0x6f, 0x6e, 0x00, 0x63, 0x74, 0x66, 0x70, 0x6f, 0x73, 0x74, 0x00, 0x53,
            0x00, 0x00, 0x00, 0x23, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x5f, 0x63,
            0x6f, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x74, 0x72, 0x69,
            0x6e, 0x67, 0x73, 0x00, 0x6f, 0x6e, 0x00, 0x53, 0x00, 0x00, 0x00, 0x1a, 0x54, 0x69,
            0x6d, 0x65, 0x5a, 0x6f, 0x6e, 0x65, 0x00, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f,
            0x50, 0x61, 0x72, 0x69, 0x73, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0b,
            0x8d, 0xcf, 0x4f, 0xb6, 0xcf, 0x5a, 0x00, 0x00, 0x00, 0x05, 0x49,
        ];

        let result = pgsql_parse_response(buf);
        assert!(result.is_ok());
    }

    #[test]
    fn test_parse_row_description() {
        // RowDescription message
        // T  ..
        // source   @  .   .......  version   @  .   .......  sid   @  .   . .....
        let buffer: &[u8] = &[
            0x54, 0x00, 0x00, 0x00, 0x50, 0x00, 0x03, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x00,
            0x00, 0x00, 0x40, 0x09, 0x00, 0x01, 0x00, 0x00, 0x00, 0x19, 0xff, 0xff, 0xff, 0xff,
            0xff, 0xff, 0x00, 0x00, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00,
            0x40, 0x09, 0x00, 0x02, 0x00, 0x00, 0x00, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
            0x00, 0x00, 0x73, 0x69, 0x64, 0x00, 0x00, 0x00, 0x40, 0x09, 0x00, 0x03, 0x00, 0x00,
            0x00, 0x14, 0x00, 0x08, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00,
        ];

        let field1 = RowField {
            field_name: br#"source"#.to_vec(),
            table_oid: 16393,
            column_index: 1,
            data_type_oid: 25,
            data_type_size: -1,
            type_modifier: -1,
            format_code: 0,
        };

        let field2 = RowField {
            field_name: br#"version"#.to_vec(),
            table_oid: 16393,
            column_index: 2,
            data_type_oid: 25,
            data_type_size: -1,
            type_modifier: -1,
            format_code: 0,
        };

        let field3 = RowField {
            field_name: br#"sid"#.to_vec(),
            table_oid: 16393,
            column_index: 3,
            data_type_oid: 20,
            data_type_size: 8,
            type_modifier: -1,
            format_code: 0,
        };

        let fields_vec = vec![field1, field2, field3];

        let ok_res = PgsqlBEMessage::RowDescription(RowDescriptionMessage {
            identifier: b'T',
            length: 80,
            field_count: 3,
            fields: fields_vec,
        });

        let result = parse_row_description(buffer);

        match result {
            Ok((rem, response)) => {
                assert_eq!(response, ok_res);
                assert!(rem.is_empty());
            }
            Err(Err::Incomplete(needed)) => {
                panic!("Should not be Incomplete! Needed: {:?}", needed);
            }
            Err(Err::Error(err)) => {
                panic!("Shouldn't be error: {:?}", err);
            }
            _ => {
                panic!("Unexpected behavior");
            }
        }
    }

    #[test]
    fn test_parse_data_row() {
        let buffer: &[u8] = &[
            0x44, 0x00, 0x00, 0x00, 0x23, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x65, 0x74, 0x2f,
            0x6f, 0x70, 0x65, 0x6e, 0x00, 0x00, 0x00, 0x03, 0x36, 0x2e, 0x30, 0x00, 0x00, 0x00,
            0x07, 0x32, 0x30, 0x32, 0x31, 0x37, 0x30, 0x31,
        ];

        let result = parse_consolidated_data_row(buffer);
        assert!(result.is_ok());
    }

    #[test]
    fn test_command_complete() {
        let buffer: &[u8] = &[
            0x43, 0x00, 0x00, 0x00, 0x0d, 0x53, 0x45, 0x4c, 0x45, 0x43, 0x54, 0x20, 0x33, 0x00,
        ];

        let ok_res = PgsqlBEMessage::CommandComplete(RegularPacket {
            identifier: b'C',
            length: 13,
            payload: b"SELECT 3".to_vec(),
        });

        let result = pgsql_parse_response(buffer);

        match result {
            Ok((rem, message)) => {
                assert_eq!(ok_res, message);
                assert!(rem.is_empty());
            }
            Err(Err::Incomplete(needed)) => {
                panic!(
                    "Shouldn't be Incomplete! Expected Ok(). Needed: {:?}",
                    needed
                );
            }
            Err(Err::Error(err)) => {
                panic!("Shouldn't be Error: {:?}, expected Ok()", err);
            }
            _ => {
                panic!("Unexpected behavior, should be Ok()");
            }
        }
    }

    #[test]
    fn test_parse_notification_response() {
        // Handcrafted notification response message, based on documentation specification
        // identifier: 'A'
        // length: 39
        // pid: 61
        // channel_name: test_channel
        // payload: Test notification
        let buf: &[u8] = &[
            0x41, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x3d, // test_channel
            0x74, 0x65, 0x73, 0x74, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x00,
            // Test notification
            0x54, 0x65, 0x73, 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
            0x69, 0x6f, 0x6e, 0x00,
        ];

        let ok_res = PgsqlBEMessage::NotificationResponse(NotificationResponse {
            identifier: b'A',
            length: 39,
            pid: 61,
            channel_name: br#"test_channel"#.to_vec(),
            payload: br#"Test notification"#.to_vec(),
        });

        let (_rem, result) = pgsql_parse_response(buf).unwrap();

        assert_eq!(ok_res, result);
    }
}
