# ================================================================
# services/orders/app/services/orders_service.py
# ================================================================
"""
Orders Service - Main business logic service
"""

import uuid
from datetime import datetime, date, timedelta
from decimal import Decimal
from typing import List, Optional, Dict, Any
from uuid import UUID
import structlog

from shared.clients import (
    InventoryServiceClient,
    ProductionServiceClient,
    SalesServiceClient
)
from shared.database.transactions import transactional

from app.core.config import settings
from app.repositories.order_repository import (
    OrderRepository, 
    CustomerRepository, 
    OrderItemRepository, 
    OrderStatusHistoryRepository
)
from app.schemas.order_schemas import (
    OrderCreate, 
    OrderUpdate, 
    OrderResponse,
    CustomerCreate,
    CustomerUpdate,
    DemandRequirements,
    OrdersDashboardSummary
)

logger = structlog.get_logger()


class OrdersService:
    """Main service for orders operations"""
    
    def __init__(
        self,
        order_repo: OrderRepository,
        customer_repo: CustomerRepository,
        order_item_repo: OrderItemRepository,
        status_history_repo: OrderStatusHistoryRepository,
        inventory_client: InventoryServiceClient,
        production_client: ProductionServiceClient,
        sales_client: SalesServiceClient,
    ):
        self.order_repo = order_repo
        self.customer_repo = customer_repo
        self.order_item_repo = order_item_repo
        self.status_history_repo = status_history_repo
        self.inventory_client = inventory_client
        self.production_client = production_client
        self.sales_client = sales_client
    
    async def create_order(
        self,
        db,
        order_data: OrderCreate,
        user_id: Optional[UUID] = None
    ) -> OrderResponse:
        """Create a new customer order with comprehensive processing"""
        try:
            logger.info("Creating new order", 
                       customer_id=str(order_data.customer_id),
                       tenant_id=str(order_data.tenant_id))
            
            # 1. Validate customer exists
            customer = await self.customer_repo.get(
                db, 
                order_data.customer_id, 
                order_data.tenant_id
            )
            if not customer:
                raise ValueError(f"Customer {order_data.customer_id} not found")
            
            # 2. Generate order number
            order_number = await self._generate_order_number(db, order_data.tenant_id)
            
            # 3. Calculate order totals
            subtotal = sum(item.quantity * item.unit_price - item.line_discount 
                          for item in order_data.items)
            discount_amount = subtotal * (order_data.discount_percentage / 100)
            tax_amount = (subtotal - discount_amount) * Decimal("0.08")  # Configurable tax rate
            total_amount = subtotal - discount_amount + tax_amount + order_data.delivery_fee
            
            # 4. Create order record
            order_dict = order_data.dict(exclude={"items"})
            order_dict.update({
                "order_number": order_number,
                "subtotal": subtotal,
                "discount_amount": discount_amount,
                "tax_amount": tax_amount,
                "total_amount": total_amount,
                "status": "pending"
            })
            
            order = await self.order_repo.create(db, obj_in=order_dict, created_by=user_id)
            
            # 5. Create order items
            for item_data in order_data.items:
                item_dict = item_data.dict()
                item_dict.update({
                    "order_id": order.id,
                    "line_total": item_data.quantity * item_data.unit_price - item_data.line_discount
                })
                await self.order_item_repo.create(db, obj_in=item_dict)
            
            # 6. Create initial status history
            await self.status_history_repo.create_status_change(
                db=db,
                order_id=order.id,
                from_status=None,
                to_status="pending",
                change_reason="Order created",
                changed_by=user_id
            )
            
            # 7. Update customer metrics
            await self.customer_repo.update_customer_metrics(
                db, order.customer_id, total_amount, order.order_date
            )
            
            # 8. Business model detection
            business_model = await self.detect_business_model(db, order_data.tenant_id)
            if business_model:
                order.business_model = business_model
            
            
            # 10. Integrate with production service if auto-processing is enabled
            if settings.ORDER_PROCESSING_ENABLED:
                await self._notify_production_service(order)
            
            logger.info("Order created successfully", 
                       order_id=str(order.id),
                       order_number=order_number,
                       total_amount=str(total_amount))
            
            # Return order with items loaded
            return await self.get_order_with_items(db, order.id, order_data.tenant_id)
            
        except Exception as e:
            logger.error("Error creating order", error=str(e))
            raise
    
    async def get_order_with_items(
        self, 
        db, 
        order_id: UUID, 
        tenant_id: UUID
    ) -> Optional[OrderResponse]:
        """Get order with all related data"""
        try:
            order = await self.order_repo.get_with_items(db, order_id, tenant_id)
            if not order:
                return None
            
            return OrderResponse.from_orm(order)
        except Exception as e:
            logger.error("Error getting order with items", 
                        order_id=str(order_id), 
                        error=str(e))
            raise
    
    async def update_order_status(
        self,
        db,
        order_id: UUID,
        tenant_id: UUID,
        new_status: str,
        user_id: Optional[UUID] = None,
        reason: Optional[str] = None
    ) -> Optional[OrderResponse]:
        """Update order status with proper tracking"""
        try:
            order = await self.order_repo.get(db, order_id, tenant_id)
            if not order:
                return None
            
            old_status = order.status
            
            # Update order status
            order.status = new_status
            if new_status == "confirmed":
                order.confirmed_delivery_date = order.requested_delivery_date
            elif new_status == "delivered":
                order.actual_delivery_date = datetime.now()
            
            # Record status change
            await self.status_history_repo.create_status_change(
                db=db,
                order_id=order_id,
                from_status=old_status,
                to_status=new_status,
                change_reason=reason,
                changed_by=user_id
            )
            
            # Customer notifications
            await self._send_status_notification(order, old_status, new_status)
            
            logger.info("Order status updated", 
                       order_id=str(order_id),
                       old_status=old_status,
                       new_status=new_status)
            
            return await self.get_order_with_items(db, order_id, tenant_id)
            
        except Exception as e:
            logger.error("Error updating order status", 
                        order_id=str(order_id), 
                        error=str(e))
            raise
    
    async def get_demand_requirements(
        self, 
        db, 
        tenant_id: UUID, 
        target_date: date
    ) -> DemandRequirements:
        """Get demand requirements for production planning"""
        try:
            logger.info("Calculating demand requirements", 
                       tenant_id=str(tenant_id),
                       target_date=str(target_date))
            
            # Get orders for target date
            orders = await self.order_repo.get_pending_orders_by_delivery_date(
                db, tenant_id, target_date
            )
            
            # Aggregate product demands
            product_demands = {}
            total_orders = len(orders)
            total_quantity = Decimal("0")
            total_value = Decimal("0")
            rush_orders_count = 0
            special_requirements = []
            earliest_delivery = None
            latest_delivery = None
            
            for order in orders:
                total_value += order.total_amount
                
                if order.order_type == "rush":
                    rush_orders_count += 1
                
                if order.special_instructions:
                    special_requirements.append(order.special_instructions)
                
                # Track delivery timing
                if not earliest_delivery or order.requested_delivery_date < earliest_delivery:
                    earliest_delivery = order.requested_delivery_date
                if not latest_delivery or order.requested_delivery_date > latest_delivery:
                    latest_delivery = order.requested_delivery_date
                
                # Aggregate product demands
                for item in order.items:
                    product_id = str(item.product_id)
                    if product_id not in product_demands:
                        product_demands[product_id] = {
                            "product_id": product_id,
                            "product_name": item.product_name,
                            "total_quantity": Decimal("0"),
                            "unit_of_measure": item.unit_of_measure,
                            "orders_count": 0,
                            "rush_quantity": Decimal("0"),
                            "special_requirements": []
                        }
                    
                    product_demands[product_id]["total_quantity"] += item.quantity
                    product_demands[product_id]["orders_count"] += 1
                    total_quantity += item.quantity
                    
                    if order.order_type == "rush":
                        product_demands[product_id]["rush_quantity"] += item.quantity
                    
                    if item.special_instructions:
                        product_demands[product_id]["special_requirements"].append(
                            item.special_instructions
                        )
            
            # Calculate average lead time
            average_lead_time_hours = 24  # Default
            if earliest_delivery and latest_delivery:
                time_diff = latest_delivery - earliest_delivery
                average_lead_time_hours = max(24, int(time_diff.total_seconds() / 3600))
            
            # Detect business model
            business_model = await self.detect_business_model(db, tenant_id)
            
            return DemandRequirements(
                date=target_date,
                tenant_id=tenant_id,
                product_demands=list(product_demands.values()),
                total_orders=total_orders,
                total_quantity=total_quantity,
                total_value=total_value,
                business_model=business_model,
                rush_orders_count=rush_orders_count,
                special_requirements=list(set(special_requirements)),
                earliest_delivery=earliest_delivery or datetime.combine(target_date, datetime.min.time()),
                latest_delivery=latest_delivery or datetime.combine(target_date, datetime.max.time()),
                average_lead_time_hours=average_lead_time_hours
            )
            
        except Exception as e:
            logger.error("Error calculating demand requirements", 
                        tenant_id=str(tenant_id), 
                        error=str(e))
            raise
    
    async def get_dashboard_summary(
        self, 
        db, 
        tenant_id: UUID
    ) -> OrdersDashboardSummary:
        """Get dashboard summary data"""
        try:
            # Get basic metrics
            metrics = await self.order_repo.get_dashboard_metrics(db, tenant_id)
            
            # Get customer counts
            total_customers = await self.customer_repo.count(
                db, tenant_id, filters={"is_active": True}
            )
            
            # Get new customers this month
            month_start = datetime.now().replace(day=1, hour=0, minute=0, second=0, microsecond=0)
            new_customers_this_month = await self.customer_repo.count_created_since(
                db,
                tenant_id,
                month_start
            )
            
            # Get recent orders
            recent_orders = await self.order_repo.get_multi(
                db, tenant_id, limit=5, order_by="order_date", order_desc=True
            )
            
            # Get high priority orders
            high_priority_orders = await self.order_repo.get_multi(
                db, 
                tenant_id, 
                filters={"priority": "high", "status": ["pending", "confirmed", "in_production"]},
                limit=10
            )
            
            # Detect business model
            business_model = await self.detect_business_model(db, tenant_id)
            
            # Calculate performance metrics from actual data
            fulfillment_rate = metrics.get("fulfillment_rate", Decimal("0.0"))  # Use actual calculated rate
            on_time_delivery_rate = metrics.get("on_time_delivery_rate", Decimal("0.0"))  # Use actual calculated rate
            repeat_customers_rate = metrics.get("repeat_customers_rate", Decimal("0.0"))  # Use actual calculated rate
            
            # Use the actual calculated values from the repository
            order_fulfillment_rate = metrics.get("fulfillment_rate", Decimal("0.0"))
            on_time_delivery_rate_metric = metrics.get("on_time_delivery_rate", Decimal("0.0"))
            repeat_customers_rate_metric = metrics.get("repeat_customers_rate", Decimal("0.0"))
            
            return OrdersDashboardSummary(
                total_orders_today=metrics["total_orders_today"],
                total_orders_this_week=metrics["total_orders_this_week"],
                total_orders_this_month=metrics["total_orders_this_month"],
                revenue_today=metrics["revenue_today"],
                revenue_this_week=metrics["revenue_this_week"],
                revenue_this_month=metrics["revenue_this_month"],
                pending_orders=metrics["status_breakdown"].get("pending", 0),
                confirmed_orders=metrics["status_breakdown"].get("confirmed", 0),
                in_production_orders=metrics["status_breakdown"].get("in_production", 0),
                ready_orders=metrics["status_breakdown"].get("ready", 0),
                delivered_orders=metrics["status_breakdown"].get("delivered", 0),
                total_customers=total_customers,
                new_customers_this_month=new_customers_this_month,
                repeat_customers_rate=repeat_customers_rate_metric,
                average_order_value=metrics["average_order_value"],
                order_fulfillment_rate=order_fulfillment_rate,
                on_time_delivery_rate=on_time_delivery_rate_metric,
                business_model=business_model,
                business_model_confidence=Decimal("85.0") if business_model else None,
                recent_orders=[OrderResponse.from_orm(order) for order in recent_orders],
                high_priority_orders=[OrderResponse.from_orm(order) for order in high_priority_orders]
            )
            
        except Exception as e:
            logger.error("Error getting dashboard summary", error=str(e))
            raise
    
    async def detect_business_model(
        self, 
        db, 
        tenant_id: UUID
    ) -> Optional[str]:
        """Detect business model based on order patterns"""
        try:
            if not settings.ENABLE_BUSINESS_MODEL_DETECTION:
                return None
            
            return await self.order_repo.detect_business_model(db, tenant_id)
        except Exception as e:
            logger.error("Error detecting business model", error=str(e))
            return None
    
    # ===== Private Helper Methods =====
    
    async def _generate_order_number(self, db, tenant_id: UUID) -> str:
        """Generate unique order number"""
        try:
            # Simple format: ORD-YYYYMMDD-XXXX
            today = datetime.now()
            date_part = today.strftime("%Y%m%d")
            
            # Get count of orders today for this tenant
            today_start = today.replace(hour=0, minute=0, second=0, microsecond=0)
            today_end = today.replace(hour=23, minute=59, second=59, microsecond=999999)
            
            count = await self.order_repo.count(
                db, 
                tenant_id, 
                filters={
                    "order_date": {"gte": today_start, "lte": today_end}
                }
            )
            
            sequence = count + 1
            return f"ORD-{date_part}-{sequence:04d}"
            
        except Exception as e:
            logger.error("Error generating order number", error=str(e))
            # Fallback to UUID
            return f"ORD-{uuid.uuid4().hex[:8].upper()}"
    
    
    async def _notify_production_service(self, order):
        """Notify production service of new order"""
        try:
            if self.production_client:
                await self.production_client.notify_new_order(
                    str(order.tenant_id),
                    {
                        "order_id": str(order.id),
                        "order_number": order.order_number,
                        "delivery_date": order.requested_delivery_date.isoformat(),
                        "priority": order.priority,
                        "items": [
                            {
                                "product_id": str(item.product_id),
                                "quantity": float(item.quantity),
                                "unit_of_measure": item.unit_of_measure
                            }
                            for item in order.items
                        ]
                    }
                )
        except Exception as e:
            logger.warning("Failed to notify production service", 
                          order_id=str(order.id), 
                          error=str(e))
    
    async def _send_status_notification(self, order, old_status: str, new_status: str):
        """Send customer notification for status change"""
        try:
            if self.notification_client and order.customer:
                message = f"Order {order.order_number} status changed from {old_status} to {new_status}"
                await self.notification_client.send_notification(
                    tenant_id=str(order.tenant_id),
                    notification_type="email",
                    message=message,
                    recipient_email=order.customer.email,
                    subject=f"Order {order.order_number} Status Update",
                    priority="normal",
                    metadata={
                        "order_id": str(order.id),
                        "order_number": order.order_number,
                        "old_status": old_status,
                        "new_status": new_status
                    }
                )
        except Exception as e:
            logger.warning("Failed to send status notification",
                          order_id=str(order.id),
                          error=str(e))
