//! Dialect-aware syntax pre-validation using polyglot-sql.
//!
//! Runs before DataFusion's planning to catch dialect-specific syntax errors
//! with better error messages and source locations.
//!
//! Note: polyglot-sql's validate function uses deep recursion that can overflow
//! the default 8MB thread stack. Validation runs on a thread with a larger stack.

use super::dialect_map::to_polyglot_dialect;
use super::types::{PrevalidateError, PrevalidateResult};
use crate::schema::types::SqlDialect;

/// Stack size for validation threads (32MB).
const VALIDATE_THREAD_STACK_SIZE: usize = 32 * 1024 * 1024;

/// Pre-validate SQL syntax using polyglot-sql's dialect-aware parser.
///
/// This catches syntax errors that DataFusion's GenericDialect parser might miss
/// or report with poor error messages. It runs before the DataFusion planning step.
pub fn prevalidate_syntax(sql: &str, dialect: SqlDialect) -> PrevalidateResult {
    let sql = sql.to_string();
    std::thread::Builder::new()
        .stack_size(VALIDATE_THREAD_STACK_SIZE)
        .spawn(move || prevalidate_syntax_inner(&sql, dialect))
        .unwrap_or_else(|e| {
            panic!("failed to spawn pre-validation thread: {e}");
        })
        .join()
        .unwrap_or_else(|_| PrevalidateResult {
            valid: false,
            errors: vec![PrevalidateError {
                message: "pre-validation thread panicked".to_string(),
                code: "INTERNAL".to_string(),
                line: None,
                column: None,
            }],
        })
}

/// Inner implementation of prevalidate_syntax.
fn prevalidate_syntax_inner(sql: &str, dialect: SqlDialect) -> PrevalidateResult {
    let pg_dialect = to_polyglot_dialect(dialect);
    let result = polyglot_sql::validate(sql, pg_dialect);

    if result.valid {
        return PrevalidateResult {
            valid: true,
            errors: vec![],
        };
    }

    let errors = result
        .errors
        .into_iter()
        .filter(|e| e.severity == polyglot_sql::ValidationSeverity::Error)
        .map(|e| PrevalidateError {
            message: e.message,
            code: e.code,
            line: e.line,
            column: e.column,
        })
        .collect();

    PrevalidateResult {
        valid: false,
        errors,
    }
}
