"""Error feedback retry loop for SQL generation."""

from __future__ import annotations

import logging
from typing import Any, Callable, Awaitable

from local_llm.error_analyzer import ErrorAnalyzer

logger = logging.getLogger(__name__)


class RetryLoop:
    """Manages the retry loop when SQL execution fails.

    Flow:
    1. SQL fails → ErrorAnalyzer produces sanitized feedback
    2. Sanitized feedback sent to Claude for retry (NO raw errors)
    3. Repeat up to max_retries times
    """

    def __init__(self, error_analyzer: ErrorAnalyzer, max_retries: int = 3) -> None:
        self._analyzer = error_analyzer
        self._max_retries = max_retries

    async def execute_with_retry(
        self,
        generate_sql: Callable[[str | None], Awaitable[str]],
        execute_sql: Callable[[str], Awaitable[tuple[list[dict], list[str]]]],
        validate_sql: Callable[[str], str],
    ) -> dict:
        """Execute the generate → validate → execute loop with retries.

        Args:
            generate_sql: Async function that takes optional feedback and returns SQL
            execute_sql: Async function that takes SQL and returns (rows, columns)
            validate_sql: Function that validates SQL and returns cleaned SQL or raises

        Returns:
            {"success": bool, "sql": str, "rows": list, "columns": list, "attempts": int, "error": str | None}
        """
        feedback = None

        for attempt in range(1, self._max_retries + 1):
            logger.info(f"SQL generation attempt {attempt}/{self._max_retries}")

            # 1. Generate SQL (with feedback from previous failure if any)
            try:
                sql = await generate_sql(feedback)
            except Exception as e:
                logger.error(f"SQL generation failed: {e}")
                return {
                    "success": False,
                    "sql": None,
                    "rows": [],
                    "columns": [],
                    "attempts": attempt,
                    "error": "Failed to generate SQL query.",
                }

            # 2. Validate SQL
            try:
                validated_sql = validate_sql(sql)
            except Exception as e:
                feedback = f"The generated SQL failed validation: {str(e)}"
                logger.warning(f"SQL validation failed (attempt {attempt}): {e}")
                continue

            # 3. Execute SQL
            try:
                rows, columns = await execute_sql(validated_sql)
                return {
                    "success": True,
                    "sql": validated_sql,
                    "rows": rows,
                    "columns": columns,
                    "attempts": attempt,
                    "error": None,
                }
            except Exception as e:
                error_msg = str(e)
                logger.warning(f"SQL execution failed (attempt {attempt}): {error_msg}")

                # Analyze error locally — produce safe abstract feedback
                try:
                    feedback = await self._analyzer.analyze(validated_sql, error_msg)
                except Exception:
                    feedback = ErrorAnalyzer.quick_analyze(error_msg)

                logger.info(f"Sanitized feedback for Claude: {feedback}")

        return {
            "success": False,
            "sql": sql if "sql" in dir() else None,
            "rows": [],
            "columns": [],
            "attempts": self._max_retries,
            "error": feedback or "Failed to generate a valid query after multiple attempts.",
        }
