use super::super::*;

impl<'a> Parser<'a> {
    pub(in crate::parser) fn parse_source_text_as_word(&self, text: &SourceText) -> Word {
        if let Some(word) = self.simple_source_text_as_word(text) {
            return word;
        }

        let span = text.span();
        let nested_profile = self
            .zsh_options_at_offset(span.start.offset())
            .cloned()
            .map(|options| ShellProfile::with_zsh_options(self.dialect, options))
            .unwrap_or_else(|| self.shell_profile.clone());
        let remaining_depth = self.max_depth.saturating_sub(self.current_depth);
        if remaining_depth == 0 {
            return self.word_with_single_part(
                self.literal_part_from_text(text.slice(self.input), span, text.is_source_backed()),
                span,
            );
        }
        let reparse_depth = (remaining_depth - 1).min(SOURCE_TEXT_WORD_REPARSE_MAX_DEPTH);

        if !text.is_source_backed()
            && span.start.offset() <= span.end.offset()
            && span.end.offset() <= self.input.len()
        {
            let raw = span.slice(self.input);
            if raw.contains("\\\"") {
                return Self::parse_word_fragment_with_limits(
                    self.input,
                    raw,
                    span,
                    reparse_depth,
                    self.fuel,
                    nested_profile.clone(),
                );
            }
        }

        Self::parse_word_fragment_with_limits(
            self.input,
            text.slice(self.input),
            text.span(),
            reparse_depth,
            self.fuel,
            nested_profile,
        )
    }

    pub(in crate::parser) fn simple_source_text_as_word(&self, text: &SourceText) -> Option<Word> {
        if !text.is_source_backed() {
            return None;
        }

        let span = text.span();
        let raw = text.slice(self.input);
        if raw.is_empty() {
            return Some(Word::literal_with_span("", span));
        }

        if let Some(word) = self.simple_quoted_source_text_as_word(raw, span) {
            return Some(word);
        }

        if Self::word_text_needs_parse(raw)
            || raw.contains(['\'', '"', '\\'])
            || self.zsh_glob_word_parsing_enabled_at(span.start.offset())
        {
            return None;
        }

        Some(self.word_with_single_part(self.literal_part_from_text(raw, span, true), span))
    }

    pub(in crate::parser) fn simple_quoted_source_text_as_word(
        &self,
        raw: &str,
        span: Span,
    ) -> Option<Word> {
        if raw.len() < 2 {
            return None;
        }

        let quote = raw.as_bytes()[0];
        if quote != b'\'' && quote != b'"' || raw.as_bytes().last().copied() != Some(quote) {
            return None;
        }

        let inner = &raw[1..raw.len() - 1];
        if quote == b'\'' && inner.contains('\'') {
            return None;
        }

        let inner_start = span.start.advanced_by(&raw[..1]);
        let inner_end = inner_start.advanced_by(inner);
        let inner_span = Span::from_positions(inner_start, inner_end);
        let part = match quote {
            b'\'' => self.single_quoted_part_from_text(inner, inner_span, span, false),
            b'"' => {
                if Self::word_text_needs_parse(inner) || inner.contains(['\\', '"']) {
                    return None;
                }
                self.double_quoted_literal_part_from_text(inner, inner_span, span, true, false)
            }
            _ => unreachable!("quote is checked above"),
        };
        Some(self.word_with_single_part(part, span))
    }

    pub(in crate::parser) fn parse_optional_source_text_as_word(
        &self,
        text: Option<&SourceText>,
    ) -> Option<Word> {
        text.map(|text| self.parse_source_text_as_word(text))
    }
}
