use crate::{
    error::{ParseTagValueError, ValidationError},
    tag::{UnknownTag, hls::into_inner_tag},
};
use std::borrow::Cow;

/// Corresponds to the `#EXT-X-DISCONTINUITY-SEQUENCE` tag.
///
/// <https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis-17#section-4.4.3.3>
#[derive(Debug, Clone)]
pub struct DiscontinuitySequence<'a> {
    discontinuity_sequence: u64,
    output_line: Cow<'a, [u8]>, // Used with Writer
    output_line_is_dirty: bool, // If should recalculate output_line
}

impl<'a> PartialEq for DiscontinuitySequence<'a> {
    fn eq(&self, other: &Self) -> bool {
        self.discontinuity_sequence() == other.discontinuity_sequence()
    }
}

impl<'a> TryFrom<UnknownTag<'a>> for DiscontinuitySequence<'a> {
    type Error = ValidationError;

    fn try_from(tag: UnknownTag<'a>) -> Result<Self, Self::Error> {
        let discontinuity_sequence = tag
            .value()
            .ok_or(ParseTagValueError::UnexpectedEmpty)?
            .try_as_decimal_integer()?;
        Ok(Self {
            discontinuity_sequence,
            output_line: Cow::Borrowed(tag.original_input),
            output_line_is_dirty: false,
        })
    }
}

impl<'a> DiscontinuitySequence<'a> {
    /// Construct a new `DiscontinuitySequence`.
    pub fn new(discontinuity_sequence: u64) -> Self {
        let output_line = Cow::Owned(calculate_line(discontinuity_sequence));
        Self {
            discontinuity_sequence,
            output_line,
            output_line_is_dirty: false,
        }
    }

    /// Corresponds to the value of the tag (`#EXT-X-DISCONTINUITY-SEQUENCE:<number>`).
    ///
    /// See [`Self`] for a link to the HLS documentation for this attribute.
    pub fn discontinuity_sequence(&self) -> u64 {
        self.discontinuity_sequence
    }

    /// Sets the value of the tag.
    ///
    /// See [`Self`] for a link to the HLS documentation for this attribute.
    pub fn set_discontinuity_sequence(&mut self, discontinuity_sequence: u64) {
        self.discontinuity_sequence = discontinuity_sequence;
        self.output_line_is_dirty = true;
    }

    fn recalculate_output_line(&mut self) {
        self.output_line = Cow::Owned(calculate_line(self.discontinuity_sequence()));
        self.output_line_is_dirty = false;
    }
}

into_inner_tag!(DiscontinuitySequence);

fn calculate_line(discontinuity_sequence: u64) -> Vec<u8> {
    format!("#EXT-X-DISCONTINUITY-SEQUENCE:{discontinuity_sequence}").into_bytes()
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::tag::{IntoInnerTag, hls::test_macro::mutation_tests};
    use pretty_assertions::assert_eq;

    #[test]
    fn as_str_should_be_valid_tag() {
        assert_eq!(
            b"#EXT-X-DISCONTINUITY-SEQUENCE:42",
            DiscontinuitySequence::new(42).into_inner().value()
        )
    }

    mutation_tests!(DiscontinuitySequence::new(42), (discontinuity_sequence, 1337, @Attr=":1337"));
}
