use std::{collections::HashMap, ops::Range};

use bstr::{BStr, BString, ByteSlice, ByteVec};
use gix_sec::Trust;
use smallvec::SmallVec;

use crate::{
    AsBStrOpt,
    file::{
        self, Index, IntoBStringOpt, SectionData, SectionId, SectionLookup, SectionRef, Size,
        mutable::{Whitespace, escape_value},
    },
    lookup,
    parse::{self, Event, Span, section::ValueName},
    value::normalize,
};

/// A opaque type that represents a mutable reference to a section.
#[derive(Debug)]
pub struct SectionMut<'a> {
    section: &'a mut SectionData,
    backing: &'a mut Vec<u8>,
    /// Present only if backed by [`crate::File`]
    lookup: Option<LookupMut<'a>>,
    implicit_newline: bool,
    whitespace: Whitespace,
    newline: SmallVec<[u8; 2]>,
}

#[derive(Debug)]
pub(crate) struct LookupMut<'a> {
    pub(crate) tree: &'a mut HashMap<parse::section::Name, SectionLookup>,
    pub(crate) order: &'a [SectionId],
}

/// Mutating methods.
impl SectionMut<'_> {
    /// Rename this section to `name` and the optional `subsection_name`.
    ///
    /// Section names are not unique, and renaming into an existing section name is permitted.
    pub fn rename(
        &mut self,
        name: impl AsRef<str>,
        subsection_name: impl IntoBStringOpt,
    ) -> Result<&mut Self, parse::section::header::Error> {
        let header = parse::section::HeaderData::new_in(name, subsection_name.into_bstring_opt(), self.backing)?;
        self.set_header(header);
        Ok(self)
    }

    /// Adds an entry to the end of this section name `value_name` and `value`. If `value` is `None`, no equal sign will be written leaving
    /// just the key. This is useful for boolean values which are true if merely the key exists.
    pub fn push(
        &mut self,
        value_name: impl AsRef<str>,
        value: impl AsBStrOpt,
    ) -> Result<&mut Self, file::section::value::Error> {
        let value_name = ValueName::try_from(value_name.as_ref())?;
        self.push_with_comment_inner(value_name, value.as_bstr_opt(), None)?;
        Ok(self)
    }

    /// Adds an entry to the end of this section name `value_name` and `value`. If `value` is `None`, no equal sign will be written leaving
    /// just the key. This is useful for boolean values which are true if merely the key exists.
    /// `comment` has to be the text to put right after the value and behind a `#` character. Note that newlines are silently transformed
    /// into spaces.
    pub fn push_with_comment(
        &mut self,
        value_name: impl AsRef<str>,
        value: impl AsBStrOpt,
        comment: impl crate::AsBStr,
    ) -> Result<&mut Self, file::section::value::Error> {
        let value_name = ValueName::try_from(value_name.as_ref())?;
        self.push_with_comment_inner(value_name, value.as_bstr_opt(), Some(comment.as_bstr()))?;
        Ok(self)
    }

    fn push_with_comment_inner(
        &mut self,
        value_name: ValueName,
        value: Option<&BStr>,
        comment: Option<&BStr>,
    ) -> Result<(), parse::span::Error> {
        let mut events = Vec::new();
        if let Some(ws) = &self.whitespace.pre_key {
            events.push(Event::Whitespace(Span::append(self.backing, ws)?));
        }

        events.push(Event::SectionValueName(Span::append(
            self.backing,
            value_name.0.as_slice(),
        )?));
        match value {
            Some(value) => {
                events.extend(self.whitespace.key_value_separators(self.backing)?);
                events.push(Event::Value(Span::append(self.backing, &escape_value(value))?));
            }
            None => events.push(Event::Value(Span::append(self.backing, b"")?)),
        }
        if let Some(comment) = comment {
            events.push(Event::Whitespace(Span::append(self.backing, b" ")?));
            let mut c = Vec::with_capacity(comment.len());
            let mut bytes = comment.iter().peekable();
            if !bytes.peek().is_none_or(|b| b.is_ascii_whitespace()) {
                c.insert(0, b' ');
            }
            c.extend(bytes.map(|b| if *b == b'\n' { b' ' } else { *b }));
            events.push(Event::Comment(parse::Comment {
                tag: b'#',
                text: Span::append(self.backing, &c)?,
            }));
        }
        if self.implicit_newline {
            events.push(Event::Newline(Span::append(self.backing, &self.newline)?));
        }
        self.section.body.0.extend(events);
        Ok(())
    }

    /// Removes all events until a key value pair is removed. This will also
    /// remove the whitespace preceding the key value pair, if any is found.
    pub fn pop(&mut self) -> Option<(String, BString)> {
        let mut values: Vec<BString> = Vec::new();
        // events are popped in reverse order
        let body = &mut self.section.body.0;
        while let Some(e) = body.pop() {
            match e {
                Event::SectionValueName(k) => {
                    // pop leading whitespace
                    if let Some(Event::Whitespace(_)) = body.last() {
                        body.pop();
                    }

                    if values.len() == 1 {
                        let value = values.pop().expect("vec is non-empty but popped to empty value");
                        return Some((
                            k.as_bstr_in(self.backing)
                                .to_str()
                                .expect("parsed value names are ASCII")
                                .to_owned(),
                            normalize(&value).into_owned(),
                        ));
                    }

                    return Some((
                        k.as_bstr_in(self.backing)
                            .to_str()
                            .expect("parsed value names are ASCII")
                            .to_owned(),
                        normalize(&{
                            let mut s = BString::default();
                            for value in values.into_iter().rev() {
                                s.push_str(value.as_slice());
                            }
                            s
                        })
                        .into_owned(),
                    ));
                }
                Event::Value(v) | Event::ValueNotDone(v) | Event::ValueDone(v) => {
                    values.push(v.to_bstring_in(self.backing));
                }
                _ => (),
            }
        }
        None
    }

    /// Sets the last key value pair if it exists, or adds the new value.
    /// Returns the previous value if it replaced a value, or None if it adds
    /// the value.
    pub fn set(
        &mut self,
        value_name: impl AsRef<str>,
        value: impl crate::AsBStr,
    ) -> Result<Option<BString>, file::section::value::Error> {
        let value_name = ValueName::try_from(value_name.as_ref())?;
        self.set_inner(value_name, value.as_bstr()).map_err(Into::into)
    }

    pub(crate) fn set_inner(
        &mut self,
        value_name: ValueName,
        value: &BStr,
    ) -> Result<Option<BString>, parse::span::Error> {
        match self.section.body.key_and_value_range_by_in(self.backing, &value_name) {
            None => {
                self.push_with_comment_inner(value_name, Some(value), None)?;
                Ok(None)
            }
            Some((key_range, value_range)) => {
                let value_range = value_range.unwrap_or(key_range.end - 1..key_range.end);
                let range_start = value_range.start;
                let value = Span::append(self.backing, &escape_value(value))?;
                let ret = self.remove_internal(value_range, false);
                self.section.body.0.insert(range_start, Event::Value(value));
                Ok(Some(ret))
            }
        }
    }

    /// Set the trust level in the meta-data of this section to `trust`.
    pub fn set_trust(&mut self, trust: Trust) -> &mut Self {
        let mut meta = (*self.section.meta).clone();
        meta.trust = trust;
        self.section.meta = meta.into();
        self
    }

    /// Removes the latest value by key and returns it, if it exists.
    pub fn remove(&mut self, value_name: &str) -> Option<BString> {
        let key = ValueName::from_str_unchecked(value_name);
        let (key_range, _value_range) = self.section.body.key_and_value_range_by_in(self.backing, &key)?;
        Some(self.remove_internal(key_range, true))
    }

    /// Adds a new line event. Note that you don't need to call this unless
    /// you've disabled implicit newlines.
    pub fn push_newline(&mut self) -> Result<&mut Self, parse::span::Error> {
        let newline = Span::append(self.backing, &self.newline)?;
        self.section.body.0.push(Event::Newline(newline));
        Ok(self)
    }

    /// Return the newline used when calling [`push_newline()`][Self::push_newline()].
    pub fn newline(&self) -> &BStr {
        self.newline.as_slice().as_bstr()
    }

    /// Enables or disables automatically adding newline events after adding
    /// a value. This is _enabled by default_.
    pub fn set_implicit_newline(&mut self, on: bool) -> &mut Self {
        self.implicit_newline = on;
        self
    }

    /// Sets the exact `whitespace` to use before each newly created key-value pair,
    /// with only whitespace characters being permissible.
    ///
    /// The default is 2 tabs.
    /// Set to `None` to disable adding whitespace before a key value.
    ///
    /// # Panics
    ///
    /// If non-whitespace characters are used. This makes the method only suitable for validated
    /// or known input.
    // TODO(error): make it fallible
    pub fn set_leading_whitespace(&mut self, whitespace: impl IntoBStringOpt) -> &mut Self {
        let whitespace = whitespace.into_bstring_opt();
        assert!(
            whitespace
                .as_deref()
                .is_none_or(|ws| ws.iter().all(u8::is_ascii_whitespace)),
            "input whitespace must only contain whitespace characters."
        );
        self.whitespace.pre_key = whitespace;
        self
    }

    /// Returns the whitespace this section will insert before the
    /// beginning of a key, if any.
    #[must_use]
    pub fn leading_whitespace(&self) -> Option<&BStr> {
        self.whitespace.pre_key.as_ref().map(|v| v.as_slice().as_bstr())
    }

    /// Return the immutable section view backing this mutable handle.
    pub fn section(&self) -> SectionRef<'_> {
        SectionRef::from_data(self.section, self.backing)
    }

    /// Return the header of the section backing this mutable handle.
    pub fn header(&self) -> file::section::HeaderRef<'_> {
        file::section::HeaderRef {
            header: &self.section.header,
            backing: self.backing,
        }
    }

    /// Return the unique `id` of the section backing this mutable handle.
    pub fn id(&self) -> file::SectionId {
        self.section.id
    }

    /// Return the section body backing this mutable handle.
    pub fn body(&self) -> file::section::BodyRef<'_> {
        file::section::BodyRef {
            body: &self.section.body,
            backing: self.backing,
        }
    }

    /// Serialize the section backing this mutable handle into a `BString`.
    #[must_use]
    pub fn to_bstring(&self) -> BString {
        self.section().to_bstring()
    }

    /// Return additional information about this section's origin.
    pub fn meta(&self) -> &file::Metadata {
        &self.section.meta
    }

    /// Retrieves the last matching value in this section with the given value name, if present.
    #[must_use]
    pub fn value(&self, value_name: impl AsRef<str>) -> Option<BString> {
        self.section
            .body
            .value_implicit_in(self.backing, value_name.as_ref())
            .flatten()
    }

    /// Retrieves the last matching value in this section, including implicit values.
    #[must_use]
    pub fn value_implicit(&self, value_name: &str) -> Option<Option<BString>> {
        self.section.body.value_implicit_in(self.backing, value_name)
    }

    /// Retrieves all values that have the provided value name.
    #[must_use]
    pub fn values(&self, value_name: &str) -> Vec<BString> {
        self.section.body.values_in(self.backing, value_name)
    }

    /// Returns an iterator visiting all value names in order.
    pub fn value_names(&self) -> impl Iterator<Item = String> + '_ {
        self.section.body.as_ref().iter().filter_map(move |e| match e {
            Event::SectionValueName(k) => Some(
                k.as_bstr_in(self.backing)
                    .to_str()
                    .expect("parsed value names are ASCII")
                    .to_owned(),
            ),
            _ => None,
        })
    }

    /// Returns true if the section contains the provided value name.
    #[must_use]
    pub fn contains_value_name(&self, value_name: &str) -> bool {
        self.section.body.contains_value_name_in(self.backing, value_name)
    }

    /// Returns the number of values in the section.
    #[must_use]
    pub fn num_values(&self) -> usize {
        self.section.body.num_values()
    }

    /// Returns if the section is empty.
    #[must_use]
    pub fn is_void(&self) -> bool {
        self.section.body.is_void()
    }

    /// Returns the whitespace to be used before and after the `=` between the key
    /// and the value.
    ///
    /// For example, `k = v` will have `(Some(" "), Some(" "))`, whereas `k=\tv` will
    /// have `(None, Some("\t"))`.
    #[must_use]
    pub fn separator_whitespace(&self) -> (Option<&BStr>, Option<&BStr>) {
        (
            self.whitespace.pre_sep.as_ref().map(|v| v.as_slice().as_bstr()),
            self.whitespace.post_sep.as_ref().map(|v| v.as_slice().as_bstr()),
        )
    }
}

// Internal methods that may require exact indices for faster operations.
impl<'a> SectionMut<'a> {
    pub(crate) fn new(
        section: &'a mut SectionData,
        backing: &'a mut Vec<u8>,
        lookup: Option<LookupMut<'a>>,
        newline: SmallVec<[u8; 2]>,
    ) -> Self {
        let whitespace = Whitespace::from_body(&section.body, backing);
        Self {
            section,
            backing,
            lookup,
            implicit_newline: true,
            whitespace,
            newline,
        }
    }

    pub(crate) fn set_header(&mut self, header: parse::section::HeaderData) {
        if let Some(lookup) = &mut self.lookup {
            crate::file::util::set_section_header(self.section, self.backing, lookup.tree, lookup.order, header);
        } else {
            self.section.header = header;
        }
    }

    pub(crate) fn get(&self, key: &ValueName, start: Index, end: Index) -> Result<BString, lookup::existing::Error> {
        let mut expect_value = false;
        let mut concatenated_value = BString::default();

        for event in &self.section.body.0[start.0..end.0] {
            match event {
                Event::SectionValueName(event_key)
                    if event_key
                        .as_bstr_in(self.backing)
                        .eq_ignore_ascii_case(key.0.as_slice()) =>
                {
                    expect_value = true;
                }
                Event::Value(v) if expect_value => return Ok(normalize(v.as_slice_in(self.backing)).into_owned()),
                Event::ValueNotDone(v) if expect_value => {
                    concatenated_value.push_str(v.as_slice_in(self.backing));
                }
                Event::ValueDone(v) if expect_value => {
                    concatenated_value.push_str(v.as_slice_in(self.backing));
                    return Ok(normalize(&concatenated_value).into_owned());
                }
                _ => (),
            }
        }

        Err(lookup::existing::Error::KeyMissing)
    }

    pub(crate) fn delete(&mut self, start: Index, end: Index) {
        self.section.body.0.drain(start.0..end.0);
    }

    pub(crate) fn set_internal(
        &mut self,
        index: Index,
        key: ValueName,
        value: &BStr,
    ) -> Result<Size, parse::span::Error> {
        let mut size = 0;
        let value = Span::append(self.backing, &escape_value(value))?;
        let sep_events = self.whitespace.key_value_separators(self.backing)?;
        let key = Span::append(self.backing, key.0.as_slice())?;

        let body = &mut self.section.body.0;
        body.insert(index.0, Event::Value(value));
        size += 1;

        size += sep_events.len();
        body.splice(index.0..index.0, sep_events.into_iter().rev())
            .for_each(|_| {});

        body.insert(index.0, Event::SectionValueName(key));
        size += 1;

        Ok(Size(size))
    }

    /// Performs the removal, assuming the range is valid.
    fn remove_internal(&mut self, range: Range<usize>, fix_whitespace: bool) -> BString {
        let events = &mut self.section.body.0;
        if fix_whitespace && events.get(range.end).is_some_and(|ev| matches!(ev, Event::Newline(_))) {
            events.remove(range.end);
        }
        let value = events.drain(range.clone()).fold(BString::default(), |mut acc, e| {
            if let Event::Value(v) | Event::ValueNotDone(v) | Event::ValueDone(v) = e {
                acc.push_str(v.as_slice_in(self.backing));
            }
            acc
        });
        if fix_whitespace
            && range
                .start
                .checked_sub(1)
                .and_then(|pos| events.get(pos))
                .is_some_and(|ev| matches!(ev, Event::Whitespace(_)))
        {
            events.remove(range.start - 1);
        }
        value
    }
}

impl file::section::BodyData {
    pub(crate) fn as_mut(&mut self) -> &mut Vec<Event> {
        &mut self.0
    }
}
