"""
Purchase Order Event Consumer
Listens for PO events and sends email notifications to suppliers
"""
import json
import structlog
from pathlib import Path
from typing import Dict, Any
from jinja2 import Environment, FileSystemLoader
from datetime import datetime

from shared.messaging import RabbitMQClient
from app.services.email_service import EmailService
from app.services.whatsapp_service import WhatsAppService

logger = structlog.get_logger()


class POEventConsumer:
    """
    Consumes purchase order events from RabbitMQ and sends notifications
    Sends both email and WhatsApp notifications to suppliers
    """

    def __init__(self, email_service: EmailService, whatsapp_service: WhatsAppService = None):
        self.email_service = email_service
        self.whatsapp_service = whatsapp_service

        # Setup Jinja2 template environment
        template_dir = Path(__file__).parent.parent / 'templates'
        self.jinja_env = Environment(
            loader=FileSystemLoader(str(template_dir)),
            autoescape=True
        )

    async def consume_po_approved_event(
        self,
        rabbitmq_client: RabbitMQClient
    ):
        """
        Start consuming PO approved events from RabbitMQ
        """
        async def process_message(message):
            """Process a single PO approved event message"""
            try:
                async with message.process():
                    # Parse event data
                    event_data = json.loads(message.body.decode())
                    logger.info(
                        "Received PO approved event",
                        event_id=event_data.get('event_id'),
                        po_id=event_data.get('data', {}).get('po_id')
                    )

                    # Send notification email
                    email_success = await self.send_po_approved_email(event_data)

                    # Send WhatsApp notification if service is available
                    whatsapp_success = False
                    if self.whatsapp_service:
                        whatsapp_success = await self.send_po_approved_whatsapp(event_data)

                    if email_success:
                        logger.info(
                            "PO approved email sent successfully",
                            po_id=event_data.get('data', {}).get('po_id'),
                            whatsapp_sent=whatsapp_success
                        )
                    else:
                        logger.error(
                            "Failed to send PO approved email",
                            po_id=event_data.get('data', {}).get('po_id'),
                            whatsapp_sent=whatsapp_success
                        )

            except Exception as e:
                logger.error(
                    "Error processing PO approved event",
                    error=str(e),
                    exc_info=True
                )

        # Start consuming events
        await rabbitmq_client.consume_events(
            exchange_name="procurement.events",
            queue_name="notification.po.approved",
            routing_key="po.approved",
            callback=process_message
        )

        logger.info("Started consuming PO approved events")

    async def send_po_approved_email(self, event_data: Dict[str, Any]) -> bool:
        """
        Send PO approved email to supplier

        Args:
            event_data: Full event payload from RabbitMQ

        Returns:
            bool: True if email sent successfully
        """
        try:
            # Extract data from event
            data = event_data.get('data', {})

            # Required fields
            supplier_email = data.get('supplier_email')
            if not supplier_email:
                logger.warning(
                    "No supplier email in event, skipping notification",
                    po_id=data.get('po_id')
                )
                return False

            # Prepare template context
            context = self._prepare_email_context(data)

            # Render HTML email from template
            template = self.jinja_env.get_template('po_approved_email.html')
            html_content = template.render(**context)

            # Prepare plain text version (fallback)
            text_content = self._generate_text_email(context)

            # Send email
            subject = f"New Purchase Order #{data.get('po_number', 'N/A')}"

            success = await self.email_service.send_email(
                to_email=supplier_email,
                subject=subject,
                text_content=text_content,
                html_content=html_content,
                from_name=context.get('bakery_name', 'Bakery Management System'),
                reply_to=context.get('bakery_email')
            )

            return success

        except Exception as e:
            logger.error(
                "Error sending PO approved email",
                error=str(e),
                po_id=data.get('po_id'),
                exc_info=True
            )
            return False

    async def _get_tenant_settings(self, tenant_id: str) -> Dict[str, Any]:
        """Fetch tenant settings from tenant service"""
        try:
            from shared.clients.tenant_client import TenantServiceClient
            from shared.config.base import get_settings

            config = get_settings()
            tenant_client = TenantServiceClient(config)

            # Get tenant details
            tenant = await tenant_client.get_tenant(tenant_id)
            if not tenant:
                logger.warning("Could not fetch tenant details", tenant_id=tenant_id)
                return {}

            return {
                'name': tenant.get('business_name') or tenant.get('name', 'Your Bakery'),
                'email': tenant.get('email', 'info@yourbakery.com'),
                'phone': tenant.get('phone', '+34 XXX XXX XXX'),
                'address': tenant.get('address', 'Your Bakery Address'),
                'contact_person': tenant.get('contact_person', 'Bakery Manager')
            }
        except Exception as e:
            logger.error("Failed to fetch tenant settings", tenant_id=tenant_id, error=str(e))
            return {}

    async def _prepare_email_context(self, data: Dict[str, Any]) -> Dict[str, Any]:
        """
        Prepare context data for email template

        Args:
            data: Event data from RabbitMQ

        Returns:
            Dict with all template variables
        """
        # Extract items and format them
        items = data.get('items', [])
        formatted_items = []
        for item in items:
            formatted_items.append({
                'product_name': item.get('product_name', 'N/A'),
                'ordered_quantity': f"{item.get('ordered_quantity', 0):.2f}",
                'unit_of_measure': item.get('unit_of_measure', 'units'),
                'unit_price': f"{item.get('unit_price', 0):.2f}",
                'line_total': f"{item.get('line_total', 0):.2f}"
            })

        # Determine currency symbol
        currency = data.get('currency', 'EUR')
        currency_symbol = '€' if currency == 'EUR' else '$'

        # Format dates
        order_date = self._format_datetime(data.get('approved_at'))
        required_delivery_date = self._format_date(data.get('required_delivery_date'))

        # Build context
        context = {
            # PO Details
            'po_number': data.get('po_number', 'N/A'),
            'order_date': order_date,
            'required_delivery_date': required_delivery_date or 'To be confirmed',
            'total_amount': f"{data.get('total_amount', 0):.2f}",
            'currency': currency,
            'currency_symbol': currency_symbol,

            # Supplier Info
            'supplier_name': data.get('supplier_name', 'Valued Supplier'),

            # Items
            'items': formatted_items,
        }

        # Fetch tenant settings (bakery info)
        tenant_id = data.get('tenant_id')
        tenant_settings = {}
        if tenant_id:
            tenant_settings = await self._get_tenant_settings(tenant_id)

        # Add bakery info from tenant settings with fallbacks
        context.update({
            'bakery_name': tenant_settings.get('name', 'Your Bakery Name'),
            'bakery_email': tenant_settings.get('email', 'orders@yourbakery.com'),
            'bakery_phone': tenant_settings.get('phone', '+34 XXX XXX XXX'),
            'bakery_address': tenant_settings.get('address', 'Your Bakery Address'),
            'delivery_address': data.get('delivery_address') or tenant_settings.get('address', 'Bakery Delivery Address'),
            'contact_person': data.get('contact_person') or tenant_settings.get('contact_person', 'Bakery Manager'),
            'contact_phone': data.get('contact_phone') or tenant_settings.get('phone', '+34 XXX XXX XXX'),

            # Payment & Delivery Terms - From PO data with fallbacks
            'payment_terms': data.get('payment_terms', 'Net 30 days'),
            'delivery_instructions': data.get('delivery_instructions', 'Please deliver during business hours'),
            'notes': data.get('notes'),
        })

        return context

    def _generate_text_email(self, context: Dict[str, Any]) -> str:
        """
        Generate plain text version of email

        Args:
            context: Template context

        Returns:
            Plain text email content
        """
        items_text = "\n".join([
            f"  - {item['product_name']}: {item['ordered_quantity']} {item['unit_of_measure']} "
            f"× {context['currency_symbol']}{item['unit_price']} = {context['currency_symbol']}{item['line_total']}"
            for item in context['items']
        ])

        text = f"""
New Purchase Order #{context['po_number']}

Dear {context['supplier_name']},

We would like to place the following purchase order:

ORDER DETAILS:
- PO Number: {context['po_number']}
- Order Date: {context['order_date']}
- Required Delivery: {context['required_delivery_date']}
- Delivery Address: {context['delivery_address']}

ORDER ITEMS:
{items_text}

TOTAL AMOUNT: {context['currency_symbol']}{context['total_amount']} {context['currency']}

PAYMENT & DELIVERY:
- Payment Terms: {context['payment_terms']}
- Delivery Instructions: {context['delivery_instructions']}
- Contact Person: {context['contact_person']}
- Phone: {context['contact_phone']}

Please confirm receipt of this order by replying to this email.

Thank you for your continued partnership.

Best regards,
{context['bakery_name']}
{context['bakery_address']}
Phone: {context['bakery_phone']}
Email: {context['bakery_email']}

---
This is an automated email from your Bakery Management System.
"""
        return text.strip()

    def _format_datetime(self, iso_datetime: str) -> str:
        """Format ISO datetime string to readable format"""
        if not iso_datetime:
            return 'N/A'
        try:
            dt = datetime.fromisoformat(iso_datetime.replace('Z', '+00:00'))
            return dt.strftime('%B %d, %Y at %H:%M')
        except Exception:
            return iso_datetime

    def _format_date(self, iso_date: str) -> str:
        """Format ISO date string to readable format"""
        if not iso_date:
            return None
        try:
            if 'T' in iso_date:
                dt = datetime.fromisoformat(iso_date.replace('Z', '+00:00'))
            else:
                dt = datetime.fromisoformat(iso_date)
            return dt.strftime('%B %d, %Y')
        except Exception:
            return iso_date

    async def send_po_approved_whatsapp(self, event_data: Dict[str, Any]) -> bool:
        """
        Send PO approved WhatsApp notification to supplier

        This sends a WhatsApp Business template message notifying the supplier
        of a new purchase order. The template must be pre-approved in Meta Business Suite.

        Args:
            event_data: Full event payload from RabbitMQ

        Returns:
            bool: True if WhatsApp message sent successfully
        """
        try:
            # Extract data from event
            data = event_data.get('data', {})

            # Check for supplier phone number
            supplier_phone = data.get('supplier_phone')
            if not supplier_phone:
                logger.debug(
                    "No supplier phone in event, skipping WhatsApp notification",
                    po_id=data.get('po_id')
                )
                return False

            # Extract tenant ID for tracking
            tenant_id = data.get('tenant_id')

            # Prepare template parameters
            # Template: "Hola {{1}}, has recibido una nueva orden de compra {{2}} por un total de {{3}}."
            # Parameters: supplier_name, po_number, total_amount
            template_params = [
                data.get('supplier_name', 'Estimado proveedor'),
                data.get('po_number', 'N/A'),
                f"€{data.get('total_amount', 0):.2f}"
            ]

            # Send WhatsApp template message
            # The template must be named 'po_notification' and approved in Meta Business Suite
            success = await self.whatsapp_service.send_message(
                to_phone=supplier_phone,
                message="",  # Not used for template messages
                template_name="po_notification",  # Must match template name in Meta
                template_params=template_params,
                tenant_id=tenant_id
            )

            if success:
                logger.info(
                    "PO approved WhatsApp sent successfully",
                    po_id=data.get('po_id'),
                    supplier_phone=supplier_phone,
                    template="po_notification"
                )
            else:
                logger.warning(
                    "Failed to send PO approved WhatsApp",
                    po_id=data.get('po_id'),
                    supplier_phone=supplier_phone
                )

            return success

        except Exception as e:
            logger.error(
                "Error sending PO approved WhatsApp",
                error=str(e),
                po_id=data.get('po_id'),
                exc_info=True
            )
            return False
