"""Feedback collection and processing for fix learning."""

from __future__ import annotations

import logging
from datetime import timedelta
from typing import TYPE_CHECKING, Any, Dict, Optional

if TYPE_CHECKING:
    from autodebug.learning.search import FixSearchService

logger = logging.getLogger(__name__)


class FeedbackCollector:
    """Collect and process fix feedback for learning."""

    # Feedback is considered stale after this period
    STALE_THRESHOLD = timedelta(days=7)

    # Minimum time between feedback changes
    CHANGE_COOLDOWN = timedelta(minutes=5)

    def __init__(self, db_pool, search_service: Optional[FixSearchService] = None):
        """Initialize feedback collector.

        Args:
            db_pool: Database connection pool.
            search_service: Optional search service for re-indexing.
        """
        self.db = db_pool
        self.search = search_service

    async def record_feedback(
        self,
        fix_id: str,
        feedback: str,
        user_id: Optional[str] = None,
        user_modification: Optional[str] = None,
        notes: Optional[str] = None,
    ) -> Dict[str, Any]:
        """Record user feedback on a fix.

        Args:
            fix_id: ID of the fix.
            feedback: Feedback type ('accepted', 'rejected', 'modified').
            user_id: Optional user ID.
            user_modification: If modified, the user's changes.
            notes: Optional notes from the user.

        Returns:
            Result dictionary with status.
        """
        if feedback not in ("accepted", "rejected", "modified", "ignored"):
            return {"error": "Invalid feedback type"}

        try:
            # Get existing fix
            fix = await self.db.fetchrow(
                "SELECT * FROM fixes WHERE id = $1",
                fix_id,
            )

            if not fix:
                return {"error": "Fix not found"}

            # Get run info for context
            run = await self.db.fetchrow(
                "SELECT * FROM runs WHERE id = $1",
                fix["run_id"],
            )

            # Create or update fix_record for learning
            existing = await self.db.fetchrow(
                "SELECT id FROM fix_records WHERE run_id = $1 AND patch = $2",
                fix["run_id"],
                fix.get("patch", ""),
            )

            if existing:
                # Update existing record
                await self.db.execute(
                    """
                    UPDATE fix_records
                    SET feedback = $1,
                        feedback_at = NOW(),
                        user_modification = $2
                    WHERE id = $3
                    """,
                    feedback,
                    user_modification,
                    existing["id"],
                )
                record_id = existing["id"]
            else:
                # Create new record
                record = await self.db.fetchrow(
                    """
                    INSERT INTO fix_records (
                        run_id, org_id, failure_signature, exception_type,
                        exception_message, failing_file, failing_line,
                        test_name, framework, strategy, patch, root_cause,
                        confidence, feedback, feedback_at, user_modification
                    ) VALUES (
                        $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, NOW(), $15
                    )
                    RETURNING id
                    """,
                    fix["run_id"],
                    run.get("project_id") if run else None,  # Using project as org proxy
                    fix.get("failure_signature", ""),
                    run.get("exception_type", "") if run else "",
                    run.get("exception_message", "") if run else "",
                    run.get("failing_file", "") if run else "",
                    run.get("failing_line") if run else None,
                    run.get("test_name", "") if run else "",
                    "pytest",  # Default framework
                    fix.get("strategy", ""),
                    fix.get("patch", ""),
                    fix.get("root_cause", ""),
                    fix.get("confidence"),
                    feedback,
                    user_modification,
                )
                record_id = record["id"]

            # If accepted, generate and store embedding for future search
            if feedback == "accepted" and self.search:
                fix_record = await self.db.fetchrow(
                    "SELECT * FROM fix_records WHERE id = $1",
                    record_id,
                )
                if fix_record:
                    await self.search.index_fix(dict(fix_record))

            # Update the fix status
            await self.db.execute(
                """
                UPDATE fixes
                SET status = CASE
                    WHEN $2 = 'accepted' THEN 'APPLIED'
                    WHEN $2 = 'rejected' THEN 'REJECTED'
                    WHEN $2 = 'modified' THEN 'APPLIED'
                    ELSE status
                END
                WHERE id = $1
                """,
                fix_id,
                feedback,
            )

            # Record in feedback history
            await self.db.execute(
                """
                INSERT INTO fix_feedback_history (fix_id, user_id, feedback, modification, notes)
                VALUES ($1, $2, $3, $4, $5)
                """,
                fix_id,
                user_id,
                feedback,
                user_modification,
                notes,
            )

            return {
                "status": "recorded",
                "fix_id": fix_id,
                "feedback": feedback,
                "record_id": str(record_id),
            }

        except Exception as e:
            logger.error(f"Failed to record feedback: {e}")
            return {"error": str(e)}

    async def get_feedback_stats(self, org_id: Optional[str] = None) -> Dict[str, Any]:
        """Get feedback statistics.

        Args:
            org_id: Filter by organization (optional).

        Returns:
            Statistics dictionary.
        """
        try:
            stats = await self.db.fetchrow(
                """
                SELECT
                    COUNT(*) as total_fixes,
                    COUNT(*) FILTER (WHERE feedback = 'accepted') as accepted,
                    COUNT(*) FILTER (WHERE feedback = 'rejected') as rejected,
                    COUNT(*) FILTER (WHERE feedback = 'modified') as modified,
                    COUNT(*) FILTER (WHERE feedback IS NULL) as pending,
                    AVG(confidence) FILTER (WHERE feedback = 'accepted') as avg_accepted_confidence,
                    AVG(confidence) FILTER (WHERE feedback = 'rejected') as avg_rejected_confidence,
                    COUNT(DISTINCT exception_type) as unique_exceptions
                FROM fix_records
                WHERE ($1::uuid IS NULL OR org_id = $1)
                """,
                org_id,
            )

            # Get acceptance rate over time
            timeline = await self.db.fetch(
                """
                SELECT
                    DATE_TRUNC('day', created_at) as date,
                    COUNT(*) as total,
                    COUNT(*) FILTER (WHERE feedback = 'accepted') as accepted
                FROM fix_records
                WHERE ($1::uuid IS NULL OR org_id = $1)
                AND created_at > NOW() - INTERVAL '30 days'
                GROUP BY DATE_TRUNC('day', created_at)
                ORDER BY date
                """,
                org_id,
            )

            # Get top exception types
            top_exceptions = await self.db.fetch(
                """
                SELECT
                    exception_type,
                    COUNT(*) as count,
                    COUNT(*) FILTER (WHERE feedback = 'accepted') as accepted,
                    ROUND(
                        100.0 * COUNT(*) FILTER (WHERE feedback = 'accepted') /
                        NULLIF(COUNT(*) FILTER (WHERE feedback IN ('accepted', 'rejected')), 0),
                        1
                    ) as acceptance_rate
                FROM fix_records
                WHERE ($1::uuid IS NULL OR org_id = $1)
                AND exception_type IS NOT NULL
                AND exception_type != ''
                GROUP BY exception_type
                ORDER BY count DESC
                LIMIT 10
                """,
                org_id,
            )

            return {
                "total_fixes": stats["total_fixes"],
                "accepted": stats["accepted"],
                "rejected": stats["rejected"],
                "modified": stats["modified"],
                "pending": stats["pending"],
                "acceptance_rate": (
                    round(100 * stats["accepted"] / max(stats["accepted"] + stats["rejected"], 1), 1)
                ),
                "avg_accepted_confidence": round(stats["avg_accepted_confidence"] or 0, 3),
                "avg_rejected_confidence": round(stats["avg_rejected_confidence"] or 0, 3),
                "unique_exceptions": stats["unique_exceptions"],
                "timeline": [
                    {
                        "date": str(r["date"].date()) if r["date"] else None,
                        "total": r["total"],
                        "accepted": r["accepted"],
                    }
                    for r in timeline
                ],
                "top_exceptions": [
                    {
                        "exception_type": r["exception_type"],
                        "count": r["count"],
                        "accepted": r["accepted"],
                        "acceptance_rate": float(r["acceptance_rate"] or 0),
                    }
                    for r in top_exceptions
                ],
            }

        except Exception as e:
            logger.error(f"Failed to get stats: {e}")
            return {"error": str(e)}
