"""
Feedback Collector

Collects and manages expert feedback for training data validation.
"""

import json
import logging
import uuid
from datetime import datetime
from pathlib import Path
from typing import Any

from sage.data_collection.schemas import ExpertFeedback, FeedbackRating

logger = logging.getLogger(__name__)


class FeedbackCollector:
    """Collects and manages expert feedback on SAGE interactions.

    Enables expert review workflow:
    1. Get pending interactions to review
    2. Submit feedback with ratings and corrections
    3. Export validated data for training

    Example:
        collector = FeedbackCollector(storage_path="data/feedback/")
        await collector.initialize()

        # Submit expert feedback
        await collector.submit_feedback(
            interaction_id="abc123",
            reviewer_id="expert@example.com",
            accuracy_score=FeedbackRating.GOOD,
            correction="The limit is 20.1 kg, not 22.8 kg.",
        )

        # Get reviewed interactions
        validated = await collector.get_validated_interactions(min_score=4)
    """

    def __init__(
        self,
        storage_path: str | Path = "data/feedback/",
    ):
        """Initialize feedback collector.

        Args:
            storage_path: Directory for storing feedback data.
        """
        self.storage_path = Path(storage_path)
        self._initialized = False

    async def initialize(self) -> None:
        """Initialize the collector and create storage directory."""
        if self._initialized:
            return

        self.storage_path.mkdir(parents=True, exist_ok=True)
        self._initialized = True
        logger.info(f"Feedback collector initialized at {self.storage_path}")

    async def close(self) -> None:
        """Close the collector."""
        self._initialized = False

    async def submit_feedback(
        self,
        interaction_id: str,
        reviewer_id: str,
        accuracy_score: FeedbackRating = FeedbackRating.AVERAGE,
        completeness_score: FeedbackRating = FeedbackRating.AVERAGE,
        conservatism_score: FeedbackRating = FeedbackRating.AVERAGE,
        citation_quality_score: FeedbackRating = FeedbackRating.AVERAGE,
        correction: str | None = None,
        corrected_response: str | None = None,
        additional_citations: list[str] | None = None,
        is_dual_use_false_positive: bool = False,
        is_dual_use_false_negative: bool = False,
        requires_escalation_actually: bool = False,
        contains_factual_error: bool = False,
        reviewer_notes: str = "",
        review_duration_seconds: float = 0.0,
    ) -> ExpertFeedback:
        """Submit expert feedback for an interaction.

        Args:
            interaction_id: ID of the interaction being reviewed.
            reviewer_id: ID of the expert reviewer.
            accuracy_score: Rating for factual accuracy.
            completeness_score: Rating for response completeness.
            conservatism_score: Rating for conservative bias.
            citation_quality_score: Rating for citation quality.
            correction: Text description of what needs correction.
            corrected_response: The corrected response text.
            additional_citations: Additional citations that should be included.
            is_dual_use_false_positive: Response incorrectly flagged as dual-use.
            is_dual_use_false_negative: Dual-use concern was missed.
            requires_escalation_actually: Should have been escalated but wasn't.
            contains_factual_error: Response contains factual errors.
            reviewer_notes: Additional notes from reviewer.
            review_duration_seconds: Time spent on review.

        Returns:
            ExpertFeedback record.
        """
        if not self._initialized:
            await self.initialize()

        feedback_id = str(uuid.uuid4())

        feedback = ExpertFeedback(
            feedback_id=feedback_id,
            interaction_id=interaction_id,
            reviewer_id=reviewer_id,
            accuracy_score=accuracy_score,
            completeness_score=completeness_score,
            conservatism_score=conservatism_score,
            citation_quality_score=citation_quality_score,
            correction=correction,
            corrected_response=corrected_response,
            additional_citations=additional_citations or [],
            is_dual_use_false_positive=is_dual_use_false_positive,
            is_dual_use_false_negative=is_dual_use_false_negative,
            requires_escalation_actually=requires_escalation_actually,
            contains_factual_error=contains_factual_error,
            reviewer_notes=reviewer_notes,
            timestamp=datetime.now(),
            review_duration_seconds=review_duration_seconds,
        )

        # Save to storage
        await self._save_feedback(feedback)

        logger.info(f"Submitted feedback {feedback_id} for interaction {interaction_id}")
        return feedback

    async def _save_feedback(self, feedback: ExpertFeedback) -> None:
        """Save feedback to storage."""
        # Write to daily log file
        date_str = datetime.now().strftime("%Y-%m-%d")
        log_file = self.storage_path / f"feedback_{date_str}.jsonl"

        with open(log_file, "a") as f:
            f.write(json.dumps(feedback.to_dict()) + "\n")

    async def get_feedback(
        self,
        interaction_id: str | None = None,
        reviewer_id: str | None = None,
        min_accuracy_score: int | None = None,
        start_date: datetime | None = None,
        end_date: datetime | None = None,
        limit: int = 1000,
    ) -> list[ExpertFeedback]:
        """Retrieve feedback records.

        Args:
            interaction_id: Filter by interaction ID.
            reviewer_id: Filter by reviewer ID.
            min_accuracy_score: Minimum accuracy score.
            start_date: Start of date range.
            end_date: End of date range.
            limit: Maximum number of records.

        Returns:
            List of ExpertFeedback records.
        """
        feedback_list = []

        # List all feedback files
        log_files = sorted(self.storage_path.glob("feedback_*.jsonl"))

        for log_file in log_files:
            with open(log_file) as f:
                for line in f:
                    if len(feedback_list) >= limit:
                        break

                    data = json.loads(line)
                    feedback = ExpertFeedback.from_dict(data)

                    # Apply filters
                    if interaction_id and feedback.interaction_id != interaction_id:
                        continue
                    if reviewer_id and feedback.reviewer_id != reviewer_id:
                        continue
                    if min_accuracy_score and feedback.accuracy_score.value < min_accuracy_score:
                        continue
                    if start_date and feedback.timestamp < start_date:
                        continue
                    if end_date and feedback.timestamp > end_date:
                        continue

                    feedback_list.append(feedback)

        return feedback_list

    async def get_interaction_feedback(
        self,
        interaction_id: str,
    ) -> list[ExpertFeedback]:
        """Get all feedback for a specific interaction.

        Args:
            interaction_id: Interaction ID.

        Returns:
            List of feedback records for the interaction.
        """
        return await self.get_feedback(interaction_id=interaction_id)

    async def get_reviewer_stats(
        self,
        reviewer_id: str,
    ) -> dict[str, Any]:
        """Get statistics for a specific reviewer.

        Args:
            reviewer_id: Reviewer ID.

        Returns:
            Dictionary of reviewer statistics.
        """
        feedback_list = await self.get_feedback(reviewer_id=reviewer_id)

        if not feedback_list:
            return {
                "reviewer_id": reviewer_id,
                "total_reviews": 0,
                "average_review_time": 0.0,
                "score_distribution": {},
            }

        total_time = sum(f.review_duration_seconds for f in feedback_list)
        avg_time = total_time / len(feedback_list) if feedback_list else 0.0

        # Count score distribution
        score_dist = {1: 0, 2: 0, 3: 0, 4: 0, 5: 0}
        for f in feedback_list:
            score_dist[f.accuracy_score.value] += 1

        return {
            "reviewer_id": reviewer_id,
            "total_reviews": len(feedback_list),
            "average_review_time": avg_time,
            "score_distribution": score_dist,
            "false_positive_count": sum(1 for f in feedback_list if f.is_dual_use_false_positive),
            "false_negative_count": sum(1 for f in feedback_list if f.is_dual_use_false_negative),
            "factual_error_count": sum(1 for f in feedback_list if f.contains_factual_error),
        }

    async def get_validated_interaction_ids(
        self,
        min_accuracy_score: int = 4,
        require_no_factual_errors: bool = True,
    ) -> list[str]:
        """Get interaction IDs that have been validated by experts.

        Args:
            min_accuracy_score: Minimum accuracy score (1-5).
            require_no_factual_errors: Exclude interactions with factual errors.

        Returns:
            List of validated interaction IDs.
        """
        all_feedback = await self.get_feedback(
            min_accuracy_score=min_accuracy_score,
        )

        validated_ids = []
        for feedback in all_feedback:
            if require_no_factual_errors and feedback.contains_factual_error:
                continue
            validated_ids.append(feedback.interaction_id)

        return list(set(validated_ids))

    async def get_corrections(
        self,
        limit: int = 100,
    ) -> list[tuple[str, str, str]]:
        """Get interactions with corrections for preference learning.

        Returns tuples of (interaction_id, original_response, corrected_response).

        Args:
            limit: Maximum number of corrections.

        Returns:
            List of correction tuples.
        """
        all_feedback = await self.get_feedback(limit=limit * 2)

        corrections = []
        for feedback in all_feedback:
            if feedback.corrected_response:
                # Need to look up original response from interaction logs
                corrections.append((
                    feedback.interaction_id,
                    feedback.correction or "",
                    feedback.corrected_response,
                ))

        return corrections[:limit]

    def get_statistics(self) -> dict[str, Any]:
        """Get collection statistics.

        Returns:
            Dictionary of statistics.
        """
        stats = {
            "storage_path": str(self.storage_path),
            "feedback_files": [],
            "total_feedback": 0,
        }

        for log_file in self.storage_path.glob("feedback_*.jsonl"):
            with open(log_file) as f:
                count = sum(1 for _ in f)
            stats["feedback_files"].append({
                "file": log_file.name,
                "count": count,
            })
            stats["total_feedback"] += count

        return stats
