"""
Staff Analytics Command for Ticket System
==========================================
A modular command for staff to view ticket analytics with beautiful graphics.
Shows response times, ratings, comments, and other key metrics.
"""

import os
import sys
import io
from datetime import datetime, timedelta
from collections import defaultdict
from typing import List, Dict, Any, Optional
from PIL import Image, ImageDraw, ImageFont

# Add parent directory to path for imports
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))

import disnake
from disnake.ext import commands

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


class TicketAnalytics(commands.Cog):
    """Analytics command for staff to view ticket statistics and ratings"""

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

    def _is_staff(self, interaction: disnake.Interaction) -> bool:
        """Check if user is staff"""
        guild = interaction.guild
        user = interaction.user
        config = self.storage.load_config(guild.id)

        for role_id in config.staff_roles:
            role = guild.get_role(int(role_id))
            if role and role in user.roles:
                return True

        return user.guild_permissions.administrator

    def _calculate_comprehensive_stats(self, guild_id: int) -> Dict[str, Any]:
        """Calculate comprehensive statistics for analytics"""
        tickets = self.storage.get_guild_tickets(guild_id)

        stats = {
            'total_tickets': len(tickets),
            'open_tickets': 0,
            'closed_tickets': 0,
            'claimed_tickets': 0,
            'ratings': [],
            'response_times': [],
            'feedbacks': [],
            'tickets_by_day': defaultdict(int),
            'tickets_by_category': defaultdict(int),
            'tickets_by_priority': defaultdict(int),
            'average_rating': 0,
            'average_response_time': 0,
            'total_response_time': 0,
            'response_time_count': 0
        }

        for ticket in tickets:
            # Basic counts
            if ticket.status == TicketStatus.OPEN:
                stats['open_tickets'] += 1
            elif ticket.status == TicketStatus.CLAIMED:
                stats['claimed_tickets'] += 1
                stats['open_tickets'] += 1
            elif ticket.status == TicketStatus.CLOSED:
                stats['closed_tickets'] += 1

            # Ratings and feedback
            if ticket.rating:
                stats['ratings'].append(ticket.rating)
            if ticket.feedback:
                stats['feedbacks'].append({
                    'rating': ticket.rating,
                    'feedback': ticket.feedback,
                    'user_id': ticket.user_id,
                    'ticket_id': ticket.channel_id
                })

            # Response time calculation
            if ticket.claimed_by and ticket.created_at:
                claimed_time = None
                for msg in ticket.messages:
                    if msg.author_id == ticket.claimed_by and msg.is_staff:
                        claimed_time = msg.timestamp
                        break

                if claimed_time:
                    response_time = (claimed_time - ticket.created_at).total_seconds() / 60  # minutes
                    stats['response_times'].append(response_time)
                    stats['total_response_time'] += response_time
                    stats['response_time_count'] += 1

            # Daily stats
            day_key = ticket.created_at.strftime('%Y-%m-%d')
            stats['tickets_by_day'][day_key] += 1

            # Category stats
            stats['tickets_by_category'][ticket.category_id] += 1

            # Priority stats
            stats['tickets_by_priority'][ticket.priority.value] += 1

        # Calculate averages
        if stats['ratings']:
            stats['average_rating'] = sum(stats['ratings']) / len(stats['ratings'])

        if stats['response_time_count'] > 0:
            stats['average_response_time'] = stats['total_response_time'] / stats['response_time_count']

        return stats

    def _create_rating_chart(self, ratings: List[int]) -> io.BytesIO:
        """Create a beautiful rating distribution chart using Pillow"""
        # Image dimensions
        width, height = 800, 500
        img = Image.new('RGB', (width, height), '#2F3136')
        draw = ImageDraw.Draw(img)

        # Try to load a font, fallback to default if not available
        try:
            font_title = ImageFont.truetype("arial.ttf", 24)
            font_label = ImageFont.truetype("arial.ttf", 16)
            font_value = ImageFont.truetype("arial.ttf", 14)
        except:
            font_title = ImageFont.load_default()
            font_label = ImageFont.load_default()
            font_value = ImageFont.load_default()

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

        values = [rating_counts[i] for i in range(1, 6)]
        max_value = max(values) if values else 1

        # Chart dimensions
        chart_left = 100
        chart_right = width - 50
        chart_top = 80
        chart_bottom = height - 100
        chart_width = chart_right - chart_left
        chart_height = chart_bottom - chart_top

        # Draw title
        title = "Distribution des Notes Clients"
        draw.text((width//2, 30), title, fill='white', font=font_title, anchor='mm')

        # Draw bars
        bar_width = chart_width // 6
        for i, value in enumerate(values):
            if max_value > 0:
                bar_height = int((value / max_value) * chart_height)
            else:
                bar_height = 0

            x1 = chart_left + i * bar_width + 10
            y1 = chart_bottom - bar_height
            x2 = x1 + bar_width - 20
            y2 = chart_bottom

            # Draw bar
            draw.rectangle([x1, y1, x2, y2], fill='#5865F2', outline='#FFFFFF', width=2)

            # Draw value on top of bar
            if value > 0:
                draw.text((x1 + (x2-x1)//2, y1 - 10), str(value), fill='white', font=font_value, anchor='mm')

        # Draw x-axis labels
        labels = ['1 ⭐', '2 ⭐', '3 ⭐', '4 ⭐', '5 ⭐']
        for i, label in enumerate(labels):
            x = chart_left + i * bar_width + bar_width//2
            draw.text((x, chart_bottom + 20), label, fill='white', font=font_label, anchor='mm')

        # Draw y-axis label
        draw.text((30, height//2), 'Nombre de Tickets', fill='white', font=font_label, anchor='mm')

        # Draw grid lines
        for i in range(0, 6):
            y = chart_bottom - (i * chart_height // 5)
            draw.line([chart_left, y, chart_right, y], fill='#FFFFFF', width=1)

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

    def _create_response_time_chart(self, response_times: List[float]) -> io.BytesIO:
        """Create response time distribution chart using Pillow"""
        # Image dimensions
        width, height = 800, 500
        img = Image.new('RGB', (width, height), '#2F3136')
        draw = ImageDraw.Draw(img)

        # Try to load a font, fallback to default if not available
        try:
            font_title = ImageFont.truetype("arial.ttf", 20)
            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()

        # Create histogram bins
        bins = [0, 5, 15, 30, 60, 120, 240, float('inf')]
        labels = ['< 5min', '5-15min', '15-30min', '30-60min', '1-2h', '2-4h', '> 4h']

        hist_values = []
        for i in range(len(bins) - 1):
            count = sum(1 for rt in response_times if bins[i] <= rt < bins[i+1])
            hist_values.append(count)

        # Last bin for > 4h
        hist_values.append(sum(1 for rt in response_times if rt >= bins[-2]))

        max_value = max(hist_values) if hist_values else 1

        # Chart dimensions
        chart_left = 120
        chart_right = width - 50
        chart_top = 80
        chart_bottom = height - 120
        chart_width = chart_right - chart_left
        chart_height = chart_bottom - chart_top

        # Draw title
        title = "Temps de Réponse Moyen"
        draw.text((width//2, 30), title, fill='white', font=font_title, anchor='mm')

        # Draw bars
        bar_width = chart_width // len(labels)
        for i, value in enumerate(hist_values):
            if max_value > 0:
                bar_height = int((value / max_value) * chart_height)
            else:
                bar_height = 0

            x1 = chart_left + i * bar_width + 5
            y1 = chart_bottom - bar_height
            x2 = x1 + bar_width - 10
            y2 = chart_bottom

            # Draw bar
            draw.rectangle([x1, y1, x2, y2], fill='#57F287', outline='#FFFFFF', width=2)

            # Draw value on top of bar
            if value > 0:
                draw.text((x1 + (x2-x1)//2, y1 - 10), str(value), fill='white', font=font_value, anchor='mm')

        # Draw x-axis labels
        for i, label in enumerate(labels):
            x = chart_left + i * bar_width + bar_width//2
            # Rotate text for better fit
            draw.text((x, chart_bottom + 30), label, fill='white', font=font_label, anchor='mm')

        # Draw y-axis label
        draw.text((40, height//2), 'Nombre de Tickets', fill='white', font=font_label, anchor='mm')

        # Draw grid lines
        for i in range(0, 6):
            y = chart_bottom - (i * chart_height // 5)
            draw.line([chart_left, y, chart_right, y], fill='#FFFFFF', width=1)

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

    def _create_trend_chart(self, tickets_by_day: Dict[str, int]) -> io.BytesIO:
        """Create ticket trend chart over time using Pillow"""
        # Image dimensions
        width, height = 900, 500
        img = Image.new('RGB', (width, height), '#2F3136')
        draw = ImageDraw.Draw(img)

        # Try to load a font, fallback to default if not available
        try:
            font_title = ImageFont.truetype("arial.ttf", 20)
            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()

        # Sort dates
        sorted_dates = sorted(tickets_by_day.keys())
        values = [tickets_by_day[date] for date in sorted_dates]

        if not values:
            # Return empty chart if no data
            buf = io.BytesIO()
            img.save(buf, format='PNG')
            buf.seek(0)
            return buf

        max_value = max(values) if values else 1

        # Chart dimensions
        chart_left = 100
        chart_right = width - 50
        chart_top = 80
        chart_bottom = height - 100
        chart_width = chart_right - chart_left
        chart_height = chart_bottom - chart_top

        # Draw title
        title = "Évolution des Tickets sur les Derniers Jours"
        draw.text((width//2, 30), title, fill='white', font=font_title, anchor='mm')

        # Calculate points for line chart
        points = []
        for i, (date_str, value) in enumerate(zip(sorted_dates, values)):
            x = chart_left + (i * chart_width) // max(1, len(values) - 1)
            if max_value > 0:
                y = chart_bottom - int((value / max_value) * chart_height)
            else:
                y = chart_bottom
            points.append((x, y))

        # Draw line
        if len(points) > 1:
            draw.line(points, fill='#FEE75C', width=3, joint='curve')

        # Draw points and values
        for i, ((x, y), value, date_str) in enumerate(zip(points, values, sorted_dates)):
            # Draw point
            draw.ellipse([x-4, y-4, x+4, y+4], fill='#FEE75C', outline='#FFFFFF', width=2)

            # Draw value above point
            draw.text((x, y-15), str(value), fill='white', font=font_value, anchor='mm')

            # Draw date label (every few points to avoid crowding)
            if i % max(1, len(points)//7) == 0 or i == len(points)-1:
                date_obj = datetime.strptime(date_str, '%Y-%m-%d')
                date_label = date_obj.strftime('%d/%m')
                draw.text((x, chart_bottom + 20), date_label, fill='white', font=font_label, anchor='mm')

        # Draw y-axis label
        draw.text((40, height//2), 'Nombre de Tickets', fill='white', font=font_label, anchor='mm')

        # Draw grid lines
        for i in range(0, 6):
            y = chart_bottom - (i * chart_height // 5)
            draw.line([chart_left, y, chart_right, y], fill='#FFFFFF', width=1)

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

    @commands.slash_command(name="ticket_analytics", description="Affiche les statistiques détaillées des tickets (Staff uniquement)")
    async def ticket_analytics(self, interaction: disnake.Interaction, period: str = commands.Param(default="30d", choices=["7d", "30d", "90d", "all"], description="Période d'analyse")):
        """Display comprehensive ticket analytics for staff"""
        await interaction.response.defer(ephemeral=True)

        # Check permissions
        if not self._is_staff(interaction):
            await interaction.followup.send("❌ Cette commande est réservée au staff.", ephemeral=True)
            return

        # Parse period
        days = 30
        if period == "7d":
            days = 7
        elif period == "30d":
            days = 30
        elif period == "90d":
            days = 90
        elif period == "all":
            days = None

        # Calculate stats
        stats = self._calculate_comprehensive_stats(interaction.guild_id)

        # Filter by period if specified
        if days:
            cutoff_date = datetime.now() - timedelta(days=days)
            tickets = self.storage.get_guild_tickets(interaction.guild_id)
            filtered_tickets = [t for t in tickets if t.created_at >= cutoff_date]

            # Recalculate stats for filtered period
            stats = self._calculate_comprehensive_stats(interaction.guild_id)
            # Filter the data
            stats['tickets_by_day'] = {k: v for k, v in stats['tickets_by_day'].items()
                                     if datetime.strptime(k, '%Y-%m-%d') >= cutoff_date}
            stats['ratings'] = [r for r in stats['ratings'] if any(
                t.created_at >= cutoff_date and t.rating == r for t in filtered_tickets)]
            stats['response_times'] = [rt for rt in stats['response_times'] if any(
                t.created_at >= cutoff_date for t in filtered_tickets if hasattr(t, 'response_time') and t.response_time == rt)]

        # Create main embed
        embed = disnake.Embed(
            title="📊 Analytics des Tickets",
            description=f"Statistiques pour la période: **{period}**",
            color=disnake.Color.blue(),
            timestamp=datetime.now()
        )

        # Basic stats
        embed.add_field(
            name="📈 Statistiques Générales",
            value=f"**Total:** {stats['total_tickets']}\n"
                  f"**Ouverts:** {stats['open_tickets']}\n"
                  f"**Fermés:** {stats['closed_tickets']}\n"
                  f"**Réclamés:** {stats['claimed_tickets']}",
            inline=True
        )

        # Performance metrics
        avg_rating = f"{stats['average_rating']:.1f}⭐" if stats['ratings'] else "N/A"
        avg_response = f"{stats['average_response_time']:.1f}min" if stats['response_times'] else "N/A"

        embed.add_field(
            name="⚡ Performance",
            value=f"**Note moyenne:** {avg_rating}\n"
                  f"**Temps réponse moyen:** {avg_response}\n"
                  f"**Feedbacks:** {len(stats['feedbacks'])}",
            inline=True
        )

        # Send main embed
        await interaction.followup.send(embed=embed, ephemeral=True)

        # Create and send charts
        try:
            # Rating chart
            if stats['ratings']:
                rating_chart = self._create_rating_chart(stats['ratings'])
                await interaction.followup.send(
                    "📊 **Distribution des Notes Clients**",
                    file=disnake.File(rating_chart, 'rating_distribution.png'),
                    ephemeral=True
                )

            # Response time chart
            if stats['response_times']:
                response_chart = self._create_response_time_chart(stats['response_times'])
                await interaction.followup.send(
                    "⏱️ **Temps de Réponse**",
                    file=disnake.File(response_chart, 'response_times.png'),
                    ephemeral=True
                )

            # Trend chart
            if stats['tickets_by_day']:
                trend_chart = self._create_trend_chart(stats['tickets_by_day'])
                await interaction.followup.send(
                    "📈 **Évolution des Tickets**",
                    file=disnake.File(trend_chart, 'ticket_trends.png'),
                    ephemeral=True
                )

        except Exception as e:
            await interaction.followup.send(f"❌ Erreur lors de la génération des graphiques: {e}", ephemeral=True)

        # Send feedback summary if available
        if stats['feedbacks']:
            feedback_embed = disnake.Embed(
                title="💬 Commentaires Clients",
                description=f"Derniers commentaires ({min(5, len(stats['feedbacks']))} affichés)",
                color=disnake.Color.green()
            )

            # Sort by rating (lowest first to show areas for improvement)
            sorted_feedbacks = sorted(stats['feedbacks'], key=lambda x: x['rating'] or 0)

            for i, fb in enumerate(sorted_feedbacks[:5]):
                rating_text = f"{fb['rating']}⭐" if fb['rating'] else "N/A"
                feedback_text = fb['feedback'][:200] + "..." if len(fb['feedback']) > 200 else fb['feedback']
                feedback_embed.add_field(
                    name=f"Ticket #{fb['ticket_id']} - {rating_text}",
                    value=feedback_text,
                    inline=False
                )

            await interaction.followup.send(embed=feedback_embed, ephemeral=True)


def setup(bot):
    """Setup function for the analytics cog"""
    bot.add_cog(TicketAnalytics(bot))
