use super::*;

impl<'a> Parser<'a> {
    pub(super) fn scan_shell_path_like_bareword_end(
        &mut self,
        start_index: usize,
    ) -> Option<usize> {
        if !matches!(
            self.token_at(start_index).kind,
            TokenKind::Ident | TokenKind::Pipe | TokenKind::Star | TokenKind::Colon
        ) {
            return None;
        }

        let mut end_index = start_index;
        let mut index = start_index + 1;
        let mut expecting_segment_start = false;

        match self.token_at(start_index).kind {
            TokenKind::Pipe | TokenKind::Colon => {
                end_index = start_index;
                expecting_segment_start = true;
            }
            TokenKind::Ident | TokenKind::Star => {
                while matches!(
                    self.kind_at(index),
                    Some(TokenKind::Ident | TokenKind::Star)
                ) && self.tokens_are_adjacent(end_index, index)
                {
                    end_index = index;
                    index += 1;
                }

                while matches!(
                    (self.kind_at(index), self.kind_at(index + 1)),
                    (
                        Some(TokenKind::Colon),
                        Some(TokenKind::Ident | TokenKind::Star)
                    )
                ) && self.tokens_are_adjacent(end_index, index)
                    && self.tokens_are_adjacent(index, index + 1)
                {
                    end_index = index + 1;
                    index += 2;
                    while matches!(
                        self.kind_at(index),
                        Some(TokenKind::Ident | TokenKind::Star)
                    ) && self.tokens_are_adjacent(end_index, index)
                    {
                        end_index = index;
                        index += 1;
                    }
                }
            }
            _ => return None,
        }

        loop {
            if expecting_segment_start {
                let mut consumed_atom = false;
                while matches!(
                    self.kind_at(index),
                    Some(TokenKind::Ident | TokenKind::Star)
                ) && self.tokens_are_adjacent(end_index, index)
                {
                    end_index = index;
                    index += 1;
                    consumed_atom = true;
                }

                if !consumed_atom {
                    return None;
                }

                while matches!(
                    (self.kind_at(index), self.kind_at(index + 1)),
                    (
                        Some(TokenKind::Colon),
                        Some(TokenKind::Ident | TokenKind::Star)
                    )
                ) && self.tokens_are_adjacent(end_index, index)
                    && self.tokens_are_adjacent(index, index + 1)
                {
                    end_index = index + 1;
                    index += 2;
                    while matches!(
                        self.kind_at(index),
                        Some(TokenKind::Ident | TokenKind::Star)
                    ) && self.tokens_are_adjacent(end_index, index)
                    {
                        end_index = index;
                        index += 1;
                    }
                }

                expecting_segment_start = false;
                continue;
            }

            if matches!(self.kind_at(index), Some(TokenKind::Pipe))
                && self.tokens_are_adjacent(end_index, index)
            {
                end_index = index;
                index += 1;
                expecting_segment_start = true;
                continue;
            }

            if matches!(
                (self.kind_at(index), self.kind_at(index + 1)),
                (Some(TokenKind::Dot), Some(TokenKind::Ident))
            ) && self.tokens_are_adjacent(end_index, index)
                && self.tokens_are_adjacent(index, index + 1)
            {
                end_index = index + 1;
                index += 2;
                continue;
            }

            if self.tokens_are_adjacent(end_index, index)
                && let Some(suffix_end) = self.bareword_bracket_suffix_end(index)
            {
                end_index = suffix_end;
                index = suffix_end + 1;
                continue;
            }

            while matches!(
                self.kind_at(index),
                Some(TokenKind::Ident | TokenKind::Star)
            ) && self.tokens_are_adjacent(end_index, index)
            {
                end_index = index;
                index += 1;
            }

            break;
        }

        if expecting_segment_start {
            return None;
        }

        Some(end_index)
    }

    pub(super) fn scan_path_like_bareword_end(&mut self, start_index: usize) -> Option<usize> {
        if !matches!(
            self.token_at(start_index).kind,
            TokenKind::Ident | TokenKind::Pipe | TokenKind::Star | TokenKind::Colon
        ) {
            return None;
        }

        let mut end_index = start_index;
        let mut index = start_index + 1;
        let mut expecting_segment_start = false;

        match self.token_at(start_index).kind {
            TokenKind::Pipe | TokenKind::Colon => {
                end_index = start_index;
                expecting_segment_start = true;
            }
            TokenKind::Ident | TokenKind::Star => {
                while matches!(
                    self.kind_at(index),
                    Some(TokenKind::Ident | TokenKind::Star)
                ) && self.tokens_are_adjacent(end_index, index)
                {
                    end_index = index;
                    index += 1;
                }

                while matches!(
                    (self.kind_at(index), self.kind_at(index + 1)),
                    (
                        Some(TokenKind::Colon),
                        Some(TokenKind::Ident | TokenKind::Star)
                    )
                ) && self.tokens_are_adjacent(end_index, index)
                    && self.tokens_are_adjacent(index, index + 1)
                {
                    end_index = index + 1;
                    index += 2;
                    while matches!(
                        self.kind_at(index),
                        Some(TokenKind::Ident | TokenKind::Star)
                    ) && self.tokens_are_adjacent(end_index, index)
                    {
                        end_index = index;
                        index += 1;
                    }
                }
            }
            _ => return None,
        }

        loop {
            if expecting_segment_start {
                let mut consumed_atom = false;
                while matches!(
                    self.kind_at(index),
                    Some(TokenKind::Ident | TokenKind::Star)
                ) && self.tokens_are_adjacent(end_index, index)
                {
                    end_index = index;
                    index += 1;
                    consumed_atom = true;
                }

                if !consumed_atom {
                    return (self.token_at(end_index).kind == TokenKind::Pipe).then_some(end_index);
                }

                while matches!(
                    (self.kind_at(index), self.kind_at(index + 1)),
                    (
                        Some(TokenKind::Colon),
                        Some(TokenKind::Ident | TokenKind::Star)
                    )
                ) && self.tokens_are_adjacent(end_index, index)
                    && self.tokens_are_adjacent(index, index + 1)
                {
                    end_index = index + 1;
                    index += 2;
                    while matches!(
                        self.kind_at(index),
                        Some(TokenKind::Ident | TokenKind::Star)
                    ) && self.tokens_are_adjacent(end_index, index)
                    {
                        end_index = index;
                        index += 1;
                    }
                }

                expecting_segment_start = false;
                continue;
            }

            if matches!(self.kind_at(index), Some(TokenKind::Pipe))
                && self.tokens_are_adjacent(end_index, index)
            {
                end_index = index;
                index += 1;
                expecting_segment_start = true;
                continue;
            }

            if matches!(
                (self.kind_at(index), self.kind_at(index + 1)),
                (Some(TokenKind::Dot), Some(TokenKind::Ident))
            ) && self.tokens_are_adjacent(end_index, index)
                && self.tokens_are_adjacent(index, index + 1)
            {
                end_index = index + 1;
                index += 2;
                continue;
            }

            if self.tokens_are_adjacent(end_index, index)
                && let Some(suffix_end) = self.bareword_bracket_suffix_end(index)
            {
                end_index = suffix_end;
                index = suffix_end + 1;
                continue;
            }

            while matches!(
                self.kind_at(index),
                Some(TokenKind::Ident | TokenKind::Star)
            ) && self.tokens_are_adjacent(end_index, index)
            {
                end_index = index;
                index += 1;
            }

            break;
        }

        if expecting_segment_start {
            return None;
        }

        Some(end_index)
    }

    pub(super) fn bareword_bracket_suffix_end(&mut self, start_index: usize) -> Option<usize> {
        if self.token_at(start_index).kind != TokenKind::LBracket {
            return None;
        }

        match (
            self.kind_at(start_index + 1),
            self.kind_at(start_index + 2),
            self.kind_at(start_index + 3),
        ) {
            (Some(TokenKind::IntLiteral), Some(TokenKind::RBracket), _) => Some(start_index + 2),
            (Some(TokenKind::Dollar), Some(TokenKind::Ident), Some(TokenKind::RBracket)) => {
                Some(start_index + 3)
            }
            _ => None,
        }
    }
}
