"""
SPARQL syntax validation utilities.

Uses rdflib's SPARQL parser to catch malformed queries before they are
sent to the server, giving faster and clearer error messages.
"""

import re
import sys
from typing import Tuple


def validate_sparql_syntax(sparql: str) -> Tuple[bool, str]:
    """Validate SPARQL query syntax without executing it.

    Runs two levels of checking:
      1. Lightweight structural checks (balanced braces, required keywords).
      2. Full grammar parse via rdflib's SPARQL parser (if available).

    Args:
        sparql: The SPARQL query string to validate.

    Returns:
        A tuple of (is_valid, error_message).
        If valid, error_message is an empty string.
    """
    if not sparql or not sparql.strip():
        return False, "Query is empty"

    query = sparql.strip()

    # --- Level 1: fast structural checks ---
    ok, msg = _check_structure(query)
    if not ok:
        return False, msg

    # --- Level 2: rdflib grammar parse ---
    ok, msg = _check_rdflib_parse(query)
    if not ok:
        return False, msg

    return True, ""


# ---------------------------------------------------------------------------
# Internal helpers
# ---------------------------------------------------------------------------

def _check_structure(query: str) -> Tuple[bool, str]:
    """Quick structural checks that catch the most common typos."""

    # Must contain a recognised SPARQL verb
    upper = query.upper()
    verbs = ("SELECT", "ASK", "CONSTRUCT", "DESCRIBE",
             "INSERT", "DELETE", "LOAD", "CLEAR", "DROP", "CREATE", "WITH")
    if not any(upper.lstrip().startswith(v) for v in verbs):
        # Could still have a PREFIX block before the verb — search deeper
        stripped = re.sub(r'(?i)PREFIX\s+\S+\s+<[^>]*>\s*', '', upper).strip()
        if not any(stripped.startswith(v) for v in verbs):
            return False, "Query does not start with a recognised SPARQL keyword (SELECT, ASK, INSERT, …)"

    # Balanced braces
    opens = query.count('{')
    closes = query.count('}')
    if opens != closes:
        return False, f"Unbalanced curly braces: {opens} opening vs {closes} closing"

    # Balanced parentheses
    opens_p = query.count('(')
    closes_p = query.count(')')
    if opens_p != closes_p:
        return False, f"Unbalanced parentheses: {opens_p} opening vs {closes_p} closing"

    # SELECT must have a WHERE (unless it's a sub-select inside an update)
    if upper.lstrip().startswith("SELECT") and "WHERE" not in upper:
        return False, "SELECT query is missing a WHERE clause"

    return True, ""


def _check_rdflib_parse(query: str) -> Tuple[bool, str]:
    """Attempt a full SPARQL grammar parse via rdflib."""
    try:
        from rdflib.plugins.sparql.parser import parseQuery, parseUpdate
    except ImportError:
        # rdflib not available — skip grammar check silently
        return True, ""

    # Decide whether this looks like an update or a query
    upper = re.sub(r'(?i)PREFIX\s+\S+\s+<[^>]*>\s*', '', query).strip().upper()
    is_update = any(upper.startswith(v) for v in ("INSERT", "DELETE", "LOAD", "CLEAR", "DROP", "CREATE", "WITH"))

    try:
        if is_update:
            parseUpdate(query)
        else:
            parseQuery(query)
    except Exception as e:
        # rdflib raises ParseException or similar — extract the useful part
        error_text = str(e)
        # Trim overly verbose rdflib output to the first meaningful line
        first_line = error_text.split('\n')[0].strip()
        return False, f"SPARQL parse error: {first_line}"

    return True, ""
