"""
Invoice Generation Worker Tasks
Handles invoice creation, PDF generation, and reminders
"""
import logging
from datetime import datetime, timedelta
from decimal import Decimal
from typing import Optional, Dict, Any, List
import json

from celery import shared_task
from sqlalchemy import select, and_
from sqlalchemy.orm import selectinload

from app.workers.celery_app import celery
from app.database import get_sync_session
from app.models.billing import Invoice, InvoiceStatus, UsageLog, UsageSummary
from app.models.subscription import Subscription, Plan
from app.models.client import Client

logger = logging.getLogger(__name__)


@celery.task(
    bind=True,
    name="app.workers.tasks.invoice_tasks.generate_invoice",
    max_retries=3,
    default_retry_delay=300,
    queue="invoices"
)
def generate_invoice(
    self,
    client_id: int,
    billing_period_start: str,
    billing_period_end: str,
    additional_items: Optional[List[Dict[str, Any]]] = None
) -> Dict[str, Any]:
    """
    Generate invoice for a specific client

    Args:
        client_id: Client ID
        billing_period_start: Start date (ISO format)
        billing_period_end: End date (ISO format)
        additional_items: Additional line items to include
    """
    try:
        logger.info(f"Generating invoice for client {client_id}")

        with get_sync_session() as session:
            # Get client with subscription
            client = session.execute(
                select(Client)
                .options(selectinload(Client.subscription))
                .where(Client.id == client_id)
            ).scalar_one_or_none()

            if not client:
                logger.error(f"Client {client_id} not found")
                return {"success": False, "error": "Client not found"}

            if not client.subscription:
                logger.warning(f"Client {client_id} has no subscription")
                return {"success": False, "error": "No active subscription"}

            subscription = client.subscription

            # Get plan details
            plan = session.execute(
                select(Plan).where(Plan.id == subscription.plan_id)
            ).scalar_one_or_none()

            if not plan:
                logger.error(f"Plan {subscription.plan_id} not found")
                return {"success": False, "error": "Plan not found"}

            # Parse dates
            period_start = datetime.fromisoformat(billing_period_start)
            period_end = datetime.fromisoformat(billing_period_end)

            # Calculate line items
            line_items = []

            # Base plan charge
            line_items.append({
                "description": f"{plan.name} - {plan.billing_cycle.capitalize()} Subscription",
                "quantity": 1,
                "unit_price": float(plan.price),
                "amount": float(plan.price)
            })

            # Get usage for the billing period
            billing_period = period_start.strftime("%Y-%m")
            usage_summary = session.execute(
                select(UsageSummary).where(
                    and_(
                        UsageSummary.client_id == client_id,
                        UsageSummary.period_type == "monthly",
                        UsageSummary.period_date == billing_period
                    )
                )
            ).scalar_one_or_none()

            # Calculate overage charges
            if usage_summary and plan.messages_per_month:
                excess_messages = max(0, usage_summary.total_messages - plan.messages_per_month)
                if excess_messages > 0:
                    overage_rate = Decimal("0.05")  # ₹0.05 per extra message
                    overage_amount = excess_messages * overage_rate
                    line_items.append({
                        "description": f"Additional Messages ({excess_messages} messages)",
                        "quantity": excess_messages,
                        "unit_price": float(overage_rate),
                        "amount": float(overage_amount)
                    })

            # Add any additional items
            if additional_items:
                line_items.extend(additional_items)

            # Calculate totals
            subtotal = sum(item["amount"] for item in line_items)
            tax_percentage = Decimal("18")  # 18% GST
            tax_amount = Decimal(str(subtotal)) * (tax_percentage / 100)
            total_amount = Decimal(str(subtotal)) + tax_amount

            # Generate invoice number
            invoice_number = Invoice.generate_invoice_number()

            # Create invoice
            invoice = Invoice(
                client_id=client_id,
                subscription_id=subscription.id,
                invoice_number=invoice_number,
                status=InvoiceStatus.PENDING,
                billing_period_start=period_start,
                billing_period_end=period_end,
                subtotal=subtotal,
                tax_percentage=tax_percentage,
                tax_amount=tax_amount,
                discount_amount=0,
                total_amount=total_amount,
                currency="INR",
                line_items=line_items,
                issue_date=datetime.utcnow(),
                due_date=datetime.utcnow() + timedelta(days=15)
            )

            session.add(invoice)
            session.commit()
            session.refresh(invoice)

            logger.info(f"Invoice {invoice_number} created for client {client_id}")

            # Queue PDF generation and email
            generate_invoice_pdf.delay(invoice.id)
            send_invoice_email.delay(invoice.id)

            return {
                "success": True,
                "invoice_id": invoice.id,
                "invoice_number": invoice_number,
                "total_amount": float(total_amount)
            }

    except Exception as e:
        logger.error(f"Error generating invoice for client {client_id}: {str(e)}")
        self.retry(exc=e)


@celery.task(
    bind=True,
    name="app.workers.tasks.invoice_tasks.generate_all_invoices",
    max_retries=2,
    queue="invoices"
)
def generate_all_invoices(self, billing_period: Optional[str] = None) -> Dict[str, Any]:
    """
    Generate invoices for all active clients

    Args:
        billing_period: Optional billing period (YYYY-MM), defaults to previous month
    """
    try:
        logger.info("Starting batch invoice generation")

        # Calculate billing period
        if billing_period:
            year, month = map(int, billing_period.split("-"))
        else:
            # Default to previous month
            today = datetime.utcnow()
            if today.month == 1:
                year, month = today.year - 1, 12
            else:
                year, month = today.year, today.month - 1

        # Calculate period dates
        period_start = datetime(year, month, 1)
        if month == 12:
            period_end = datetime(year + 1, 1, 1) - timedelta(seconds=1)
        else:
            period_end = datetime(year, month + 1, 1) - timedelta(seconds=1)

        with get_sync_session() as session:
            # Get all clients with active subscriptions
            clients = session.execute(
                select(Client)
                .options(selectinload(Client.subscription))
                .where(Client.is_active == True)
            ).scalars().all()

            results = {
                "total_clients": len(clients),
                "invoices_generated": 0,
                "invoices_failed": 0,
                "errors": []
            }

            for client in clients:
                if client.subscription and client.subscription.status == "active":
                    # Queue individual invoice generation
                    try:
                        generate_invoice.delay(
                            client_id=client.id,
                            billing_period_start=period_start.isoformat(),
                            billing_period_end=period_end.isoformat()
                        )
                        results["invoices_generated"] += 1
                    except Exception as e:
                        results["invoices_failed"] += 1
                        results["errors"].append({
                            "client_id": client.id,
                            "error": str(e)
                        })

        logger.info(f"Batch invoice generation complete: {results}")
        return results

    except Exception as e:
        logger.error(f"Error in batch invoice generation: {str(e)}")
        self.retry(exc=e)


@celery.task(
    bind=True,
    name="app.workers.tasks.invoice_tasks.generate_invoice_pdf",
    max_retries=3,
    queue="invoices"
)
def generate_invoice_pdf(self, invoice_id: int) -> Dict[str, Any]:
    """
    Generate PDF for an invoice

    Args:
        invoice_id: Invoice ID
    """
    try:
        logger.info(f"Generating PDF for invoice {invoice_id}")

        with get_sync_session() as session:
            invoice = session.execute(
                select(Invoice)
                .options(selectinload(Invoice.client))
                .where(Invoice.id == invoice_id)
            ).scalar_one_or_none()

            if not invoice:
                return {"success": False, "error": "Invoice not found"}

            # Generate GST-compliant PDF
            from app.services.invoice_pdf_generator import generate_gst_invoice_pdf
            pdf_content = generate_gst_invoice_pdf(invoice, invoice.client)

            # Upload to storage
            from app.services.storage_service import StorageService
            storage = StorageService()

            filename = f"invoices/{invoice.invoice_number}.pdf"
            pdf_url = storage.upload_file(
                file_content=pdf_content,
                filename=filename,
                content_type="application/pdf"
            )

            # Update invoice with PDF URL
            invoice.pdf_url = pdf_url
            session.commit()

            logger.info(f"PDF generated for invoice {invoice_id}: {pdf_url}")

            return {
                "success": True,
                "pdf_url": pdf_url
            }

    except Exception as e:
        logger.error(f"Error generating PDF for invoice {invoice_id}: {str(e)}")
        self.retry(exc=e)


@celery.task(
    bind=True,
    name="app.workers.tasks.invoice_tasks.send_invoice_email",
    max_retries=3,
    queue="emails"
)
def send_invoice_email(self, invoice_id: int) -> Dict[str, Any]:
    """
    Send invoice email to client

    Args:
        invoice_id: Invoice ID
    """
    try:
        logger.info(f"Sending invoice email for invoice {invoice_id}")

        with get_sync_session() as session:
            invoice = session.execute(
                select(Invoice)
                .options(selectinload(Invoice.client))
                .where(Invoice.id == invoice_id)
            ).scalar_one_or_none()

            if not invoice:
                return {"success": False, "error": "Invoice not found"}

            # Import email service
            from app.workers.tasks.email_tasks import send_templated_email

            # Send email
            result = send_templated_email.delay(
                template_name="invoice",
                recipient_email=invoice.client.email,
                recipient_name=invoice.client.company_name,
                context={
                    "invoice_number": invoice.invoice_number,
                    "amount": str(invoice.total_amount),
                    "currency": invoice.currency,
                    "due_date": invoice.due_date.strftime("%B %d, %Y"),
                    "pdf_url": invoice.pdf_url,
                    "client_name": invoice.client.company_name,
                    "billing_period": f"{invoice.billing_period_start.strftime('%B %Y')}"
                },
                attachments=[{
                    "filename": f"{invoice.invoice_number}.pdf",
                    "url": invoice.pdf_url
                }] if invoice.pdf_url else None
            )

            logger.info(f"Invoice email sent for invoice {invoice_id}")

            return {
                "success": True,
                "email_task_id": result.id
            }

    except Exception as e:
        logger.error(f"Error sending invoice email for {invoice_id}: {str(e)}")
        self.retry(exc=e)


@celery.task(
    bind=True,
    name="app.workers.tasks.invoice_tasks.send_invoice_reminders",
    max_retries=2,
    queue="invoices"
)
def send_invoice_reminders(self) -> Dict[str, Any]:
    """
    Send reminders for pending invoices approaching due date
    """
    try:
        logger.info("Processing invoice reminders")

        with get_sync_session() as session:
            # Find pending invoices due within 3 days
            reminder_threshold = datetime.utcnow() + timedelta(days=3)

            invoices = session.execute(
                select(Invoice)
                .options(selectinload(Invoice.client))
                .where(
                    and_(
                        Invoice.status == InvoiceStatus.PENDING,
                        Invoice.due_date <= reminder_threshold,
                        Invoice.due_date > datetime.utcnow()
                    )
                )
            ).scalars().all()

            results = {
                "reminders_sent": 0,
                "errors": []
            }

            for invoice in invoices:
                try:
                    from app.workers.tasks.email_tasks import send_templated_email

                    send_templated_email.delay(
                        template_name="invoice_reminder",
                        recipient_email=invoice.client.email,
                        recipient_name=invoice.client.company_name,
                        context={
                            "invoice_number": invoice.invoice_number,
                            "amount": str(invoice.total_amount),
                            "currency": invoice.currency,
                            "due_date": invoice.due_date.strftime("%B %d, %Y"),
                            "days_until_due": (invoice.due_date - datetime.utcnow()).days,
                            "payment_url": f"https://portal.ai360.com/invoices/{invoice.id}/pay"
                        }
                    )
                    results["reminders_sent"] += 1
                except Exception as e:
                    results["errors"].append({
                        "invoice_id": invoice.id,
                        "error": str(e)
                    })

            logger.info(f"Invoice reminders sent: {results}")
            return results

    except Exception as e:
        logger.error(f"Error processing invoice reminders: {str(e)}")
        self.retry(exc=e)


@celery.task(
    bind=True,
    name="app.workers.tasks.invoice_tasks.mark_overdue_invoices",
    max_retries=2,
    queue="invoices"
)
def mark_overdue_invoices(self) -> Dict[str, Any]:
    """
    Mark invoices past due date as overdue
    """
    try:
        logger.info("Processing overdue invoices")

        with get_sync_session() as session:
            # Find pending invoices past due date
            overdue_invoices = session.execute(
                select(Invoice)
                .options(selectinload(Invoice.client))
                .where(
                    and_(
                        Invoice.status == InvoiceStatus.PENDING,
                        Invoice.due_date < datetime.utcnow()
                    )
                )
            ).scalars().all()

            results = {
                "invoices_marked_overdue": 0,
                "notifications_sent": 0,
                "errors": []
            }

            for invoice in overdue_invoices:
                try:
                    # Mark as overdue
                    invoice.status = InvoiceStatus.OVERDUE
                    session.commit()
                    results["invoices_marked_overdue"] += 1

                    # Send overdue notification
                    from app.workers.tasks.email_tasks import send_templated_email

                    send_templated_email.delay(
                        template_name="invoice_overdue",
                        recipient_email=invoice.client.email,
                        recipient_name=invoice.client.company_name,
                        context={
                            "invoice_number": invoice.invoice_number,
                            "amount": str(invoice.total_amount),
                            "currency": invoice.currency,
                            "due_date": invoice.due_date.strftime("%B %d, %Y"),
                            "days_overdue": (datetime.utcnow() - invoice.due_date).days,
                            "payment_url": f"https://portal.ai360.com/invoices/{invoice.id}/pay"
                        }
                    )
                    results["notifications_sent"] += 1

                except Exception as e:
                    results["errors"].append({
                        "invoice_id": invoice.id,
                        "error": str(e)
                    })

            logger.info(f"Overdue invoices processed: {results}")
            return results

    except Exception as e:
        logger.error(f"Error processing overdue invoices: {str(e)}")
        self.retry(exc=e)


def _generate_invoice_pdf_content(invoice: Invoice) -> bytes:
    """
    Generate PDF content for an invoice
    This is a placeholder - implement using reportlab, weasyprint, or similar
    """
    try:
        from io import BytesIO
        from reportlab.lib import colors
        from reportlab.lib.pagesizes import A4
        from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer
        from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle

        buffer = BytesIO()
        doc = SimpleDocTemplate(buffer, pagesize=A4)
        elements = []

        styles = getSampleStyleSheet()

        # Header
        elements.append(Paragraph(f"Invoice: {invoice.invoice_number}", styles['Heading1']))
        elements.append(Spacer(1, 20))

        # Client info
        elements.append(Paragraph(f"Bill To: {invoice.client.company_name}", styles['Normal']))
        elements.append(Paragraph(f"Email: {invoice.client.email}", styles['Normal']))
        elements.append(Spacer(1, 20))

        # Invoice details
        elements.append(Paragraph(f"Issue Date: {invoice.issue_date.strftime('%B %d, %Y')}", styles['Normal']))
        elements.append(Paragraph(f"Due Date: {invoice.due_date.strftime('%B %d, %Y')}", styles['Normal']))
        elements.append(Spacer(1, 20))

        # Line items table
        data = [["Description", "Qty", "Unit Price", "Amount"]]
        for item in invoice.line_items:
            data.append([
                item["description"],
                str(item["quantity"]),
                f"₹{item['unit_price']:.2f}",
                f"₹{item['amount']:.2f}"
            ])

        # Totals
        data.append(["", "", "Subtotal:", f"₹{invoice.subtotal:.2f}"])
        data.append(["", "", f"Tax ({invoice.tax_percentage}%):", f"₹{invoice.tax_amount:.2f}"])
        data.append(["", "", "Total:", f"₹{invoice.total_amount:.2f}"])

        table = Table(data)
        table.setStyle(TableStyle([
            ('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), 12),
            ('BOTTOMPADDING', (0, 0), (-1, 0), 12),
            ('BACKGROUND', (0, 1), (-1, -1), colors.beige),
            ('GRID', (0, 0), (-1, -1), 1, colors.black)
        ]))

        elements.append(table)

        doc.build(elements)
        return buffer.getvalue()

    except ImportError:
        # If reportlab not installed, return placeholder
        logger.warning("reportlab not installed, returning placeholder PDF")
        return b"%PDF-1.4 placeholder"
