"""
Staff Analytics Module
======================
Advanced analytics for individual staff performance and ratings.
"""
import io
from datetime import datetime, timedelta
from collections import defaultdict
from typing import List, Dict, Any, Optional

import disnake
import matplotlib.pyplot as plt
import plotly.graph_objects as go
import plotly.express as px
from plotly.subplots import make_subplots
import numpy as np
from PIL import Image, ImageDraw, ImageFont

from .data.storage import get_storage
from .data.models import TicketData, TicketStatus


class StaffAnalytics:
    """Handles staff-specific analytics and visualizations"""

    def __init__(self, bot):
        self.bot = bot
        self.storage = get_storage()

    async def get_staff_list(self, guild_id: int) -> List[Dict[str, Any]]:
        """Get list of staff members with their stats"""
        config = self.storage.load_config(guild_id)
        # Invalidate cache to ensure we get the latest ticket data with new ratings
        self.storage.invalidate_cache(guild_id)
        tickets = self.storage.get_guild_tickets(guild_id)

        staff_stats = defaultdict(lambda: {
            'user_id': 0,
            'name': 'Unknown',
            'tickets_claimed': 0,
            'tickets_closed': 0,
            'average_rating': 0,
            'total_ratings': 0,
            'response_times': [],
            'ratings': [],
            'categories': defaultdict(int)
        })

        for ticket in tickets:
            if ticket.claimed_by:
                staff_id = ticket.claimed_by
                staff_stats[staff_id]['user_id'] = staff_id
                staff_stats[staff_id]['tickets_claimed'] += 1

                if ticket.status == TicketStatus.CLOSED:
                    staff_stats[staff_id]['tickets_closed'] += 1

                if ticket.rating:
                    staff_stats[staff_id]['ratings'].append(ticket.rating)
                    staff_stats[staff_id]['total_ratings'] += 1

                # Calculate response time
                if ticket.created_at:
                    claimed_time = None
                    for msg in ticket.messages:
                        if msg.author_id == staff_id and msg.is_staff:
                            claimed_time = msg.timestamp
                            break

                    if claimed_time:
                        response_time = (claimed_time - ticket.created_at).total_seconds() / 60
                        staff_stats[staff_id]['response_times'].append(response_time)

                # Category stats
                staff_stats[staff_id]['categories'][ticket.category_id] += 1

        # Calculate averages and get names
        result = []
        for staff_id, stats in staff_stats.items():
            if stats['ratings']:
                stats['average_rating'] = sum(stats['ratings']) / len(stats['ratings'])

            if stats['response_times']:
                stats['average_response_time'] = sum(stats['response_times']) / len(stats['response_times'])
            else:
                stats['average_response_time'] = 0

            # Get user name
            try:
                user = self.bot.get_user(staff_id)
                if user:
                    stats['name'] = str(user)
                else:
                    # Try to get from guild
                    guild = self.bot.get_guild(guild_id)
                    if guild:
                        member = guild.get_member(staff_id)
                        if member:
                            stats['name'] = str(member)
            except:
                pass

            result.append(stats)

        # Sort by tickets claimed
        result.sort(key=lambda x: x['tickets_claimed'], reverse=True)
        return result

    def create_staff_rating_chart(self, staff_list: List[Dict[str, Any]]) -> io.BytesIO:
        """Create a comprehensive staff rating chart using matplotlib"""
        # Filter staff with ratings
        rated_staff = [s for s in staff_list if s['total_ratings'] > 0]

        if not rated_staff:
            # Return empty chart
            fig, ax = plt.subplots(figsize=(10, 6), facecolor='#2F3136')
            ax.set_facecolor('#2F3136')
            ax.text(0.5, 0.5, 'Aucune évaluation disponible', ha='center', va='center',
                    transform=ax.transAxes, color='white')
            ax.set_title('Évaluations des Membres du Staff', color='white')
            buf = io.BytesIO()
            plt.savefig(buf, format='png', bbox_inches='tight', facecolor='#2F3136')
            buf.seek(0)
            plt.close()
            return buf

        # Sort by average rating
        rated_staff.sort(key=lambda x: x['average_rating'], reverse=True)
        
        # Prepare data - limit to top 10 for better readability
        top_staff = rated_staff[:10]
        names = [s['name'][:15] + '...' if len(s['name']) > 15 else s['name'] for s in top_staff]
        ratings = [s['average_rating'] for s in top_staff]
        counts = [s['total_ratings'] for s in top_staff]

        # Create figure
        fig, ax = plt.subplots(figsize=(12, 8), facecolor='#2F3136')
        fig.patch.set_facecolor('#2F3136')
        ax.set_facecolor('#2F3136')
        
        # Create bars with color coding
        colors = ['#57F287' if r >= 4.5 else '#FEE75C' if r >= 3.5 else '#FAA61A' if r >= 2.5 else '#ED4245' for r in ratings]
        bars = ax.bar(range(len(ratings)), ratings, color=colors)
        
        # Customize
        ax.set_xticks(range(len(names)))
        ax.set_xticklabels(names, rotation=45, ha='right', color='white')
        ax.set_ylabel('Note moyenne', color='white')
        ax.set_title('Évaluations des Membres du Staff', color='white', fontsize=16)
        ax.tick_params(colors='white')
        ax.grid(True, alpha=0.3, color='gray')
        
        # Add value labels on bars (avoiding Unicode stars to prevent warnings)
        for i, (bar, rating, count) in enumerate(zip(bars, ratings, counts)):
            ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.05,
                   f'{rating:.1f}\n({count})', ha='center', va='bottom', color='white', fontsize=8)
        
        # Set y-axis limits
        ax.set_ylim(0, 5.2)
        
        # Add legend
        legend_elements = [
            plt.Rectangle((0,0),1,1, color='#57F287', label='Excellent (4.5+)'),
            plt.Rectangle((0,0),1,1, color='#FEE75C', label='Bon (3.5-4.4)'),
            plt.Rectangle((0,0),1,1, color='#FAA61A', label='Moyen (2.5-3.4)'),
            plt.Rectangle((0,0),1,1, color='#ED4245', label='À améliorer (<2.5)')
        ]
        ax.legend(handles=legend_elements, loc='upper right', facecolor='#2F3136', edgecolor='white', labelcolor='white')
        
        # Adjust layout
        plt.tight_layout()
        
        # Save to buffer
        buf = io.BytesIO()
        plt.savefig(buf, format='png', bbox_inches='tight', facecolor='#2F3136')
        buf.seek(0)
        plt.close()
        return buf

    def create_individual_staff_chart(self, staff_data: Dict[str, Any]) -> io.BytesIO:
        """Create detailed chart for individual staff member"""
        width, height = 800, 500
        img = Image.new('RGB', (width, height), '#2F3136')
        draw = ImageDraw.Draw(img)

        try:
            font_title = ImageFont.truetype("arial.ttf", 18)
            font_label = ImageFont.truetype("arial.ttf", 14)
            font_value = ImageFont.truetype("arial.ttf", 12)
        except:
            font_title = ImageFont.load_default()
            font_label = ImageFont.load_default()
            font_value = ImageFont.load_default()

        # Title
        title = f"Statistiques de {staff_data['name']}"
        draw.text((width//2, 30), title, fill='white', font=font_title, anchor='mm')

        # Basic stats
        stats_y = 80
        draw.text((50, stats_y), f"Tickets réclamés: {staff_data['tickets_claimed']}", fill='white', font=font_label)
        draw.text((50, stats_y + 25), f"Tickets fermés: {staff_data['tickets_closed']}", fill='white', font=font_label)

        if staff_data['total_ratings'] > 0:
            avg_rating = staff_data['average_rating']
            color = '#57F287' if avg_rating >= 4 else '#FEE75C' if avg_rating >= 3 else '#ED4245'
            draw.text((50, stats_y + 50), f"Note moyenne: {avg_rating:.1f}⭐ ({staff_data['total_ratings']} évaluations)", fill=color, font=font_label)

        if staff_data['response_times']:
            avg_response = staff_data['average_response_time']
            draw.text((50, stats_y + 75), f"Temps de réponse moyen: {avg_response:.1f} min", fill='white', font=font_label)

        # Rating distribution if available
        if staff_data['ratings']:
            chart_left = 400
            chart_top = 150
            chart_width = 350
            chart_height = 200

            # Count ratings
            rating_counts = defaultdict(int)
            for rating in staff_data['ratings']:
                rating_counts[rating] += 1

            max_count = max(rating_counts.values()) if rating_counts else 1

            # Draw mini bars
            bar_width = chart_width // 5
            for i in range(1, 6):
                count = rating_counts[i]
                if max_count > 0:
                    bar_height = int((count / max_count) * chart_height)
                else:
                    bar_height = 0

                x1 = chart_left + (i-1) * bar_width + 10
                y1 = chart_top + chart_height - bar_height
                x2 = x1 + bar_width - 20
                y2 = chart_top + chart_height

                draw.rectangle([x1, y1, x2, y2], fill='#5865F2', outline='#FFFFFF', width=1)
                draw.text((x1 + (x2-x1)//2, y1 - 15), str(count), fill='white', font=font_value, anchor='mm')

            # Labels
            for i in range(1, 6):
                x = chart_left + (i-1) * bar_width + bar_width//2
                draw.text((x, chart_top + chart_height + 20), f"{i}⭐", fill='white', font=font_label, anchor='mm')

            draw.text((chart_left + chart_width//2, chart_top - 20), "Distribution des Notes", fill='white', font=font_label, anchor='mm')

        buf = io.BytesIO()
        img.save(buf, format='PNG')
        buf.seek(0)
        return buf


class StaffStatsView(disnake.ui.View):
    """View for selecting individual staff member stats"""

    def __init__(self, bot, staff_list: List[Dict[str, Any]], analytics: StaffAnalytics):
        super().__init__(timeout=300)
        self.bot = bot
        self.staff_list = staff_list
        self.analytics = analytics
        self._build_select()

    def _build_select(self):
        """Build staff selection dropdown"""
        options = []
        for staff in self.staff_list[:25]:  # Discord limit
            label = f"{staff['name'][:20]} ({staff['tickets_claimed']} tickets)"
            description = f"Note: {staff['average_rating']:.1f}⭐" if staff['total_ratings'] > 0 else "Aucune évaluation"

            options.append(
                disnake.SelectOption(
                    label=label,
                    value=str(staff['user_id']),
                    description=description[:50]
                )
            )

        if options:
            select = disnake.ui.Select(
                placeholder="Sélectionnez un membre du staff...",
                options=options
            )
            select.callback = self._staff_selected
            self.add_item(select)

    async def _staff_selected(self, interaction: disnake.Interaction):
        """Handle staff selection"""
        staff_id = int(interaction.data["values"][0])
        staff_data = next((s for s in self.staff_list if s['user_id'] == staff_id), None)

        if not staff_data:
            await interaction.response.send_message("Membre du staff introuvable.", ephemeral=True)
            return

        await interaction.response.defer(ephemeral=True)

        # Create individual chart
        chart = self.analytics.create_individual_staff_chart(staff_data)

        embed = disnake.Embed(
            title=f"📊 Statistiques de {staff_data['name']}",
            description="Performances détaillées du membre du staff",
            color=disnake.Color.blue(),
            timestamp=datetime.now()
        )

        embed.add_field(
            name="📈 Activité",
            value=f"**Tickets réclamés:** {staff_data['tickets_claimed']}\n"
                  f"**Tickets fermés:** {staff_data['tickets_closed']}\n"
                  f"**Taux de résolution:** {staff_data['tickets_closed']/staff_data['tickets_claimed']*100:.1f}%" if staff_data['tickets_claimed'] > 0 else "N/A",
            inline=True
        )

        if staff_data['total_ratings'] > 0:
            embed.add_field(
                name="⭐ Évaluations",
                value=f"**Note moyenne:** {staff_data['average_rating']:.1f}/5\n"
                      f"**Total d'évaluations:** {staff_data['total_ratings']}",
                inline=True
            )

        if staff_data['response_times']:
            embed.add_field(
                name="⏱️ Performance",
                value=f"**Temps de réponse moyen:** {staff_data['average_response_time']:.1f} min",
                inline=True
            )

        await interaction.followup.send(embed=embed, file=disnake.File(chart, 'staff_stats.png'), ephemeral=True)
