"""Static methods to support ReflowPoint.respace_point()."""

import logging
from collections import defaultdict
from typing import TYPE_CHECKING, Optional, cast

from sqlfluff.core.errors import SQLFluffUserError
from sqlfluff.core.parser import (
    BaseSegment,
    PositionMarker,
    RawSegment,
    WhitespaceSegment,
)
from sqlfluff.core.rules import LintFix, LintResult
from sqlfluff.utils.reflow.helpers import pretty_segment_name

if TYPE_CHECKING:  # pragma: no cover
    from sqlfluff.utils.reflow.elements import ReflowBlock


# We're in the utils module, but users will expect reflow
# logs to appear in the context of rules. Hence it's a subset
# of the rules logger.
reflow_logger = logging.getLogger("sqlfluff.rules.reflow")


# ---------------------------
# Small helper functions
# ---------------------------


def _construct_alignment_whitespace(width: int, indent_unit: str) -> str:
    """Construct alignment whitespace using tabs or spaces.

    Args:
        width: Number of whitespace characters needed.
        indent_unit: Either 'tab' or 'space'.

    Returns:
        A string of whitespace (tabs or spaces) of the specified width.
    """
    if indent_unit == "tab":
        return "\t" * width
    else:
        # For 'space' or any space string value
        return " " * width


def _pos_line(pm: PositionMarker, use_source: bool) -> int:
    """Return the line number in the chosen coordinate space."""
    return pm.line_no if use_source else pm.working_line_no


def _pos_col(
    pm: PositionMarker,
    use_source: bool,
    tab_space_size: int = 4,
    indent_unit: str = "space",
) -> int:
    """Return the column (position) in the chosen coordinate space.

    Args:
        pm: Position marker to get column from.
        use_source: If True, use source position; if False, use working position.
        tab_space_size: Number of spaces per tab stop
            (used for visual column calculation).
        indent_unit: The indent unit ('tab' or 'space') to determine if
            visual columns needed.

    Returns:
        Column position in the chosen coordinate space.
    """
    if use_source:
        return pm.line_pos
    # For working positions with tabs, use visual columns;
    # otherwise use character positions
    if indent_unit == "tab":
        return pm.working_visual_column(tab_space_size)
    return pm.working_line_pos


def _has_templated_content(
    parent_segment: BaseSegment, siblings: list[BaseSegment], next_seg: RawSegment
) -> bool:
    """Check if any participating segments contain templated content.

    Returns True if any of the participating segments are non-literal (templated)
    or if any code tokens in the alignment parent are non-literal.
    """
    try:
        if next_seg.pos_marker and not next_seg.pos_marker.is_literal():
            return True  # pragma: no cover
        if any(s.pos_marker and not s.pos_marker.is_literal() for s in siblings):
            return True  # pragma: no cover
        for rs in parent_segment.raw_segments:
            if rs.is_code and rs.pos_marker and not rs.pos_marker.is_literal():
                return True
    except AttributeError:  # pragma: no cover
        # Handle missing pos_marker attributes gracefully
        return False
    return False


def _group_siblings_by_line(
    siblings: list[BaseSegment],
    use_source: bool,
    tab_space_size: int = 4,
    indent_unit: str = "space",
) -> dict[int, list[BaseSegment]]:
    """Group sibling segments by line and sort by column within each line."""
    grouped: dict[int, list[BaseSegment]] = defaultdict(list)
    for sibling in siblings:
        _pos = sibling.pos_marker
        assert _pos
        grouped[_pos_line(_pos, use_source)].append(sibling)
    # Sort each line's segments by column position in the chosen coordinate space
    for line_siblings in grouped.values():
        line_siblings.sort(
            key=lambda s: _pos_col(
                cast(PositionMarker, s.pos_marker),
                use_source,
                tab_space_size,
                indent_unit,
            )
        )
    return grouped


def _unpack_constraint(constraint: str, strip_newlines: bool) -> tuple[str, bool]:
    """Unpack a spacing constraint.

    Used as a helper function in `determine_constraints`.
    """
    # Check for deprecated options.
    if constraint == "inline":  # pragma: no cover
        reflow_logger.warning(
            "Found 'inline' specified as a 'spacing_within' constraint. "
            "This setting is deprecated and has been replaced by the more "
            "explicit 'touch:inline'. Upgrade your configuration to "
            "remove this warning."
        )
        constraint = "touch:inline"

    # Unless align, split.
    if constraint.startswith("align"):
        modifier = ""
    else:
        constraint, _, modifier = constraint.partition(":")

    if not modifier:
        pass
    elif modifier == "inline":
        strip_newlines = True
    else:  # pragma: no cover
        raise SQLFluffUserError(f"Unexpected constraint modifier: {constraint!r}")

    return constraint, strip_newlines


def determine_constraints(
    prev_block: Optional["ReflowBlock"],
    next_block: Optional["ReflowBlock"],
    strip_newlines: bool = False,
) -> tuple[str, str, bool]:
    """Given the surrounding blocks, determine appropriate constraints."""
    # Start with the defaults.
    pre_constraint, strip_newlines = _unpack_constraint(
        prev_block.spacing_after if prev_block else "single", strip_newlines
    )
    post_constraint, strip_newlines = _unpack_constraint(
        next_block.spacing_before if next_block else "single", strip_newlines
    )

    # Work out the common parent segment and depth
    within_spacing = ""
    if prev_block and next_block:
        common = prev_block.depth_info.common_with(next_block.depth_info)
        # Just check the most immediate parent for now for speed.
        # TODO: Review whether just checking the parent is enough.
        # NOTE: spacing configs will be available on both sides if they're common
        # so it doesn't matter whether we get it from prev_block or next_block.
        idx = prev_block.depth_info.stack_hashes.index(common[-1])

        within_constraint = prev_block.stack_spacing_configs.get(common[-1], None)
        if within_constraint:
            within_spacing, strip_newlines = _unpack_constraint(
                within_constraint, strip_newlines
            )
        # Prohibit stripping newlines adjacent to comment segments (either
        # immediately before or immediately after this point), since doing
        # so could glue code to a comment marker and change meaning.
        if any(seg.is_type("comment") for seg in prev_block.segments) or any(
            seg.is_type("comment") for seg in next_block.segments
        ):
            strip_newlines = False

    # If segments are expected to be touch within. Then modify
    # constraints accordingly.
    if within_spacing == "touch":
        # NOTE: We don't override if it's already "any"
        if pre_constraint != "any":
            pre_constraint = "touch"
        if post_constraint != "any":
            post_constraint = "touch"
    elif within_spacing == "any":
        pre_constraint = "any"
        post_constraint = "any"
    elif within_spacing == "single":
        pass
    elif within_spacing:  # pragma: no cover
        assert prev_block
        raise SQLFluffUserError(
            f"Unexpected within constraint: {within_constraint!r} for "
            f"{prev_block.depth_info.stack_class_types[idx]}"
        )

    return pre_constraint, post_constraint, strip_newlines


def process_spacing(
    segment_buffer: list[RawSegment], strip_newlines: bool = False
) -> tuple[list[RawSegment], Optional[RawSegment], list[LintResult]]:
    """Given the existing spacing, extract information and do basic pruning."""
    removal_buffer: list[RawSegment] = []
    result_buffer: list[LintResult] = []
    last_whitespace: list[RawSegment] = []

    # Loop through the existing segments looking for spacing.
    for seg in segment_buffer:
        # If it's whitespace, store it.
        if seg.is_type("whitespace"):
            last_whitespace.append(seg)

        # If it's a newline, react accordingly.
        # NOTE: This should only trigger on literal newlines.
        elif seg.is_type("newline", "end_of_file"):
            if seg.pos_marker and not seg.pos_marker.is_literal():
                last_whitespace = []
                reflow_logger.debug("    Skipping templated newline: %s", seg)
                continue

            # Are we stripping newlines?
            if strip_newlines and seg.is_type("newline"):
                reflow_logger.debug("    Stripping newline: %s", seg)
                removal_buffer.append(seg)
                result_buffer.append(
                    LintResult(
                        seg, [LintFix.delete(seg)], description="Unexpected line break."
                    )
                )
                # Carry on as though it wasn't here.
                continue

            # Check if we've just passed whitespace. If we have, remove it
            # as trailing whitespace, both from the buffer and create a fix.
            if last_whitespace:
                reflow_logger.debug("    Removing trailing whitespace.")
                for ws in last_whitespace:
                    removal_buffer.append(ws)
                    result_buffer.append(
                        LintResult(
                            ws,
                            [LintFix.delete(ws)],
                            description="Unnecessary trailing whitespace.",
                        )
                    )

            # Regardless, unset last_whitespace.
            # We either just deleted it, or it's not relevant for any future
            # segments.
            last_whitespace = []

    if len(last_whitespace) >= 2:
        reflow_logger.debug("   Removing adjoining whitespace.")
        # If we find multiple sequential whitespaces, it's the sign
        # that we've removed something. Only the first one should be
        # a valid indent (or the one we consider for constraints).
        # Remove all the following ones.
        for ws in last_whitespace[1:]:
            removal_buffer.append(ws)
            result_buffer.append(
                LintResult(
                    seg,
                    [LintFix.delete(seg)],
                    description="Removing duplicate whitespace.",
                )
            )

    # Turn the removal buffer updated segment buffer, last whitespace
    # and associated fixes.
    return (
        [s for s in segment_buffer if s not in removal_buffer],
        # We should have removed all other whitespace by now.
        last_whitespace[0] if last_whitespace else None,
        result_buffer,
    )


def _determine_aligned_inline_spacing(
    root_segment: BaseSegment,
    whitespace_seg: RawSegment,
    next_seg: RawSegment,
    next_pos: PositionMarker,
    segment_type: str,
    align_within: Optional[str],
    align_scope: Optional[str],
    align_space: Optional[str],
    indent_unit: str = "space",
    tab_space_size: int = 4,
) -> str:
    """Work out spacing for instance of an `align` constraint.

    Args:
        root_segment: The root segment to search within.
        whitespace_seg: The whitespace segment being modified.
        next_seg: The segment immediately following the whitespace.
        next_pos: Position marker for the next segment.
        segment_type: Type of segments to align (e.g., 'alias_expression').
        align_within: Parent segment type to limit alignment scope.
        align_scope: Further scope limitation (e.g., 'bracketed').
        align_space: Coordinate space override ('source', 'templated', or None).
        indent_unit: The indent unit to use ('tab', 'space', or a space string).
        tab_space_size: The number of spaces per tab (used when indent_unit='tab').

    Returns:
        A string of whitespace (tabs or spaces) to achieve proper alignment.

    Note:
        When align_space is None, automatically chooses 'source' coordinates if
        templated content is detected, otherwise uses 'templated' coordinates.
        See https://github.com/sqlfluff/sqlfluff/issues/5429 for rationale.
    """
    # Find the parent segment within which we're aligning
    parent_segment = None
    if align_within:
        for ps in root_segment.path_to(
            next_seg if next_seg.pos_marker else whitespace_seg
        )[::-1]:
            if ps.segment.is_type(align_within):
                parent_segment = ps.segment
            if align_scope and ps.segment.is_type(align_scope):
                break

    if not parent_segment:
        reflow_logger.debug("    No Parent found for alignment case. Treat as single.")
        return " "

    # We've got a parent. Find some siblings.
    reflow_logger.debug("    Determining alignment within: %s", parent_segment)
    siblings = []
    for sibling in parent_segment.recursive_crawl(segment_type):
        # Purge any siblings with a boundary between them
        if not align_scope or not any(
            ps.segment.is_type(align_scope) for ps in parent_segment.path_to(sibling)
        ):
            siblings.append(sibling)
        else:
            reflow_logger.debug(
                "    Purging a sibling because they're blocked by a boundary: %s",
                sibling,
            )

    # Use the segment's position marker if available, fallback to provided position
    if next_seg.pos_marker:
        next_pos = next_seg.pos_marker

    # Choose coordinate space: source (visible) vs templated (rendered)
    if align_space == "source":
        use_source_positions = True
    elif align_space == "templated":
        use_source_positions = False
    else:
        use_source_positions = _has_templated_content(
            parent_segment, siblings, next_seg
        )
    reflow_logger.debug(
        "    Alignment coordinate space: %s",
        "source" if use_source_positions else "templated",
    )

    # Group siblings by line using the chosen coordinate space
    siblings_by_line = _group_siblings_by_line(
        siblings, use_source_positions, tab_space_size, indent_unit
    )

    # Identify the alignment column index for the current line
    current_line_key = _pos_line(next_pos, use_source_positions)
    current_line_segments = siblings_by_line.get(current_line_key, [])

    # Handle edge case where no segments are found on the current line
    # This is defensive: in practice siblings should exist on the line, but this
    # provides a safe fallback. Excluded from coverage as it's difficult to trigger.
    if not current_line_segments:  # pragma: no cover
        reflow_logger.debug(
            "    No segments found on current line for alignment. Treat as single."
        )
        return " "

    target_index = next(
        (
            idx
            for idx, segment in enumerate(current_line_segments)
            if (
                _pos_col(
                    cast(PositionMarker, segment.pos_marker),
                    use_source_positions,
                    tab_space_size,
                    indent_unit,
                )
                == _pos_col(next_pos, use_source_positions, tab_space_size, indent_unit)
            )
        ),
        None,  # Default value if no match found
    )

    # Handle case where target segment is not found
    # This is defensive: the target should normally be found among siblings, but
    # provides a safe fallback. Excluded from coverage as it's difficult to trigger.
    if target_index is None:  # pragma: no cover
        reflow_logger.debug(
            "    Target segment not found in current line. Treat as single."
        )
        return " "

    # Now that we know the target index, we can extract the relevant segment from
    # all lines
    siblings = [
        segment
        for segments in siblings_by_line.values()
        for segment in (
            [segments[target_index]] if target_index < len(segments) else []
        )
    ]

    # If there's only one sibling, we have nothing to compare to. Default to a single
    # space.
    if len(siblings) <= 1:
        desired_space = " "
        reflow_logger.debug(
            "    desired_space: %r (based on no other siblings)",
            desired_space,
        )
        return desired_space

    # Work out the current spacing before each.
    last_code: Optional[RawSegment] = None
    max_desired_line_pos = 0
    for seg in parent_segment.raw_segments:
        for sibling in siblings:
            # NOTE: We're asserting that there must have been
            # a last_code. Otherwise this won't work.
            if (
                seg.pos_marker
                and sibling.pos_marker
                and seg.pos_marker.working_loc == sibling.pos_marker.working_loc
                and last_code
            ):
                if use_source_positions:
                    end_pm = last_code.pos_marker.end_point_marker()
                    loc = (
                        _pos_line(end_pm, True),
                        _pos_col(end_pm, True, tab_space_size, indent_unit),
                    )
                else:
                    loc = last_code.pos_marker.working_loc_after(last_code.raw)
                reflow_logger.debug(
                    "    loc for %s: %s from %s",
                    sibling,
                    loc,
                    last_code,
                )
                # When using tabs, convert to visual column position
                if indent_unit == "tab" and last_code.pos_marker:
                    # Get visual position after the last code segment
                    # Use end_point_marker to get position after the segment
                    end_pm = last_code.pos_marker.end_point_marker()
                    visual_pos = end_pm.working_visual_column(tab_space_size)
                    if visual_pos > max_desired_line_pos:
                        max_desired_line_pos = visual_pos
                else:
                    if loc[1] > max_desired_line_pos:
                        max_desired_line_pos = loc[1]
        if seg.is_code:
            last_code = seg

    # Compute desired whitespace size in the chosen coordinate space.
    # Ensure we always return at least a single space to avoid deleting
    # whitespace when the current position already exceeds the target.

    # Find the code segment immediately before whitespace_seg on the same line
    # This is more reliable than using last_code from the loop above, which may
    # refer to code segments from other lines or after the whitespace.
    code_before_ws: Optional[RawSegment] = None
    if whitespace_seg.pos_marker:
        ws_line = (
            whitespace_seg.pos_marker.line_no
            if use_source_positions
            else whitespace_seg.pos_marker.working_line_no
        )
        ws_loc = (
            (whitespace_seg.pos_marker.line_no, whitespace_seg.pos_marker.line_pos)
            if use_source_positions
            else whitespace_seg.pos_marker.working_loc
        )

        for seg in parent_segment.raw_segments:
            if not seg.pos_marker:  # pragma: no cover
                continue
            seg_loc = (
                (seg.pos_marker.line_no, seg.pos_marker.line_pos)
                if use_source_positions
                else seg.pos_marker.working_loc
            )
            if seg_loc >= ws_loc:
                break
            seg_line = (
                seg.pos_marker.line_no
                if use_source_positions
                else seg.pos_marker.working_line_no
            )
            if seg.is_code and seg_line == ws_line:
                code_before_ws = seg

    # Calculate current position after the code segment before whitespace
    if code_before_ws and code_before_ws.pos_marker:
        if use_source_positions or indent_unit == "tab":
            # For source positions or tabs, use _pos_col which handles visual columns
            end_pm = code_before_ws.pos_marker.end_point_marker()
            current_ws_pos = _pos_col(
                end_pm, use_source_positions, tab_space_size, indent_unit
            )
        else:
            # For working positions without tabs, use working_loc_after for accuracy
            loc = code_before_ws.pos_marker.working_loc_after(code_before_ws.raw)
            current_ws_pos = loc[1]
    else:  # pragma: no cover
        # Fallback: use whitespace position if no code_before_ws found
        current_ws_pos = _pos_col(
            whitespace_seg.pos_marker, use_source_positions, tab_space_size, indent_unit
        )

    pad_width = max(1, 1 + max_desired_line_pos - current_ws_pos)

    # When using tabs, calculate tabs needed to reach the target tab stop
    if indent_unit == "tab":
        # Find the target column: first tab stop >= (max_desired_line_pos + 1)
        # The +1 ensures at least one space before the aligned element
        target_column = ((max_desired_line_pos // tab_space_size) + 1) * tab_space_size

        # Calculate how many tabs needed to reach target_column from current position
        # Tabs move to tab stops (multiples of tab_space_size), not by fixed increments
        tab_count = 0
        pos = current_ws_pos
        while pos < target_column:
            # Move to next tab stop
            pos = ((pos // tab_space_size) + 1) * tab_space_size
            tab_count += 1

        desired_space = _construct_alignment_whitespace(tab_count, "tab")
    else:
        desired_space = _construct_alignment_whitespace(pad_width, "space")

    reflow_logger.debug(
        "    desired_space: %r (based on max line pos of %s)",
        desired_space,
        max_desired_line_pos,
    )
    return desired_space


def _extract_alignment_config(
    constraint: str,
) -> tuple[str, Optional[str], Optional[str], Optional[str]]:
    """Helper function to break apart an alignment config.

    Returns:
        Tuple of (segment_type, align_within, align_scope, align_space)

        - segment_type: The type of segments to align
        - align_within: Parent segment type to limit alignment scope
        - align_scope: Further scope limitation (e.g., 'bracketed')
        - align_space: Coordinate space ('source', 'templated', or None)

    >>> _extract_alignment_config("align:alias_expression")
    ('alias_expression', None, None, None)
    >>> _extract_alignment_config("align:alias_expression:statement")
    ('alias_expression', 'statement', None, None)
    >>> _extract_alignment_config("align:alias_expression:statement:bracketed")
    ('alias_expression', 'statement', 'bracketed', None)
    >>> _extract_alignment_config("align:alias_expression:select_clause:bracketed:source")
    ('alias_expression', 'select_clause', 'bracketed', 'source')
    >>> _extract_alignment_config("align:alias_expression:select_clause:bracketed:templated")
    ('alias_expression', 'select_clause', 'bracketed', 'templated')
    """
    assert ":" in constraint
    alignment_config = constraint.split(":")
    assert alignment_config[0] == "align"
    seg_type = alignment_config[1]
    align_within = alignment_config[2] if len(alignment_config) > 2 else None
    align_scope = alignment_config[3] if len(alignment_config) > 3 else None
    align_space = alignment_config[4] if len(alignment_config) > 4 else None
    reflow_logger.debug(
        "    Alignment Config: %s, %s, %s",
        seg_type,
        align_within,
        align_scope,
    )
    return seg_type, align_within, align_scope, align_space


def handle_respace__inline_with_space(
    pre_constraint: str,
    post_constraint: str,
    prev_block: Optional["ReflowBlock"],
    next_block: Optional["ReflowBlock"],
    root_segment: BaseSegment,
    segment_buffer: list[RawSegment],
    last_whitespace: RawSegment,
    indent_unit: str = "space",
    tab_space_size: int = 4,
) -> tuple[list[RawSegment], list[LintResult]]:
    """Check inline spacing is the right size.

    This forms one of the cases handled by .respace_point().

    This code assumes:
    - a ReflowPoint with no newlines.
    - a ReflowPoint which has _some_ whitespace.

    Given this we apply constraints to ensure the whitespace
    is of an appropriate size.
    """
    # Get some indices so that we can reference around them
    ws_idx = segment_buffer.index(last_whitespace)

    # Do we have either side set to "any"
    if "any" in [pre_constraint, post_constraint]:
        # In this instance - don't change anything.
        # e.g. this could mean there is a comment on one side.
        return segment_buffer, []

    # Do we have either side set to "touch"?
    if "touch" in [pre_constraint, post_constraint]:
        # In this instance - no whitespace is correct, This
        # means we should delete it.
        segment_buffer.pop(ws_idx)
        if next_block:
            description = (
                "Unexpected whitespace before "
                f"{pretty_segment_name(next_block.segments[0])}."
            )
        else:  # pragma: no cover
            # This clause has no test coverage because next_block is
            # normally provided.
            description = "Unexpected whitespace"

        return segment_buffer, [
            LintResult(
                last_whitespace,
                [LintFix.delete(last_whitespace)],
                # Should make description from constraints.
                description=description,
            ),
        ]

    # Handle left alignment & singles
    if (
        post_constraint.startswith("align") and next_block
    ) or pre_constraint == post_constraint == "single":
        # Determine the desired spacing, either as alignment or as a single.
        if post_constraint.startswith("align") and next_block:
            seg_type, align_within, align_scope, align_space = (
                _extract_alignment_config(post_constraint)
            )

            next_pos: Optional[PositionMarker]
            if next_block.segments[0].pos_marker:
                next_pos = next_block.segments[0].pos_marker
            # Excluded from coverage: no longer triggered since AL01 rule was refactored
            elif last_whitespace.pos_marker:  # pragma: no cover
                next_pos = last_whitespace.pos_marker.end_point_marker()
            # These second clauses are much less likely and so are excluded from
            # coverage. If we find a way of covering them, that would be great
            # but for now they exist as backups.
            elif prev_block and prev_block.segments[-1].pos_marker:  # pragma: no cover
                next_pos = prev_block.segments[-1].pos_marker.end_point_marker()
            else:  # pragma: no cover
                reflow_logger.info("Unable to find position marker for alignment.")
                next_pos = None
                desired_space = " "
                desc = f"Expected only single space. Found {last_whitespace.raw!r}."

            if next_pos:
                desired_space = _determine_aligned_inline_spacing(
                    root_segment,
                    last_whitespace,
                    next_block.segments[0],
                    next_pos,
                    seg_type,
                    align_within,
                    align_scope,
                    align_space,
                    indent_unit,
                    tab_space_size,
                )

                desc = (
                    f"{seg_type!r} elements are expected to be aligned. Found "
                    "incorrect whitespace before "
                    f"{pretty_segment_name(next_block.segments[0])}: "
                    f"{last_whitespace.raw!r}."
                )
        else:
            if next_block:
                desc = (
                    "Expected only single space before "
                    f"{pretty_segment_name(next_block.segments[0])}. Found "
                    f"{last_whitespace.raw!r}."
                )
            else:  # pragma: no cover
                # This clause isn't has no test coverage because next_block is
                # normally provided.
                desc = f"Expected only single space. Found {last_whitespace.raw!r}."
            desired_space = " "

        new_results: list[LintResult] = []

        if last_whitespace.raw != desired_space:
            new_seg = last_whitespace.edit(desired_space)
            new_results.append(
                LintResult(
                    last_whitespace,
                    [
                        LintFix(
                            "replace",
                            anchor=last_whitespace,
                            edit=[new_seg],
                        )
                    ],
                    description=desc,
                )
            )
            segment_buffer[ws_idx] = new_seg

        return segment_buffer, new_results

    raise NotImplementedError(  # pragma: no cover
        f"Unexpected Constraints: {pre_constraint}, {post_constraint}"
    )


def handle_respace__inline_without_space(
    pre_constraint: str,
    post_constraint: str,
    prev_block: Optional["ReflowBlock"],
    next_block: Optional["ReflowBlock"],
    segment_buffer: list[RawSegment],
    existing_results: list[LintResult],
    anchor_on: str = "before",
) -> tuple[list[RawSegment], list[LintResult], bool]:
    """Ensure spacing is the right size.

    This forms one of the cases handled by .respace_point().

    This code assumes:
    - a ReflowPoint with no newlines.
    - a ReflowPoint which _no_ whitespace.

    Given this we apply constraints to either confirm no
    spacing is required or create some of the right size.
    """
    # Do we have either side set to "touch" or "any"
    if {"touch", "any"}.intersection([pre_constraint, post_constraint]):
        # In this instance - no whitespace is correct.
        # Either because there shouldn't be, or because "any"
        # means we shouldn't check.
        return segment_buffer, existing_results, False
    # Are we supposed to be aligning?
    elif post_constraint.startswith("align"):
        reflow_logger.debug("    Inserting Aligned Whitespace.")
        # TODO: We currently rely on a second pass to align
        # insertions. This is where we could devise alignment
        # in advance, but most of the alignment code relies on
        # having existing position markers for those insertions.
        # https://github.com/sqlfluff/sqlfluff/issues/4492
        desired_space = " "
        added_whitespace = WhitespaceSegment(desired_space)
    # Is it anything other than the default case?
    elif not (pre_constraint == post_constraint == "single"):  # pragma: no cover
        # TODO: This will get test coverage when configuration routines
        # are in properly.
        raise NotImplementedError(
            f"Unexpected Constraints: {pre_constraint}, {post_constraint}"
        )
    else:
        # Default to a single whitespace
        reflow_logger.debug("    Inserting Single Whitespace.")
        added_whitespace = WhitespaceSegment()

    # Add it to the buffer first (the easy bit). The hard bit
    # is to then determine how to generate the appropriate LintFix
    # objects.
    segment_buffer.append(added_whitespace)

    # So special handling here. If segments either side
    # already exist then we don't care which we anchor on
    # but if one is already an insertion (as shown by a lack)
    # of pos_marker, then we should piggy back on that pre-existing
    # fix.
    existing_fix = None
    insertion = None
    if prev_block and not prev_block.segments[-1].pos_marker:
        existing_fix = "after"
        insertion = prev_block.segments[-1]
    elif next_block and not next_block.segments[0].pos_marker:
        existing_fix = "before"
        insertion = next_block.segments[0]

    if existing_fix:
        reflow_logger.debug("    Detected existing fix %s", existing_fix)
        if not existing_results:  # pragma: no cover
            raise ValueError(
                "Fixes detected, but none passed to .respace(). "
                "This will cause conflicts."
            )
        # Find the fix
        assert insertion
        for res in existing_results:
            # Does it contain the insertion?
            # TODO: This feels ugly - eq for BaseSegment is different
            # to uuid matching for RawSegment. Perhaps this should be
            # more aligned. There might be a better way of doing this.
            for fix in res.fixes or []:
                if fix.edit and insertion.uuid in [elem.uuid for elem in fix.edit]:
                    break
            else:  # pragma: no cover
                continue
            break
        else:  # pragma: no cover
            reflow_logger.warning("Results %s", existing_results)
            raise ValueError(f"Couldn't find insertion for {insertion}")
        # Mutate the existing fix
        assert res
        assert fix
        assert fix in res.fixes
        assert fix.edit  # It's going to be an edit if we've picked it up.
        # Mutate the fix, it's still in the same result, and that result
        # is still in the existing_results.
        if existing_fix == "before":
            fix.edit = [cast(BaseSegment, added_whitespace)] + fix.edit
        elif existing_fix == "after":
            fix.edit = fix.edit + [cast(BaseSegment, added_whitespace)]

        # No need to add new results, because we mutated the existing.
        return segment_buffer, existing_results, True

    # Otherwise...
    reflow_logger.debug("    Not Detected existing fix. Creating new")
    if prev_block and next_block:
        desc = (
            "Expected single whitespace between "
            f"{pretty_segment_name(prev_block.segments[-1])} "
            f"and {pretty_segment_name(next_block.segments[0])}."
        )
    else:  # pragma: no cover
        # Something to fall back on if prev_block and next_block not provided.
        desc = "Expected single whitespace."
    # Take into account hint on where to anchor if given.
    if prev_block and anchor_on != "after":
        new_result = LintResult(
            # We do this shuffle, because for the CLI it's clearer if the
            # anchor for the error is at the point that the insertion will
            # happen which is the *start* of the next segment, even if
            # we're anchoring the fix on the previous.
            next_block.segments[0] if next_block else prev_block.segments[-1],
            fixes=[
                LintFix(
                    "create_after",
                    anchor=prev_block.segments[-1],
                    edit=[WhitespaceSegment()],
                )
            ],
            description=desc,
        )
    elif next_block:
        new_result = LintResult(
            next_block.segments[0],
            fixes=[
                LintFix(
                    "create_before",
                    anchor=next_block.segments[0],
                    edit=[WhitespaceSegment()],
                )
            ],
            description=desc,
        )
    else:  # pragma: no cover
        NotImplementedError("Not set up to handle a missing _after_ and _before_.")

    return segment_buffer, existing_results + [new_result], True
