use crate::cfdp::pdu::{
    add_pdu_crc, generic_length_checks_pdu_deserialization, FileDirectiveType, PduError, PduHeader,
};
use crate::cfdp::tlv::{
    EntityIdTlv, FilestoreResponseTlv, GenericTlv, Tlv, TlvType, TlvTypeField, WritableTlv,
};
use crate::cfdp::{ConditionCode, CrcFlag, Direction, PduType};
use crate::ByteConversionError;
use num_enum::{IntoPrimitive, TryFromPrimitive};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

use super::tlv::ReadableTlv;
use super::{CfdpPdu, InvalidTlvTypeFieldError, WritablePduPacket};

#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum DeliveryCode {
    Complete = 0,
    Incomplete = 1,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum FileStatus {
    DiscardDeliberately = 0b00,
    DiscardedFsRejection = 0b01,
    Retained = 0b10,
    Unreported = 0b11,
}

/// Finished PDU abstraction.
///
/// For more information, refer to CFDP chapter 5.2.3.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct FinishedPduCreator<'fs_responses> {
    pdu_header: PduHeader,
    condition_code: ConditionCode,
    delivery_code: DeliveryCode,
    file_status: FileStatus,
    fs_responses:
        &'fs_responses [FilestoreResponseTlv<'fs_responses, 'fs_responses, 'fs_responses>],
    fault_location: Option<EntityIdTlv>,
}

impl<'fs_responses> FinishedPduCreator<'fs_responses> {
    /// Default finished PDU: No error (no fault location field) and no filestore responses.
    pub fn new_no_error(
        pdu_header: PduHeader,
        delivery_code: DeliveryCode,
        file_status: FileStatus,
    ) -> Self {
        Self::new(
            pdu_header,
            ConditionCode::NoError,
            delivery_code,
            file_status,
            &[],
            None,
        )
    }

    pub fn new_with_error(
        pdu_header: PduHeader,
        condition_code: ConditionCode,
        delivery_code: DeliveryCode,
        file_status: FileStatus,
        fault_location: EntityIdTlv,
    ) -> Self {
        Self::new(
            pdu_header,
            condition_code,
            delivery_code,
            file_status,
            &[],
            Some(fault_location),
        )
    }

    pub fn new(
        mut pdu_header: PduHeader,
        condition_code: ConditionCode,
        delivery_code: DeliveryCode,
        file_status: FileStatus,
        fs_responses: &'fs_responses [FilestoreResponseTlv<
            'fs_responses,
            'fs_responses,
            'fs_responses,
        >],
        fault_location: Option<EntityIdTlv>,
    ) -> Self {
        pdu_header.pdu_type = PduType::FileDirective;
        // Enforce correct direction bit.
        pdu_header.pdu_conf.direction = Direction::TowardsSender;
        let mut finished_pdu = Self {
            pdu_header,
            condition_code,
            delivery_code,
            file_status,
            fs_responses,
            fault_location,
        };
        finished_pdu.pdu_header.pdu_datafield_len = finished_pdu.calc_pdu_datafield_len() as u16;
        finished_pdu
    }

    #[inline]
    pub fn pdu_header(&self) -> &PduHeader {
        &self.pdu_header
    }

    #[inline]
    pub fn condition_code(&self) -> ConditionCode {
        self.condition_code
    }

    #[inline]
    pub fn delivery_code(&self) -> DeliveryCode {
        self.delivery_code
    }

    #[inline]
    pub fn file_status(&self) -> FileStatus {
        self.file_status
    }

    // If there are no filestore responses, an empty slice will be returned.
    #[inline]
    pub fn filestore_responses(&self) -> &[FilestoreResponseTlv<'_, '_, '_>] {
        self.fs_responses
    }

    #[inline]
    pub fn fault_location(&self) -> Option<EntityIdTlv> {
        self.fault_location
    }

    fn calc_pdu_datafield_len(&self) -> usize {
        let mut datafield_len = 2;
        for fs_response in self.fs_responses {
            datafield_len += fs_response.len_full();
        }
        if let Some(fault_location) = self.fault_location {
            datafield_len += fault_location.len_full();
        }
        if self.crc_flag() == CrcFlag::WithCrc {
            datafield_len += 2;
        }
        datafield_len
    }

    /// Write [Self] to the provided buffer and returns the written size.
    pub fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, PduError> {
        let expected_len = self.len_written();
        if buf.len() < expected_len {
            return Err(ByteConversionError::ToSliceTooSmall {
                found: buf.len(),
                expected: expected_len,
            }
            .into());
        }

        let mut current_idx = self.pdu_header.write_to_bytes(buf)?;
        buf[current_idx] = FileDirectiveType::FinishedPdu as u8;
        current_idx += 1;
        buf[current_idx] = ((self.condition_code as u8) << 4)
            | ((self.delivery_code as u8) << 2)
            | self.file_status as u8;
        current_idx += 1;
        for fs_responses in self.fs_responses {
            current_idx += fs_responses.write_to_bytes(&mut buf[current_idx..])?;
        }
        if let Some(fault_location) = self.fault_location {
            current_idx += fault_location.write_to_bytes(&mut buf[current_idx..])?;
        }
        if self.crc_flag() == CrcFlag::WithCrc {
            current_idx = add_pdu_crc(buf, current_idx);
        }
        Ok(current_idx)
    }

    pub fn len_written(&self) -> usize {
        self.pdu_header.header_len() + self.calc_pdu_datafield_len()
    }
}

impl CfdpPdu for FinishedPduCreator<'_> {
    #[inline]
    fn pdu_header(&self) -> &PduHeader {
        self.pdu_header()
    }

    #[inline]
    fn file_directive_type(&self) -> Option<FileDirectiveType> {
        Some(FileDirectiveType::FinishedPdu)
    }
}

impl WritablePduPacket for FinishedPduCreator<'_> {
    fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, PduError> {
        self.write_to_bytes(buf)
    }

    fn len_written(&self) -> usize {
        self.len_written()
    }
}

/// Helper structure to loop through all filestore responses of a read Finished PDU. It should be
/// noted that iterators in Rust are not fallible, but the TLV creation can fail, for example if
/// the raw TLV data is invalid for some reason. In that case, the iterator will yield [None]
/// because there is no way to recover from this.
///
/// The user can accumulate the length of all TLVs yielded by the iterator and compare it against
/// the full length of the options to check whether the iterator was able to parse all TLVs
/// successfully.
pub struct FilestoreResponseIterator<'buf> {
    responses_buf: &'buf [u8],
    current_idx: usize,
}

impl<'buf> Iterator for FilestoreResponseIterator<'buf> {
    type Item = FilestoreResponseTlv<'buf, 'buf, 'buf>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.current_idx == self.responses_buf.len() {
            return None;
        }
        let tlv = FilestoreResponseTlv::from_bytes(&self.responses_buf[self.current_idx..]);
        // There are not really fallible iterators so we can't continue here..
        if tlv.is_err() {
            return None;
        }
        let tlv = tlv.unwrap();
        self.current_idx += tlv.len_full();
        Some(tlv)
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct FinishedPduReader<'buf> {
    pdu_header: PduHeader,
    condition_code: ConditionCode,
    delivery_code: DeliveryCode,
    file_status: FileStatus,
    fs_responses_raw: &'buf [u8],
    fault_location: Option<EntityIdTlv>,
}

impl<'buf> FinishedPduReader<'buf> {
    /// Generates [Self] from a raw bytestream.
    pub fn new(buf: &'buf [u8]) -> Result<Self, PduError> {
        Self::from_bytes(buf)
    }

    /// Generates [Self] from a raw bytestream.
    pub fn from_bytes(buf: &'buf [u8]) -> Result<Self, PduError> {
        let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?;
        let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?;
        let min_expected_len = current_idx + 2;
        generic_length_checks_pdu_deserialization(buf, min_expected_len, full_len_without_crc)?;
        let directive_type = FileDirectiveType::try_from(buf[current_idx]).map_err(|_| {
            PduError::InvalidDirectiveType {
                found: buf[current_idx],
                expected: Some(FileDirectiveType::FinishedPdu),
            }
        })?;
        if directive_type != FileDirectiveType::FinishedPdu {
            return Err(PduError::WrongDirectiveType {
                found: directive_type,
                expected: FileDirectiveType::FinishedPdu,
            });
        }
        current_idx += 1;
        let condition_code = ConditionCode::try_from((buf[current_idx] >> 4) & 0b1111)
            .map_err(|_| PduError::InvalidConditionCode((buf[current_idx] >> 4) & 0b1111))?;
        // Unwrap is okay here for both of the following operations which can not fail.
        let delivery_code = DeliveryCode::try_from((buf[current_idx] >> 2) & 0b1).unwrap();
        let file_status = FileStatus::try_from(buf[current_idx] & 0b11).unwrap();
        current_idx += 1;
        let (fs_responses_raw, fault_location) =
            Self::parse_tlv_fields(current_idx, full_len_without_crc, buf)?;
        Ok(Self {
            pdu_header,
            condition_code,
            delivery_code,
            file_status,
            fs_responses_raw,
            fault_location,
        })
    }

    #[inline]
    pub fn fs_responses_raw(&self) -> &[u8] {
        self.fs_responses_raw
    }

    #[inline]
    pub fn fs_responses_iter(&self) -> FilestoreResponseIterator<'_> {
        FilestoreResponseIterator {
            responses_buf: self.fs_responses_raw,
            current_idx: 0,
        }
    }

    #[inline]
    pub fn condition_code(&self) -> ConditionCode {
        self.condition_code
    }

    #[inline]
    pub fn delivery_code(&self) -> DeliveryCode {
        self.delivery_code
    }

    #[inline]
    pub fn file_status(&self) -> FileStatus {
        self.file_status
    }

    #[inline]
    pub fn fault_location(&self) -> Option<EntityIdTlv> {
        self.fault_location
    }

    #[inline]
    pub fn pdu_header(&self) -> &PduHeader {
        &self.pdu_header
    }

    fn parse_tlv_fields(
        mut current_idx: usize,
        full_len_without_crc: usize,
        buf: &[u8],
    ) -> Result<(&[u8], Option<EntityIdTlv>), PduError> {
        let mut fs_responses: &[u8] = &[];
        let mut fault_location = None;
        let start_of_fs_responses = current_idx;
        // There are leftover filestore response(s) and/or a fault location field.
        while current_idx < full_len_without_crc {
            let next_tlv = Tlv::from_bytes(&buf[current_idx..])?;
            match next_tlv.tlv_type_field() {
                TlvTypeField::Standard(tlv_type) => {
                    if tlv_type == TlvType::FilestoreResponse {
                        current_idx += next_tlv.len_full();
                        if current_idx == full_len_without_crc {
                            fs_responses = &buf[start_of_fs_responses..current_idx];
                        }
                    } else if tlv_type == TlvType::EntityId {
                        // At least one FS response is included.
                        if current_idx > start_of_fs_responses {
                            fs_responses = &buf[start_of_fs_responses..current_idx];
                        }
                        fault_location = Some(EntityIdTlv::from_bytes(&buf[current_idx..])?);
                        current_idx += fault_location.as_ref().unwrap().len_full();
                        // This is considered a configuration error: The entity ID has to be the
                        // last TLV, everything else would break the whole handling of the packet
                        // TLVs.
                        if current_idx != full_len_without_crc {
                            return Err(PduError::Format);
                        }
                    } else {
                        return Err(PduError::TlvLv(
                            InvalidTlvTypeFieldError {
                                found: tlv_type.into(),
                                expected: Some(TlvType::FilestoreResponse.into()),
                            }
                            .into(),
                        ));
                    }
                }
                TlvTypeField::Custom(raw) => {
                    return Err(PduError::TlvLv(
                        InvalidTlvTypeFieldError {
                            found: raw,
                            expected: None,
                        }
                        .into(),
                    ));
                }
            }
        }
        Ok((fs_responses, fault_location))
    }
}

impl CfdpPdu for FinishedPduReader<'_> {
    #[inline]
    fn pdu_header(&self) -> &PduHeader {
        self.pdu_header()
    }

    #[inline]
    fn file_directive_type(&self) -> Option<FileDirectiveType> {
        Some(FileDirectiveType::FinishedPdu)
    }
}

impl PartialEq<FinishedPduCreator<'_>> for FinishedPduReader<'_> {
    fn eq(&self, other: &FinishedPduCreator<'_>) -> bool {
        self.pdu_header == other.pdu_header
            && self.condition_code == other.condition_code
            && self.delivery_code == other.delivery_code
            && self.file_status == other.file_status
            && self.fault_location == other.fault_location
            && self
                .fs_responses_iter()
                .zip(other.filestore_responses().iter())
                .all(|(a, b)| a == *b)
    }
}

impl PartialEq<FinishedPduReader<'_>> for FinishedPduCreator<'_> {
    fn eq(&self, other: &FinishedPduReader<'_>) -> bool {
        other.eq(self)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::cfdp::lv::Lv;
    use crate::cfdp::pdu::tests::{
        common_pdu_conf, verify_raw_header, TEST_DEST_ID, TEST_SEQ_NUM, TEST_SRC_ID,
    };
    use crate::cfdp::pdu::{FileDirectiveType, PduHeader};
    use crate::cfdp::tlv::FilestoreResponseTlv;
    use crate::cfdp::{ConditionCode, CrcFlag, Direction, LargeFileFlag, TransmissionMode};

    fn generic_finished_pdu(
        crc_flag: CrcFlag,
        fss: LargeFileFlag,
        delivery_code: DeliveryCode,
        file_status: FileStatus,
    ) -> FinishedPduCreator<'static> {
        let pdu_header = PduHeader::new_for_file_directive(common_pdu_conf(crc_flag, fss), 0);
        FinishedPduCreator::new_no_error(pdu_header, delivery_code, file_status)
    }

    #[test]
    fn test_basic() {
        let finished_pdu = generic_finished_pdu(
            CrcFlag::NoCrc,
            LargeFileFlag::Normal,
            DeliveryCode::Complete,
            FileStatus::Retained,
        );
        assert_eq!(finished_pdu.condition_code(), ConditionCode::NoError);
        assert_eq!(
            finished_pdu.pdu_header().pdu_conf.direction,
            Direction::TowardsSender
        );
        assert_eq!(finished_pdu.delivery_code(), DeliveryCode::Complete);
        assert_eq!(finished_pdu.file_status(), FileStatus::Retained);
        assert_eq!(finished_pdu.filestore_responses(), &[]);
        assert_eq!(finished_pdu.fault_location(), None);
        assert_eq!(finished_pdu.pdu_header().pdu_datafield_len, 2);

        assert_eq!(finished_pdu.crc_flag(), CrcFlag::NoCrc);
        assert_eq!(finished_pdu.file_flag(), LargeFileFlag::Normal);
        assert_eq!(finished_pdu.pdu_type(), PduType::FileDirective);
        assert_eq!(
            finished_pdu.file_directive_type(),
            Some(FileDirectiveType::FinishedPdu)
        );
        assert_eq!(
            finished_pdu.transmission_mode(),
            TransmissionMode::Acknowledged
        );
        assert_eq!(finished_pdu.direction(), Direction::TowardsSender);
        assert_eq!(finished_pdu.source_id(), TEST_SRC_ID.into());
        assert_eq!(finished_pdu.dest_id(), TEST_DEST_ID.into());
        assert_eq!(finished_pdu.transaction_seq_num(), TEST_SEQ_NUM.into());
    }

    fn generic_serialization_test_no_error(delivery_code: DeliveryCode, file_status: FileStatus) {
        let finished_pdu = generic_finished_pdu(
            CrcFlag::NoCrc,
            LargeFileFlag::Normal,
            delivery_code,
            file_status,
        );
        let mut buf: [u8; 64] = [0; 64];
        let written = finished_pdu.write_to_bytes(&mut buf);
        assert!(written.is_ok());
        let written = written.unwrap();
        assert_eq!(written, 9);
        assert_eq!(written, finished_pdu.len_written());
        assert_eq!(written, finished_pdu.pdu_header().header_len() + 2);
        assert_eq!(
            finished_pdu.pdu_header().pdu_conf.direction,
            Direction::TowardsSender
        );
        verify_raw_header(finished_pdu.pdu_header(), &buf);
        let mut current_idx = finished_pdu.pdu_header().header_len();
        assert_eq!(buf[current_idx], FileDirectiveType::FinishedPdu as u8);
        current_idx += 1;
        assert_eq!(
            (buf[current_idx] >> 4) & 0b1111,
            ConditionCode::NoError as u8
        );
        assert_eq!((buf[current_idx] >> 2) & 0b1, delivery_code as u8);
        assert_eq!(buf[current_idx] & 0b11, file_status as u8);
        assert_eq!(current_idx + 1, written);
    }

    #[test]
    fn test_serialization_simple() {
        generic_serialization_test_no_error(DeliveryCode::Complete, FileStatus::Retained);
    }

    #[test]
    fn test_serialization_simple_2() {
        generic_serialization_test_no_error(
            DeliveryCode::Incomplete,
            FileStatus::DiscardDeliberately,
        );
    }

    #[test]
    fn test_serialization_simple_3() {
        generic_serialization_test_no_error(DeliveryCode::Incomplete, FileStatus::Unreported);
    }

    #[test]
    fn test_write_to_vec() {
        let finished_pdu = generic_finished_pdu(
            CrcFlag::NoCrc,
            LargeFileFlag::Normal,
            DeliveryCode::Complete,
            FileStatus::Retained,
        );
        let mut buf: [u8; 64] = [0; 64];
        let written = finished_pdu.write_to_bytes(&mut buf).unwrap();
        let pdu_vec = finished_pdu.to_vec().unwrap();
        assert_eq!(buf[0..written], pdu_vec);
    }

    #[test]
    fn test_deserialization_simple() {
        let finished_pdu = generic_finished_pdu(
            CrcFlag::NoCrc,
            LargeFileFlag::Normal,
            DeliveryCode::Complete,
            FileStatus::Retained,
        );
        let mut buf: [u8; 64] = [0; 64];
        finished_pdu.write_to_bytes(&mut buf).unwrap();
        let read_back = FinishedPduReader::from_bytes(&buf);
        assert!(read_back.is_ok());
        let read_back = read_back.unwrap();
        assert_eq!(finished_pdu, read_back);
        // Use all getter functions here explicitely once.
        assert_eq!(finished_pdu.pdu_header(), read_back.pdu_header());
        assert_eq!(finished_pdu.condition_code(), read_back.condition_code());
        assert_eq!(finished_pdu.fault_location(), read_back.fault_location());
        assert_eq!(finished_pdu.file_status(), read_back.file_status());
        assert_eq!(finished_pdu.delivery_code(), read_back.delivery_code());
    }

    #[test]
    fn test_serialization_buf_too_small() {
        let finished_pdu = generic_finished_pdu(
            CrcFlag::NoCrc,
            LargeFileFlag::Normal,
            DeliveryCode::Complete,
            FileStatus::Retained,
        );
        let mut buf: [u8; 8] = [0; 8];
        let error = finished_pdu.write_to_bytes(&mut buf);
        assert!(error.is_err());
        if let PduError::ByteConversion(ByteConversionError::ToSliceTooSmall { found, expected }) =
            error.unwrap_err()
        {
            assert_eq!(found, 8);
            assert_eq!(expected, 9);
        } else {
            panic!("expected to_slice_too_small error");
        }
    }

    #[test]
    fn test_with_crc() {
        let finished_pdu = generic_finished_pdu(
            CrcFlag::WithCrc,
            LargeFileFlag::Normal,
            DeliveryCode::Complete,
            FileStatus::Retained,
        );
        let mut buf: [u8; 64] = [0; 64];
        let written = finished_pdu.write_to_bytes(&mut buf).unwrap();
        assert_eq!(written, finished_pdu.len_written());
        let finished_pdu_from_raw = FinishedPduReader::new(&buf).unwrap();
        assert_eq!(finished_pdu, finished_pdu_from_raw);
        buf[written - 1] -= 1;
        let crc: u16 = ((buf[written - 2] as u16) << 8) as u16 | buf[written - 1] as u16;
        let error = FinishedPduReader::new(&buf).unwrap_err();
        if let PduError::Checksum(e) = error {
            assert_eq!(e, crc);
        } else {
            panic!("expected crc error");
        }
    }

    #[test]
    fn test_with_fault_location() {
        let pdu_header = PduHeader::new_for_file_directive(
            common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Normal),
            0,
        );
        let finished_pdu = FinishedPduCreator::new_with_error(
            pdu_header,
            ConditionCode::NakLimitReached,
            DeliveryCode::Incomplete,
            FileStatus::DiscardDeliberately,
            EntityIdTlv::new(TEST_DEST_ID.into()),
        );
        let finished_pdu_vec = finished_pdu.to_vec().unwrap();
        assert_eq!(finished_pdu_vec.len(), 12);
        assert_eq!(finished_pdu_vec[9], TlvType::EntityId.into());
        assert_eq!(finished_pdu_vec[10], 1);
        assert_eq!(finished_pdu_vec[11], TEST_DEST_ID.value_typed());
        assert_eq!(
            finished_pdu.fault_location().unwrap().entity_id(),
            &TEST_DEST_ID.into()
        );
    }

    #[test]
    fn test_deserialization_with_fault_location() {
        let pdu_header = PduHeader::new_for_file_directive(
            common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Normal),
            0,
        );
        let entity_id_tlv = EntityIdTlv::new(TEST_DEST_ID.into());
        let finished_pdu = FinishedPduCreator::new_with_error(
            pdu_header,
            ConditionCode::NakLimitReached,
            DeliveryCode::Incomplete,
            FileStatus::DiscardDeliberately,
            entity_id_tlv,
        );
        let finished_pdu_vec = finished_pdu.to_vec().unwrap();
        let finished_pdu_deserialized = FinishedPduReader::from_bytes(&finished_pdu_vec).unwrap();
        assert_eq!(finished_pdu, finished_pdu_deserialized);
    }

    #[test]
    fn test_deserialization_with_fs_responses() {
        let entity_id_tlv = EntityIdTlv::new(TEST_DEST_ID.into());
        let first_name = "first.txt";
        let first_name_lv = Lv::new_from_str(first_name).unwrap();
        let fs_response_0 = FilestoreResponseTlv::new_no_filestore_message(
            crate::cfdp::tlv::FilestoreActionCode::CreateFile,
            0,
            first_name_lv,
            None,
        )
        .unwrap();
        let fs_response_1 = FilestoreResponseTlv::new_no_filestore_message(
            crate::cfdp::tlv::FilestoreActionCode::DeleteFile,
            0,
            first_name_lv,
            None,
        )
        .unwrap();
        let fs_responses = &[fs_response_0, fs_response_1];

        let pdu_header = PduHeader::new_for_file_directive(
            common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Normal),
            0,
        );
        let finished_pdu = FinishedPduCreator::new(
            pdu_header,
            ConditionCode::NakLimitReached,
            DeliveryCode::Incomplete,
            FileStatus::DiscardDeliberately,
            fs_responses,
            Some(entity_id_tlv),
        );
        let finished_pdu_vec = finished_pdu.to_vec().unwrap();
        let finished_pdu_deserialized = FinishedPduReader::from_bytes(&finished_pdu_vec).unwrap();
        assert_eq!(finished_pdu_deserialized, finished_pdu);
    }

    #[test]
    fn test_deserialization_with_fs_responses_and_fault_location() {
        let first_name = "first.txt";
        let first_name_lv = Lv::new_from_str(first_name).unwrap();
        let fs_response_0 = FilestoreResponseTlv::new_no_filestore_message(
            crate::cfdp::tlv::FilestoreActionCode::CreateFile,
            0,
            first_name_lv,
            None,
        )
        .unwrap();
        let fs_response_1 = FilestoreResponseTlv::new_no_filestore_message(
            crate::cfdp::tlv::FilestoreActionCode::DeleteFile,
            0,
            first_name_lv,
            None,
        )
        .unwrap();
        let fs_responses = &[fs_response_0, fs_response_1];

        let pdu_header = PduHeader::new_for_file_directive(
            common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Normal),
            0,
        );
        let finished_pdu = FinishedPduCreator::new(
            pdu_header,
            ConditionCode::NakLimitReached,
            DeliveryCode::Incomplete,
            FileStatus::DiscardDeliberately,
            fs_responses,
            None,
        );
        let finished_pdu_vec = finished_pdu.to_vec().unwrap();
        let finished_pdu_deserialized = FinishedPduReader::from_bytes(&finished_pdu_vec).unwrap();
        assert_eq!(finished_pdu_deserialized, finished_pdu);
    }
}
