import csv
import io
import json
from datetime import datetime

from flask import Response
from sqlalchemy import or_

from extensions.ext_database import db
from models.enums import FeedbackRating
from models.model import Account, App, Conversation, Message, MessageFeedback


class FeedbackService:
    @staticmethod
    def export_feedbacks(
        app_id: str,
        from_source: str | None = None,
        rating: str | None = None,
        has_comment: bool | None = None,
        start_date: str | None = None,
        end_date: str | None = None,
        format_type: str = "csv",
    ):
        """
        Export feedback data with message details for analysis

        Args:
            app_id: Application ID
            from_source: Filter by feedback source ('user' or 'admin')
            rating: Filter by rating ('like' or 'dislike')
            has_comment: Only include feedback with comments
            start_date: Start date filter (YYYY-MM-DD)
            end_date: End date filter (YYYY-MM-DD)
            format_type: Export format ('csv' or 'json')
        """

        # Validate format early to avoid hitting DB when unnecessary
        fmt = (format_type or "csv").lower()
        if fmt not in {"csv", "json"}:
            raise ValueError(f"Unsupported format: {format_type}")

        # Build base query
        query = (
            db.session.query(MessageFeedback, Message, Conversation, App, Account)
            .join(Message, MessageFeedback.message_id == Message.id)
            .join(Conversation, MessageFeedback.conversation_id == Conversation.id)
            .join(App, MessageFeedback.app_id == App.id)
            .outerjoin(Account, MessageFeedback.from_account_id == Account.id)
            .where(MessageFeedback.app_id == app_id)
        )

        # Apply filters
        if from_source:
            query = query.filter(MessageFeedback.from_source == from_source)

        if rating:
            query = query.filter(MessageFeedback.rating == rating)

        if has_comment is not None:
            if has_comment:
                query = query.filter(MessageFeedback.content.isnot(None), MessageFeedback.content != "")
            else:
                query = query.filter(or_(MessageFeedback.content.is_(None), MessageFeedback.content == ""))

        if start_date:
            try:
                start_dt = datetime.strptime(start_date, "%Y-%m-%d")
                query = query.filter(MessageFeedback.created_at >= start_dt)
            except ValueError:
                raise ValueError(f"Invalid start_date format: {start_date}. Use YYYY-MM-DD")

        if end_date:
            try:
                end_dt = datetime.strptime(end_date, "%Y-%m-%d")
                query = query.filter(MessageFeedback.created_at <= end_dt)
            except ValueError:
                raise ValueError(f"Invalid end_date format: {end_date}. Use YYYY-MM-DD")

        # Order by creation date (newest first)
        query = query.order_by(MessageFeedback.created_at.desc())

        # Execute query
        results = query.all()

        # Prepare data for export
        export_data = []
        for feedback, message, conversation, app, account in results:
            # Get the user query from the message
            user_query = message.query or (message.inputs.get("query", "") if message.inputs else "")

            # Format the feedback data
            feedback_record = {
                "feedback_id": str(feedback.id),
                "app_name": app.name,
                "app_id": str(app.id),
                "conversation_id": str(conversation.id),
                "conversation_name": conversation.name or "",
                "message_id": str(message.id),
                "user_query": user_query,
                "ai_response": message.answer[:500] + "..."
                if len(message.answer) > 500
                else message.answer,  # Truncate long responses
                "feedback_rating": "👍" if feedback.rating == FeedbackRating.LIKE else "👎",
                "feedback_rating_raw": feedback.rating,
                "feedback_comment": feedback.content or "",
                "feedback_source": feedback.from_source,
                "feedback_date": feedback.created_at.strftime("%Y-%m-%d %H:%M:%S"),
                "message_date": message.created_at.strftime("%Y-%m-%d %H:%M:%S"),
                "from_account_name": account.name if account else "",
                "from_end_user_id": str(feedback.from_end_user_id) if feedback.from_end_user_id else "",
                "has_comment": "Yes" if feedback.content and feedback.content.strip() else "No",
            }
            export_data.append(feedback_record)

        # Export based on format
        if fmt == "csv":
            return FeedbackService._export_csv(export_data, app_id)
        else:  # fmt == "json"
            return FeedbackService._export_json(export_data, app_id)

    @staticmethod
    def _export_csv(data, app_id):
        """Export data as CSV"""
        if not data:
            pass  # allow empty CSV with headers only

        # Create CSV in memory
        output = io.StringIO()

        # Define headers
        headers = [
            "feedback_id",
            "app_name",
            "app_id",
            "conversation_id",
            "conversation_name",
            "message_id",
            "user_query",
            "ai_response",
            "feedback_rating",
            "feedback_rating_raw",
            "feedback_comment",
            "feedback_source",
            "feedback_date",
            "message_date",
            "from_account_name",
            "from_end_user_id",
            "has_comment",
        ]

        writer = csv.DictWriter(output, fieldnames=headers)
        writer.writeheader()
        writer.writerows(data)

        # Create response without requiring app context
        response = Response(output.getvalue(), mimetype="text/csv; charset=utf-8-sig")
        response.headers["Content-Disposition"] = (
            f"attachment; filename=dify_feedback_export_{app_id}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv"
        )

        return response

    @staticmethod
    def _export_json(data, app_id):
        """Export data as JSON"""
        response_data = {
            "export_info": {
                "app_id": app_id,
                "export_date": datetime.now().isoformat(),
                "total_records": len(data),
                "data_source": "dify_feedback_export",
            },
            "feedback_data": data,
        }

        # Create response without requiring app context
        response = Response(
            json.dumps(response_data, ensure_ascii=False, indent=2),
            mimetype="application/json; charset=utf-8",
        )
        response.headers["Content-Disposition"] = (
            f"attachment; filename=dify_feedback_export_{app_id}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
        )

        return response
