"""
Export Service for Command Center Dashboard

Provides functionality to export dashboard data to Excel and PDF formats.
"""
from datetime import date, datetime
from typing import Dict, Any, List, Optional
import io
import base64

from django.conf import settings
from django.http import HttpResponse
from django.utils import timezone
import json


class ExportService:
    """
    Service for exporting dashboard data to Excel and PDF.
    """
    
    @staticmethod
    def export_to_excel(
        data: Dict[str, Any],
        filename: str = None,
        include_charts: bool = True
    ) -> HttpResponse:
        """
        Export dashboard data to Excel format.
        
        Args:
            data: Dashboard data containing KPIs, charts, and tables
            filename: Optional custom filename (without extension)
            include_charts: Whether to include chart data
        
        Returns:
            HttpResponse with Excel file
        """
        try:
            import openpyxl
            from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
            from openpyxl.utils import get_column_letter
        except ImportError:
            raise ImportError("openpyxl is required for Excel export. Install with: pip install openpyxl")
        
        if not filename:
            filename = f"px360_dashboard_{timezone.now().strftime('%Y%m%d_%H%M%S')}"
        
        # Create workbook
        wb = openpyxl.Workbook()
        ws = wb.active
        ws.title = "Dashboard Summary"
        
        # Styles
        header_font = Font(bold=True, size=12, color="FFFFFF")
        header_fill = PatternFill(start_color="4472C4", end_color="4472C4", fill_type="solid")
        title_font = Font(bold=True, size=14, color="000000")
        border = Border(
            left=Side(style='thin'),
            right=Side(style='thin'),
            top=Side(style='thin'),
            bottom=Side(style='thin')
        )
        
        # Title
        ws['A1'] = "PX360 Dashboard Export"
        ws['A1'].font = title_font
        ws['A2'] = f"Generated: {timezone.now().strftime('%Y-%m-%d %H:%M:%S')}"
        ws['A2'].font = Font(italic=True, color="666666")
        
        row = 4
        
        # KPIs Section
        if 'kpis' in data:
            ws[f'A{row}'] = "Key Performance Indicators"
            ws[f'A{row}'].font = title_font
            row += 2
            
            # Header
            headers = ['KPI', 'Value', 'Description']
            for col, header in enumerate(headers, 1):
                cell = ws.cell(row=row, column=col, value=header)
                cell.font = header_font
                cell.fill = header_fill
                cell.border = border
            row += 1
            
            # KPI Data
            kpi_descriptions = {
                'total_complaints': 'Total number of complaints',
                'open_complaints': 'Currently open complaints',
                'overdue_complaints': 'Complaints past due date',
                'high_severity_complaints': 'High and critical severity complaints',
                'resolved_complaints': 'Complaints marked as resolved or closed',
                'total_actions': 'Total PX actions created',
                'open_actions': 'Currently open PX actions',
                'overdue_actions': 'PX actions past due date',
                'escalated_actions': 'Actions that have been escalated',
                'resolved_actions': 'Actions marked as completed',
                'total_surveys': 'Total completed surveys',
                'negative_surveys': 'Surveys with negative sentiment',
                'avg_survey_score': 'Average survey score (out of 5)',
                'negative_social_mentions': 'Negative social media mentions',
                'low_call_ratings': 'Low call center ratings',
                'total_sentiment_analyses': 'Total sentiment analyses performed',
            }
            
            for kpi_key, kpi_value in data['kpis'].items():
                if kpi_key == 'complaints_trend':
                    continue
                
                ws.cell(row=row, column=1, value=kpi_key.replace('_', ' ').title())
                ws.cell(row=row, column=2, value=kpi_value)
                ws.cell(row=row, column=3, value=kpi_descriptions.get(kpi_key, ''))
                
                for col in range(1, 4):
                    ws.cell(row=row, column=col).border = border
                
                row += 1
            
            row += 2
        
        # Charts Section
        if include_charts and 'charts' in data:
            ws[f'A{row}'] = "Chart Data"
            ws[f'A{row}'].font = title_font
            row += 2
            
            for chart_name, chart_data in data['charts'].items():
                ws[f'A{row}'] = f"Chart: {chart_name}"
                ws[f'A{row}'].font = Font(bold=True, size=11)
                row += 1
                
                # Chart metadata
                if 'type' in chart_data:
                    ws[f'A{row}'] = f"Type: {chart_data['type']}"
                    row += 1
                
                # Labels
                if 'labels' in chart_data:
                    ws[f'A{row}'] = "Labels"
                    ws[f'A{row}'].font = header_font
                    ws[f'A{row}'].fill = header_fill
                    
                    for i, label in enumerate(chart_data['labels'], row + 1):
                        ws.cell(row=i, column=1, value=label)
                        ws.cell(row=i, column=1).border = border
                    
                    row = row + len(chart_data['labels']) + 1
                
                # Series Data
                if 'series' in chart_data:
                    ws[f'A{row}'] = "Series"
                    ws[f'A{row}'].font = header_font
                    ws[f'A{row}'].fill = header_fill
                    
                    series_list = chart_data['series']
                    if isinstance(series_list, list):
                        for series in series_list:
                            row += 1
                            if isinstance(series, dict):
                                series_name = series.get('name', 'Series')
                                series_data = series.get('data', [])
                            else:
                                series_name = 'Series'
                                series_data = []
                            
                            ws.cell(row=row, column=1, value=series_name)
                            ws.cell(row=row, column=1).font = Font(bold=True)
                            
                            if isinstance(series_data, list) and series_data:
                                for i, value in enumerate(series_data, row + 1):
                                    ws.cell(row=i, column=1, value=value)
                                    ws.cell(row=i, column=1).border = border
                                
                                row = row + len(series_data) + 1
                
                row += 2
        
        # Tables Section
        if 'tables' in data:
            # Create new sheet for tables
            ws_tables = wb.create_sheet("Tables")
            ws_tables_row = 1
            
            for table_name, table_data in data['tables'].items():
                ws_tables[f'A{ws_tables_row}'] = table_name
                ws_tables[f'A{ws_tables_row}'].font = title_font
                ws_tables_row += 2
                
                # Headers
                if 'headers' in table_data and 'rows' in table_data:
                    headers = table_data['headers']
                    for col, header in enumerate(headers, 1):
                        cell = ws_tables.cell(row=ws_tables_row, column=col, value=header)
                        cell.font = header_font
                        cell.fill = header_fill
                        cell.border = border
                    ws_tables_row += 1
                    
                    # Data rows
                    for table_row in table_data['rows']:
                        for col, value in enumerate(table_row, 1):
                            # Convert UUID to string if needed
                            if isinstance(value, str):
                                pass  # Already a string
                            elif hasattr(value, '__class__') and value.__class__.__name__ == 'UUID':
                                value = str(value)
                            # Strip timezone from datetime objects
                            elif isinstance(value, datetime):
                                if value.tzinfo is not None:
                                    value = timezone.make_naive(value)
                            cell = ws_tables.cell(row=ws_tables_row, column=col, value=value)
                            cell.border = border
                        ws_tables_row += 1
                    
                    ws_tables_row += 2
        
        # Adjust column widths
        for sheet in wb.worksheets:
            for column in sheet.columns:
                max_length = 0
                column_letter = get_column_letter(column[0].column)
                for cell in column:
                    try:
                        if len(str(cell.value)) > max_length:
                            max_length = len(str(cell.value))
                    except:
                        pass
                adjusted_width = min(max_length + 2, 50)
                sheet.column_dimensions[column_letter].width = adjusted_width
        
        # Prepare response
        response = HttpResponse(
            content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
        )
        response['Content-Disposition'] = f'attachment; filename="{filename}.xlsx"'
        
        # Save workbook to response
        output = io.BytesIO()
        wb.save(output)
        response.write(output.getvalue())
        
        return response
    
    @staticmethod
    def _generate_chart_image(chart_name: str, chart_data: Dict[str, Any]) -> Optional[bytes]:
        """
        Generate a chart image from chart data using Matplotlib.
        
        Args:
            chart_name: Name of the chart
            chart_data: Chart data dictionary
        
        Returns:
            bytes: PNG image data or None
        """
        try:
            import matplotlib
            matplotlib.use('Agg')  # Use non-interactive backend
            import matplotlib.pyplot as plt
            import numpy as np
        except ImportError:
            return None
        
        chart_type = chart_data.get('type', 'bar')
        labels = chart_data.get('labels', [])
        series_list = chart_data.get('series', [])
        
        if not labels or not series_list:
            return None
        
        # Create figure
        fig, ax = plt.subplots(figsize=(10, 6))
        fig.patch.set_facecolor('white')
        
        # Extract data from series
        if isinstance(series_list, list) and len(series_list) > 0:
            series = series_list[0]
            if isinstance(series, dict):
                data = series.get('data', [])
                series_name = series.get('name', 'Value')
            else:
                data = series_list
                series_name = 'Value'
        else:
            data = []
            series_name = 'Value'
        
        # Generate chart based on type
        if chart_type == 'line':
            ax.plot(labels, data, marker='o', linewidth=2, markersize=6, color='#4472C4')
            ax.set_xlabel('Date', fontsize=10)
            ax.set_ylabel(series_name, fontsize=10)
            ax.tick_params(axis='x', rotation=45, labelsize=8)
        
        elif chart_type == 'bar':
            colors = ['#4472C4', '#ED7D31', '#A5A5A5', '#FFC000', '#5B9BD5', '#70AD47']
            bars = ax.bar(labels, data, color=colors[:len(labels)], edgecolor='black', linewidth=0.5)
            ax.set_ylabel(series_name, fontsize=10)
            ax.tick_params(axis='x', rotation=45, labelsize=8)
            
            # Add value labels on bars
            for bar in bars:
                height = bar.get_height()
                ax.text(bar.get_x() + bar.get_width()/2., height,
                       f'{height:.2f}' if isinstance(height, float) else f'{int(height)}',
                       ha='center', va='bottom', fontsize=8)
        
        elif chart_type in ['donut', 'pie']:
            colors = ['#4472C4', '#ED7D31', '#A5A5A5', '#FFC000', '#5B9BD5', '#70AD47']
            wedges, texts, autotexts = ax.pie(data, labels=labels, autopct='%1.1f%%',
                                              colors=colors[:len(labels)],
                                              startangle=90)
            
            if chart_type == 'donut':
                centre_circle = plt.Circle((0, 0), 0.70, fc='white')
                fig.gca().add_artist(centre_circle)
            
            # Improve text readability
            for text in texts:
                text.set_fontsize(9)
            for autotext in autotexts:
                autotext.set_color('white')
                autotext.set_fontsize(9)
                autotext.set_weight('bold')
        
        # Set title
        ax.set_title(chart_name.replace('_', ' ').title(), fontsize=12, fontweight='bold', pad=20)
        
        # Tight layout
        plt.tight_layout()
        
        # Save to bytes
        img_buffer = io.BytesIO()
        plt.savefig(img_buffer, format='png', dpi=150, bbox_inches='tight')
        plt.close()
        
        return img_buffer.getvalue()
    
    @staticmethod
    def export_to_pdf(
        data: Dict[str, Any],
        filename: str = None,
        title: str = "PX360 Dashboard Report"
    ) -> HttpResponse:
        """
        Export dashboard data to PDF format.
        
        Args:
            data: Dashboard data containing KPIs, charts, and tables
            filename: Optional custom filename (without extension)
            title: Custom report title
        
        Returns:
            HttpResponse with PDF file
        """
        try:
            from reportlab.lib import colors
            from reportlab.lib.pagesizes import letter, A4
            from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
            from reportlab.lib.units import inch
            from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer, PageBreak, Image
            from reportlab.lib.enums import TA_CENTER, TA_LEFT
        except ImportError:
            raise ImportError("reportlab is required for PDF export. Install with: pip install reportlab")
        
        if not filename:
            filename = f"px360_dashboard_{timezone.now().strftime('%Y%m%d_%H%M%S')}"
        
        # Create PDF buffer
        buffer = io.BytesIO()
        doc = SimpleDocTemplate(
            buffer,
            pagesize=A4,
            rightMargin=72,
            leftMargin=72,
            topMargin=72,
            bottomMargin=18
        )
        
        # Styles
        styles = getSampleStyleSheet()
        title_style = ParagraphStyle(
            'CustomTitle',
            parent=styles['Heading1'],
            fontSize=24,
            spaceAfter=30,
            textColor=colors.HexColor('#4472C4'),
            alignment=TA_CENTER
        )
        
        heading_style = ParagraphStyle(
            'CustomHeading',
            parent=styles['Heading2'],
            fontSize=16,
            spaceAfter=12,
            spaceBefore=20,
            textColor=colors.HexColor('#333333')
        )
        
        normal_style = ParagraphStyle(
            'CustomNormal',
            parent=styles['Normal'],
            fontSize=10,
            spaceAfter=6
        )
        
        # Build content
        story = []
        
        # Title
        story.append(Paragraph(title, title_style))
        story.append(Paragraph(
            f"Generated: {timezone.now().strftime('%Y-%m-%d %H:%M:%S')}",
            styles['Normal']
        ))
        story.append(Spacer(1, 12))
        
        # KPIs Section
        if 'kpis' in data:
            story.append(Paragraph("Key Performance Indicators", heading_style))
            
            kpi_data = []
            kpi_descriptions = {
                'total_complaints': 'Total number of complaints',
                'open_complaints': 'Currently open complaints',
                'overdue_complaints': 'Complaints past due date',
                'high_severity_complaints': 'High and critical severity complaints',
                'resolved_complaints': 'Complaints marked as resolved or closed',
                'total_actions': 'Total PX actions created',
                'open_actions': 'Currently open PX actions',
                'overdue_actions': 'PX actions past due date',
                'escalated_actions': 'Actions that have been escalated',
                'resolved_actions': 'Actions marked as completed',
                'total_surveys': 'Total completed surveys',
                'negative_surveys': 'Surveys with negative sentiment',
                'avg_survey_score': 'Average survey score (out of 5)',
                'negative_social_mentions': 'Negative social media mentions',
                'low_call_ratings': 'Low call center ratings',
                'total_sentiment_analyses': 'Total sentiment analyses performed',
            }
            
            for kpi_key, kpi_value in data['kpis'].items():
                if kpi_key == 'complaints_trend':
                    continue
                
                kpi_name = kpi_key.replace('_', ' ').title()
                kpi_desc = kpi_descriptions.get(kpi_key, '')
                kpi_data.append([kpi_name, str(kpi_value), kpi_desc])
            
            # Create KPI table
            kpi_table = Table(kpi_data, colWidths=[2.5*inch, 1*inch, 2.5*inch])
            kpi_table.setStyle(TableStyle([
                ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor('#4472C4')),
                ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
                ('ALIGN', (0, 0), (-1, -1), 'LEFT'),
                ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
                ('FONTSIZE', (0, 0), (-1, 0), 10),
                ('BOTTOMPADDING', (0, 0), (-1, 0), 12),
                ('BACKGROUND', (0, 1), (-1, -1), colors.beige),
                ('GRID', (0, 0), (-1, -1), 1, colors.black),
                ('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.white, colors.lightgrey]),
            ]))
            
            story.append(kpi_table)
            story.append(Spacer(1, 12))
        
        # Charts Section (with actual chart images)
        if 'charts' in data:
            story.append(PageBreak())
            story.append(Paragraph("Charts", heading_style))
            story.append(Spacer(1, 12))
            
            for chart_name, chart_data in data['charts'].items():
                # Add chart title
                chart_title = chart_name.replace('_', ' ').title()
                story.append(Paragraph(f"<b>{chart_title}</b>", normal_style))
                story.append(Spacer(1, 6))
                
                # Generate and add chart image
                img_data = ExportService._generate_chart_image(chart_name, chart_data)
                if img_data:
                    try:
                        # Create image from bytes
                        img_buffer = io.BytesIO(img_data)
                        img = Image(img_buffer, width=6*inch, height=3.6*inch)
                        story.append(img)
                        story.append(Spacer(1, 12))
                    except Exception as e:
                        # Fallback to text representation if image fails
                        story.append(Paragraph("Chart Type: " + chart_data.get('type', 'Unknown'), normal_style))
                        if 'labels' in chart_data:
                            labels_str = ", ".join(str(l) for l in chart_data['labels'][:10])
                            if len(chart_data['labels']) > 10:
                                labels_str += f" ... ({len(chart_data['labels'])} total)"
                            story.append(Paragraph(f"Labels: {labels_str}", normal_style))
                        story.append(Spacer(1, 6))
                else:
                    # Fallback to text representation if image generation fails
                    story.append(Paragraph("Chart Type: " + chart_data.get('type', 'Unknown'), normal_style))
                    if 'labels' in chart_data:
                        labels_str = ", ".join(str(l) for l in chart_data['labels'][:10])
                        if len(chart_data['labels']) > 10:
                            labels_str += f" ... ({len(chart_data['labels'])} total)"
                        story.append(Paragraph(f"Labels: {labels_str}", normal_style))
                    story.append(Spacer(1, 12))
        
        # Tables Section
        if 'tables' in data:
            story.append(PageBreak())
            story.append(Paragraph("Detailed Tables", heading_style))
            
            for table_name, table_data in data['tables'].items():
                story.append(Paragraph(f"<b>{table_name}</b>", normal_style))
                story.append(Spacer(1, 6))
                
                if 'headers' in table_data and 'rows' in table_data:
                    table_rows = [table_data['headers']] + table_data['rows']
                    table = Table(table_rows)
                    table.setStyle(TableStyle([
                        ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor('#4472C4')),
                        ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
                        ('ALIGN', (0, 0), (-1, -1), 'LEFT'),
                        ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
                        ('FONTSIZE', (0, 0), (-1, 0), 9),
                        ('GRID', (0, 0), (-1, -1), 1, colors.black),
                        ('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.white, colors.lightgrey]),
                    ]))
                    story.append(table)
                    story.append(Spacer(1, 12))
        
        # Build PDF
        doc.build(story)
        
        # Prepare response
        response = HttpResponse(content_type='application/pdf')
        response['Content-Disposition'] = f'attachment; filename="{filename}.pdf"'
        response.write(buffer.getvalue())
        
        return response
    
    @staticmethod
    def prepare_dashboard_data(
        user,
        kpis: Dict[str, Any],
        charts: Dict[str, Any],
        tables: Dict[str, Any]
    ) -> Dict[str, Any]:
        """
        Prepare dashboard data for export by consolidating all components.
        
        Args:
            user: Current user
            kpis: KPI data
            charts: Chart data dictionary
            tables: Tables data dictionary
        
        Returns:
            dict: Consolidated export data
        """
        export_data = {
            'metadata': {
                'generated_at': timezone.now().isoformat(),
                'user': user.get_full_name() or user.username,
                'user_role': user.get_role_display() if hasattr(user, 'get_role_display') else 'Unknown',
            },
            'kpis': kpis,
            'charts': charts,
            'tables': tables
        }
        
        return export_data
