"""
Export Service for RateRight Time Tracking System
Provides CSV and PDF export functionality for timesheets and payroll processing
"""

import csv
import io
import logging
from datetime import date, datetime
from typing import Dict, Any, List, Optional
from decimal import Decimal

from flask import current_app
from reportlab.lib import colors
from reportlab.lib.pagesizes import letter, A4
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import inch

from ..services.time_tracking_service import time_tracking_service

logger = logging.getLogger(__name__)


class ExportService:
    """Service for exporting timesheet data in various formats"""

    def __init__(self):
        self.logger = logger

    # ==================== CSV EXPORT ====================

    def export_contract_hours_csv(
        self,
        contract_id: int,
        start_date: Optional[date] = None,
        end_date: Optional[date] = None,
        approved_only: bool = False
    ) -> Dict[str, Any]:
        """
        Export contract hours as CSV for payroll processing
        
        Args:
            contract_id: Contract ID
            start_date: Optional start date filter
            end_date: Optional end date filter
            approved_only: Only include approved entries
            
        Returns:
            Dictionary with success status and CSV data
        """
        try:
            # Get contract hours data
            if start_date and end_date:
                hours_result = time_tracking_service.get_hours_by_date_range(
                    contract_id=contract_id,
                    start_date=start_date,
                    end_date=end_date
                )
                if not hours_result.get('success'):
                    return hours_result
                
                # Extract entries from date range result
                entries = []
                for day_data in hours_result.get('by_date', []):
                    entries.extend(day_data.get('entries', []))
            else:
                hours_result = time_tracking_service.get_contract_hours(contract_id)
                if not hours_result.get('success'):
                    return hours_result
                
                entries = hours_result.get('entries', [])

            # Filter approved only if requested
            if approved_only:
                entries = [e for e in entries if e.get('is_approved')]

            if not entries:
                return {
                    "success": False,
                    "error": "No time entries found for export"
                }

            # Create CSV data
            csv_buffer = io.StringIO()
            csv_writer = csv.writer(csv_buffer)

            # Write header
            csv_writer.writerow([
                'Date',
                'Start Time',
                'End Time', 
                'Regular Hours',
                'Overtime Hours',
                'Total Hours',
                'Description',
                'Location',
                'Status',
                'Entry Type'
            ])

            # Write data rows
            total_regular = 0
            total_overtime = 0
            
            for entry in entries:
                regular_hours = entry.get('regular_hours', 0)
                overtime_hours = entry.get('overtime_hours', 0)
                total_hours = regular_hours + overtime_hours
                
                # Format times
                start_time = ""
                end_time = ""
                if entry.get('clock_in'):
                    try:
                        clock_in = datetime.fromisoformat(entry['clock_in'].replace('Z', '+00:00'))
                        start_time = clock_in.strftime('%H:%M')
                    except:
                        start_time = "Manual Entry"
                
                if entry.get('clock_out'):
                    try:
                        clock_out = datetime.fromisoformat(entry['clock_out'].replace('Z', '+00:00'))
                        end_time = clock_out.strftime('%H:%M')
                    except:
                        end_time = "Manual Entry"

                csv_writer.writerow([
                    entry.get('work_date', ''),
                    start_time,
                    end_time,
                    f"{regular_hours:.2f}",
                    f"{overtime_hours:.2f}",
                    f"{total_hours:.2f}",
                    entry.get('description', ''),
                    entry.get('location', ''),
                    'Approved' if entry.get('is_approved') else 'Pending',
                    'Clock In/Out' if not entry.get('is_manual_entry') else 'Manual Entry'
                ])
                
                total_regular += regular_hours
                total_overtime += overtime_hours

            # Write summary row
            csv_writer.writerow([])  # Empty row
            csv_writer.writerow([
                'TOTALS',
                '',
                '',
                f"{total_regular:.2f}",
                f"{total_overtime:.2f}",
                f"{total_regular + total_overtime:.2f}",
                f"{len(entries)} entries",
                '',
                f"{len([e for e in entries if e.get('is_approved')])} approved",
                ''
            ])

            # Get contract details for filename
            contract_details = hours_result.get('worker_name', 'Worker')
            date_range = f"{start_date}_{end_date}" if start_date and end_date else "all_dates"
            filename = f"timesheet_{contract_details.replace(' ', '_')}_{date_range}.csv"

            return {
                "success": True,
                "csv_data": csv_buffer.getvalue(),
                "filename": filename,
                "summary": {
                    "total_entries": len(entries),
                    "total_regular_hours": round(total_regular, 2),
                    "total_overtime_hours": round(total_overtime, 2),
                    "total_hours": round(total_regular + total_overtime, 2),
                    "approved_entries": len([e for e in entries if e.get('is_approved')])
                }
            }

        except Exception as e:
            self.logger.error(f"Error exporting CSV for contract {contract_id}: {e}")
            return {"success": False, "error": str(e)}

    # ==================== PDF EXPORT ====================

    def export_contract_hours_pdf(
        self,
        contract_id: int,
        start_date: Optional[date] = None,
        end_date: Optional[date] = None,
        approved_only: bool = False
    ) -> Dict[str, Any]:
        """
        Export contract hours as professional PDF timesheet
        
        Args:
            contract_id: Contract ID
            start_date: Optional start date filter
            end_date: Optional end date filter
            approved_only: Only include approved entries
            
        Returns:
            Dictionary with success status and PDF data
        """
        try:
            # Get contract hours data
            if start_date and end_date:
                hours_result = time_tracking_service.get_hours_by_date_range(
                    contract_id=contract_id,
                    start_date=start_date,
                    end_date=end_date
                )
                if not hours_result.get('success'):
                    return hours_result
                
                # Extract entries from date range result
                entries = []
                for day_data in hours_result.get('by_date', []):
                    entries.extend(day_data.get('entries', []))
            else:
                hours_result = time_tracking_service.get_contract_hours(contract_id)
                if not hours_result.get('success'):
                    return hours_result
                
                entries = hours_result.get('entries', [])

            # Filter approved only if requested
            if approved_only:
                entries = [e for e in entries if e.get('is_approved')]

            if not entries:
                return {
                    "success": False,
                    "error": "No time entries found for export"
                }

            # Create PDF document
            pdf_buffer = io.BytesIO()
            doc = SimpleDocTemplate(pdf_buffer, pagesize=A4, topMargin=72, bottomMargin=72)
            
            # Build PDF content
            story = []
            styles = getSampleStyleSheet()
            
            # Title
            title_style = ParagraphStyle(
                'CustomTitle',
                parent=styles['Heading1'],
                fontSize=18,
                spaceAfter=30,
                alignment=1  # Center alignment
            )
            
            title = Paragraph("RateRight Timesheet", title_style)
            story.append(title)
            
            # Contract information
            contract_info = hours_result
            info_text = f"""
            <b>Worker:</b> {contract_info.get('worker_name', 'N/A')}<br/>
            <b>Contract ID:</b> {contract_id}<br/>
            <b>Rate:</b> ${contract_info.get('contract_rate', 0):.2f} ({contract_info.get('rate_type', 'total')})<br/>
            <b>Generated:</b> {datetime.now().strftime('%B %d, %Y at %I:%M %p')}<br/>
            """
            
            if start_date and end_date:
                info_text += f"<b>Period:</b> {start_date} to {end_date}<br/>"
            
            info_para = Paragraph(info_text, styles['Normal'])
            story.append(info_para)
            story.append(Spacer(1, 20))

            # Create timesheet table
            table_data = [
                ['Date', 'Start', 'End', 'Regular', 'Overtime', 'Total', 'Location', 'Status']
            ]

            total_regular = 0
            total_overtime = 0

            for entry in entries:
                regular_hours = entry.get('regular_hours', 0)
                overtime_hours = entry.get('overtime_hours', 0)
                total_hours = regular_hours + overtime_hours
                
                # Format times
                start_time = "Manual"
                end_time = "Entry"
                if entry.get('clock_in') and entry.get('clock_out'):
                    try:
                        clock_in = datetime.fromisoformat(entry['clock_in'].replace('Z', '+00:00'))
                        clock_out = datetime.fromisoformat(entry['clock_out'].replace('Z', '+00:00'))
                        start_time = clock_in.strftime('%H:%M')
                        end_time = clock_out.strftime('%H:%M')
                    except:
                        pass

                table_data.append([
                    entry.get('work_date', ''),
                    start_time,
                    end_time,
                    f"{regular_hours:.1f}h",
                    f"{overtime_hours:.1f}h" if overtime_hours > 0 else "-",
                    f"{total_hours:.1f}h",
                    entry.get('location', '')[:20],  # Truncate location
                    '✓' if entry.get('is_approved') else 'Pending'
                ])
                
                total_regular += regular_hours
                total_overtime += overtime_hours

            # Add totals row
            table_data.append([
                'TOTAL',
                '',
                '',
                f"{total_regular:.1f}h",
                f"{total_overtime:.1f}h",
                f"{total_regular + total_overtime:.1f}h",
                f"{len(entries)} entries",
                f"{len([e for e in entries if e.get('is_approved')])} approved"
            ])

            # Create table
            table = Table(table_data, colWidths=[
                1.0*inch,  # Date
                0.7*inch,  # Start
                0.7*inch,  # End
                0.8*inch,  # Regular
                0.8*inch,  # Overtime
                0.7*inch,  # Total
                1.5*inch,  # Location
                0.8*inch   # Status
            ])

            # Style the table
            table.setStyle(TableStyle([
                # Header row
                ('BACKGROUND', (0, 0), (-1, 0), colors.grey),
                ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
                ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
                ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
                ('FONTSIZE', (0, 0), (-1, 0), 10),
                
                # Data rows
                ('FONTNAME', (0, 1), (-1, -2), 'Helvetica'),
                ('FONTSIZE', (0, 1), (-1, -2), 9),
                ('GRID', (0, 0), (-1, -2), 1, colors.black),
                
                # Totals row
                ('BACKGROUND', (0, -1), (-1, -1), colors.lightgrey),
                ('FONTNAME', (0, -1), (-1, -1), 'Helvetica-Bold'),
                ('FONTSIZE', (0, -1), (-1, -1), 10),
                
                # Alternating row colors
                ('ROWBACKGROUNDS', (0, 1), (-1, -2), [colors.beige, colors.white])
            ]))

            story.append(table)
            
            # Add payment calculation if hourly rate
            if contract_info.get('rate_type') == 'hourly':
                story.append(Spacer(1, 20))
                
                regular_pay = total_regular * contract_info.get('contract_rate', 0)
                overtime_pay = total_overtime * contract_info.get('contract_rate', 0)  # Should be 1.5x in production
                total_pay = regular_pay + overtime_pay
                
                payment_text = f"""
                <b>PAYMENT CALCULATION:</b><br/>
                Regular Hours: {total_regular:.1f}h × ${contract_info.get('contract_rate', 0):.2f} = ${regular_pay:.2f}<br/>
                Overtime Hours: {total_overtime:.1f}h × ${contract_info.get('contract_rate', 0):.2f} = ${overtime_pay:.2f}<br/>
                <b>Total Payment: ${total_pay:.2f}</b>
                """
                
                payment_para = Paragraph(payment_text, styles['Normal'])
                story.append(payment_para)

            # Build PDF
            doc.build(story)

            # Generate filename
            worker_name = contract_info.get('worker_name', 'Worker').replace(' ', '_')
            date_range = f"{start_date}_{end_date}" if start_date and end_date else "all_dates"
            filename = f"timesheet_{worker_name}_{date_range}.pdf"

            return {
                "success": True,
                "pdf_data": pdf_buffer.getvalue(),
                "filename": filename,
                "summary": {
                    "total_entries": len(entries),
                    "total_regular_hours": round(total_regular, 2),
                    "total_overtime_hours": round(total_overtime, 2),
                    "total_hours": round(total_regular + total_overtime, 2),
                    "approved_entries": len([e for e in entries if e.get('is_approved')])
                }
            }

        except Exception as e:
            self.logger.error(f"Error exporting PDF for contract {contract_id}: {e}")
            return {"success": False, "error": str(e)}

    # ==================== PAYROLL EXPORT ====================

    def export_payroll_summary_csv(
        self,
        contractor_id: int,
        start_date: date,
        end_date: date,
        approved_only: bool = True
    ) -> Dict[str, Any]:
        """
        Export payroll summary for all contractor's workers
        
        Args:
            contractor_id: Contractor ID
            start_date: Start date for payroll period
            end_date: End date for payroll period
            approved_only: Only include approved hours
            
        Returns:
            Dictionary with success status and payroll CSV data
        """
        try:
            from ..models.contract import Contract
            from ..models.user import User
            
            # Get all active contracts for contractor
            contracts = Contract.query.filter_by(
                contractor_id=contractor_id,
                status='active'
            ).all()

            if not contracts:
                return {
                    "success": False,
                    "error": "No active contracts found"
                }

            # Create payroll CSV
            csv_buffer = io.StringIO()
            csv_writer = csv.writer(csv_buffer)

            # Write header
            csv_writer.writerow([
                'Worker Name',
                'Contract ID',
                'Regular Hours',
                'Overtime Hours',
                'Total Hours',
                'Hourly Rate',
                'Regular Pay',
                'Overtime Pay',
                'Total Pay',
                'Period',
                'Approved Entries',
                'Pending Entries'
            ])

            grand_total_pay = 0
            grand_total_hours = 0

            for contract in contracts:
                # Get hours for this contract in date range
                hours_result = time_tracking_service.get_hours_by_date_range(
                    contract_id=contract.id,
                    start_date=start_date,
                    end_date=end_date
                )

                if not hours_result.get('success'):
                    continue

                # Extract and filter entries
                entries = []
                for day_data in hours_result.get('by_date', []):
                    entries.extend(day_data.get('entries', []))

                if approved_only:
                    entries = [e for e in entries if e.get('is_approved')]

                if not entries:
                    continue

                # Calculate totals for worker
                regular_hours = sum(e.get('regular_hours', 0) for e in entries)
                overtime_hours = sum(e.get('overtime_hours', 0) for e in entries)
                total_hours = regular_hours + overtime_hours

                # Calculate pay (only for hourly contracts)
                regular_pay = 0
                overtime_pay = 0
                total_pay = 0
                
                if contract.rate_type == 'hourly':
                    hourly_rate = float(contract.agreed_rate)
                    regular_pay = regular_hours * hourly_rate
                    overtime_pay = overtime_hours * hourly_rate  # Should be 1.5x in production
                    total_pay = regular_pay + overtime_pay

                csv_writer.writerow([
                    f"{contract.worker.first_name} {contract.worker.last_name}",
                    contract.id,
                    f"{regular_hours:.2f}",
                    f"{overtime_hours:.2f}",
                    f"{total_hours:.2f}",
                    f"${float(contract.agreed_rate):.2f}" if contract.rate_type == 'hourly' else 'Fixed Rate',
                    f"${regular_pay:.2f}" if contract.rate_type == 'hourly' else 'N/A',
                    f"${overtime_pay:.2f}" if contract.rate_type == 'hourly' else 'N/A',
                    f"${total_pay:.2f}" if contract.rate_type == 'hourly' else 'Fixed Contract',
                    f"{start_date} to {end_date}",
                    len([e for e in entries if e.get('is_approved')]),
                    len([e for e in entries if not e.get('is_approved')])
                ])

                grand_total_pay += total_pay
                grand_total_hours += total_hours

            # Write summary
            csv_writer.writerow([])
            csv_writer.writerow([
                'PAYROLL TOTALS',
                f"{len(contracts)} contracts",
                '',
                '',
                f"{grand_total_hours:.2f}",
                '',
                '',
                '',
                f"${grand_total_pay:.2f}",
                f"{start_date} to {end_date}",
                '',
                ''
            ])

            filename = f"payroll_summary_{start_date}_{end_date}.csv"

            return {
                "success": True,
                "csv_data": csv_buffer.getvalue(),
                "filename": filename,
                "summary": {
                    "total_contracts": len(contracts),
                    "total_hours": round(grand_total_hours, 2),
                    "total_payroll": round(grand_total_pay, 2),
                    "period": f"{start_date} to {end_date}"
                }
            }

        except Exception as e:
            self.logger.error(f"Error exporting payroll summary: {e}")
            return {"success": False, "error": str(e)}

    # ==================== HELPER METHODS ====================

    def _format_currency(self, amount: float) -> str:
        """Format currency for Australian dollars"""
        return f"${amount:,.2f} AUD"

    def _get_contract_export_data(self, contract_id: int) -> Dict[str, Any]:
        """Get contract data optimized for export"""
        from ..models.contract import Contract
        
        contract = Contract.query.get(contract_id)
        if not contract:
            return {"success": False, "error": "Contract not found"}
        
        return {
            "success": True,
            "contract": contract,
            "worker_name": f"{contract.worker.first_name} {contract.worker.last_name}",
            "contractor_name": f"{contract.contractor.first_name} {contract.contractor.last_name}",
            "job_title": contract.job.title,
            "rate": float(contract.agreed_rate),
            "rate_type": contract.rate_type
        }


# Singleton instance
export_service = ExportService()
