"""String-literal-aware SQL scanning utilities.

Single-pass state-machine scanner for stripping SQL comments and
finding unquoted semicolons while respecting single-quoted strings,
dollar-quoted strings, and nested block comments.

Internal module — do not import directly.
"""

from __future__ import annotations

import re

__all__: list[str] = []

_DOLLAR_TAG_RE = re.compile(r"\$([A-Za-z_][\w]*)?(\$)")


def strip_sql_comments(content: str) -> str:
    """Remove SQL comments while respecting string literals.

    Handles:
    - Single-quoted strings with ``''`` escape (``'it''s'``).
    - Dollar-quoted strings (``$$body$$``, ``$tag$body$tag$``).
    - Nested block comments (``/* outer /* inner */ still comment */``).
    - Line comments (``-- ...``).
    """
    out: list[str] = []
    i = 0
    n = len(content)

    while i < n:
        c = content[i]

        # --- single-quoted string ---
        if c == "'":
            j = i + 1
            while j < n:
                if content[j] == "'" and j + 1 < n and content[j + 1] == "'":
                    j += 2  # escaped ''
                elif content[j] == "'":
                    j += 1
                    break
                else:
                    j += 1
            out.append(content[i:j])
            i = j
            continue

        # --- dollar-quoted string ---
        if c == "$":
            m = _DOLLAR_TAG_RE.match(content, i)
            if m:
                tag = content[i : m.end()]  # e.g. $$ or $tag$
                end = content.find(tag, m.end())
                if end != -1:
                    out.append(content[i : end + len(tag)])
                    i = end + len(tag)
                else:
                    out.append(content[i:])
                    i = n
                continue

        # --- block comment (supports nesting) ---
        if c == "/" and i + 1 < n and content[i + 1] == "*":
            depth = 1
            j = i + 2
            while j < n and depth > 0:
                if content[j] == "/" and j + 1 < n and content[j + 1] == "*":
                    depth += 1
                    j += 2
                elif content[j] == "*" and j + 1 < n and content[j + 1] == "/":
                    depth -= 1
                    j += 2
                else:
                    j += 1
            i = j
            continue

        # --- line comment ---
        if c == "-" and i + 1 < n and content[i + 1] == "-":
            j = i + 2
            while j < n and content[j] != "\n":
                j += 1
            i = j
            continue

        out.append(c)
        i += 1

    return "".join(out)


def find_unquoted_semicolon(content: str, start: int = 0) -> int:
    """Find the first ``;`` not inside a string literal.

    Returns the index of the semicolon, or ``-1`` if not found.
    Respects single-quoted strings (with ``''`` escapes) and
    dollar-quoted strings.
    """
    i = start
    n = len(content)

    while i < n:
        c = content[i]

        if c == ";":
            return i

        # skip single-quoted string
        if c == "'":
            i += 1
            while i < n:
                if content[i] == "'" and i + 1 < n and content[i + 1] == "'":
                    i += 2
                elif content[i] == "'":
                    i += 1
                    break
                else:
                    i += 1
            continue

        # skip dollar-quoted string
        if c == "$":
            m = _DOLLAR_TAG_RE.match(content, i)
            if m:
                tag = content[i : m.end()]
                end = content.find(tag, m.end())
                i = (end + len(tag)) if end != -1 else n
                continue

        i += 1

    return -1
