"""
Internal Transfer Service for managing internal purchase orders between parent and child tenants
"""

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

from app.models.purchase_order import PurchaseOrder, PurchaseOrderItem, PurchaseOrderStatus
from app.repositories.purchase_order_repository import PurchaseOrderRepository
from shared.clients.recipes_client import RecipesServiceClient
from shared.clients.production_client import ProductionServiceClient
from shared.clients.inventory_client import InventoryServiceClient

logger = logging.getLogger(__name__)


class InternalTransferService:
    """
    Service for managing internal transfer workflow between parent and child tenants
    """

    def __init__(
        self,
        purchase_order_repository: PurchaseOrderRepository,
        recipe_client: RecipesServiceClient,
        production_client: ProductionServiceClient,
        inventory_client: InventoryServiceClient
    ):
        self.purchase_order_repository = purchase_order_repository
        self.recipe_client = recipe_client
        self.production_client = production_client
        self.inventory_client = inventory_client

    async def create_internal_purchase_order(
        self,
        child_tenant_id: str,
        parent_tenant_id: str,
        items: List[Dict[str, Any]],
        delivery_date: date,
        requested_by_user_id: str,
        notes: Optional[str] = None
    ) -> Dict[str, Any]:
        """
        Create an internal purchase order from child tenant to parent tenant
        
        Args:
            child_tenant_id: Child tenant ID (requesting/destination)
            parent_tenant_id: Parent tenant ID (fulfilling/supplier)
            items: List of items with product_id, quantity, unit_of_measure
            delivery_date: When child needs delivery
            requested_by_user_id: User ID creating the request
            notes: Optional notes for the transfer
            
        Returns:
            Dict with created purchase order details
        """
        try:
            logger.info(f"Creating internal PO from child {child_tenant_id} to parent {parent_tenant_id}")
            
            # Calculate transfer pricing for each item
            priced_items = []
            subtotal = Decimal("0.00")
            
            for item in items:
                product_id = item['product_id']
                quantity = item['quantity']
                unit_of_measure = item.get('unit_of_measure', 'units')
                
                # Calculate transfer price using cost-based pricing
                unit_cost = await self._calculate_transfer_pricing(
                    parent_tenant_id=parent_tenant_id,
                    product_id=product_id
                )
                
                line_total = unit_cost * Decimal(str(quantity))
                
                priced_items.append({
                    'product_id': product_id,
                    'product_name': item.get('product_name', f'Product {product_id}'),  # Would fetch from inventory
                    'quantity': quantity,
                    'unit_of_measure': unit_of_measure,
                    'unit_price': unit_cost,
                    'line_total': line_total
                })
                
                subtotal += line_total
            
            # Create purchase order
            po_data = {
                'tenant_id': child_tenant_id,  # The requesting tenant
                'supplier_id': parent_tenant_id,  # The parent tenant acts as supplier
                'po_number': f"INT-{datetime.now().strftime('%Y%m%d')}-{str(uuid.uuid4())[:8].upper()}",
                'status': PurchaseOrderStatus.draft,
                'priority': 'normal',
                'order_date': datetime.now(),
                'required_delivery_date': datetime.combine(delivery_date, datetime.min.time()),
                'subtotal': subtotal,
                'tax_amount': Decimal("0.00"),  # No tax for internal transfers
                'shipping_cost': Decimal("0.00"),  # Included in transfer price
                'discount_amount': Decimal("0.00"),
                'total_amount': subtotal,
                'currency': 'EUR',
                'notes': notes,
                'created_by': requested_by_user_id,
                'updated_by': requested_by_user_id,
                
                # Internal transfer specific fields
                'is_internal': True,
                'source_tenant_id': parent_tenant_id,
                'destination_tenant_id': child_tenant_id,
                'transfer_type': item.get('transfer_type', 'finished_goods')  # Default to finished goods
            }
            
            # Create the purchase order
            purchase_order = await self.purchase_order_repository.create_purchase_order(po_data)
            
            # Create purchase order items
            for item_data in priced_items:
                po_item_data = {
                    'tenant_id': child_tenant_id,
                    'purchase_order_id': purchase_order['id'],
                    'inventory_product_id': item_data['product_id'],
                    'product_name': item_data['product_name'],
                    'ordered_quantity': item_data['quantity'],
                    'unit_of_measure': item_data['unit_of_measure'],
                    'unit_price': item_data['unit_price'],
                    'line_total': item_data['line_total'],
                    'received_quantity': 0  # Not received yet
                }
                
                await self.purchase_order_repository.create_purchase_order_item(po_item_data)
            
            # Fetch the complete PO with items
            complete_po = await self.purchase_order_repository.get_purchase_order_by_id(purchase_order['id'])
            
            logger.info(f"Created internal PO {complete_po['po_number']} from {child_tenant_id} to {parent_tenant_id}")
            
            # Publish internal_transfer.created event
            await self._publish_internal_transfer_event(
                event_type='internal_transfer.created',
                transfer_data={
                    'po_id': complete_po['id'],
                    'child_tenant_id': child_tenant_id,
                    'parent_tenant_id': parent_tenant_id,
                    'delivery_date': delivery_date.isoformat()
                }
            )
            
            return complete_po
            
        except Exception as e:
            logger.error(f"Error creating internal purchase order: {e}", exc_info=True)
            raise

    async def _calculate_transfer_pricing(
        self,
        parent_tenant_id: str,
        product_id: str
    ) -> Decimal:
        """
        Calculate transfer price using cost-based pricing
        
        Args:
            parent_tenant_id: Parent tenant ID
            product_id: Product ID to price
            
        Returns:
            Decimal with unit cost for transfer
        """
        try:
            # Check if product is produced locally by parent
            is_locally_produced = await self._check_if_locally_produced(parent_tenant_id, product_id)
            
            if is_locally_produced:
                # Fetch recipe for the product
                recipe = await self.recipe_client.get_recipe_by_id(parent_tenant_id, product_id)
                
                if recipe:
                    # Calculate raw material cost
                    raw_material_cost = await self._calculate_raw_material_cost(
                        parent_tenant_id, 
                        recipe
                    )
                    
                    # Fetch production cost per unit
                    production_cost = await self._get_production_cost_per_unit(
                        parent_tenant_id,
                        product_id
                    )
                    
                    # Unit cost = raw material cost + production cost
                    unit_cost = raw_material_cost + production_cost
                else:
                    # Fallback to average cost from inventory
                    unit_cost = await self._get_average_cost_from_inventory(
                        parent_tenant_id, 
                        product_id
                    )
            else:
                # Not produced locally, use average cost from inventory
                unit_cost = await self._get_average_cost_from_inventory(
                    parent_tenant_id, 
                    product_id
                )
            
            # Apply optional markup (default 0%, configurable in tenant settings)
            markup_percentage = await self._get_transfer_markup_percentage(parent_tenant_id)
            markup_amount = unit_cost * Decimal(str(markup_percentage / 100))
            final_unit_price = unit_cost + markup_amount
            
            return final_unit_price
            
        except Exception as e:
            logger.error(f"Error calculating transfer pricing for product {product_id}: {e}", exc_info=True)
            # Fallback to average cost
            return await self._get_average_cost_from_inventory(parent_tenant_id, product_id)

    async def _check_if_locally_produced(self, tenant_id: str, product_id: str) -> bool:
        """
        Check if a product is locally produced by the tenant
        """
        try:
            # This would check the recipes service to see if the tenant has a recipe for this product
            # In a real implementation, this would call the recipes service
            recipe = await self.recipe_client.get_recipe_by_id(tenant_id, product_id)
            return recipe is not None
        except Exception:
            logger.warning(f"Could not verify if product {product_id} is locally produced by tenant {tenant_id}")
            return False

    async def _calculate_raw_material_cost(self, tenant_id: str, recipe: Dict[str, Any]) -> Decimal:
        """
        Calculate total raw material cost based on recipe
        """
        total_cost = Decimal("0.00")
        
        try:
            for ingredient in recipe.get('ingredients', []):
                ingredient_id = ingredient['ingredient_id']
                required_quantity = Decimal(str(ingredient.get('quantity', 0)))
                
                # Get cost of this ingredient
                ingredient_cost = await self._get_average_cost_from_inventory(
                    tenant_id,
                    ingredient_id
                )
                
                ingredient_total_cost = ingredient_cost * required_quantity
                total_cost += ingredient_total_cost
                
        except Exception as e:
            logger.error(f"Error calculating raw material cost: {e}", exc_info=True)
            # Return 0 to avoid blocking the process
            return Decimal("0.00")
        
        return total_cost

    async def _get_production_cost_per_unit(self, tenant_id: str, product_id: str) -> Decimal:
        """
        Get the production cost per unit for a specific product
        """
        try:
            # In a real implementation, this would call the production service
            # to get actual production costs
            # For now, return a placeholder value
            return Decimal("0.50")  # Placeholder: EUR 0.50 per unit production cost
        except Exception as e:
            logger.error(f"Error getting production cost for product {product_id}: {e}", exc_info=True)
            return Decimal("0.00")

    async def _get_average_cost_from_inventory(self, tenant_id: str, product_id: str) -> Decimal:
        """
        Get average cost for a product from inventory
        """
        try:
            # This would call the inventory service to get average cost
            # For now, return a placeholder
            return Decimal("2.00")  # Placeholder: EUR 2.00 average cost
        except Exception as e:
            logger.error(f"Error getting average cost for product {product_id}: {e}", exc_info=True)
            return Decimal("1.00")

    async def _get_transfer_markup_percentage(self, tenant_id: str) -> float:
        """
        Get transfer markup percentage from tenant settings
        """
        try:
            # This would fetch tenant-specific settings
            # For now, default to 0% markup
            return 0.0
        except Exception as e:
            logger.error(f"Error getting transfer markup for tenant {tenant_id}: {e}")
            return 0.0

    async def approve_internal_transfer(self, po_id: str, approved_by_user_id: str) -> Dict[str, Any]:
        """
        Approve an internal transfer request
        """
        try:
            # Get the purchase order
            po = await self.purchase_order_repository.get_purchase_order_by_id(po_id)
            if not po:
                raise ValueError(f"Purchase order {po_id} not found")
            
            if not po.get('is_internal'):
                raise ValueError("Cannot approve non-internal purchase order as internal transfer")
            
            # Update status to approved
            approved_po = await self.purchase_order_repository.update_purchase_order_status(
                po_id=po_id,
                status=PurchaseOrderStatus.approved,
                updated_by=approved_by_user_id
            )
            
            logger.info(f"Approved internal transfer PO {po_id} by user {approved_by_user_id}")
            
            # Publish internal_transfer.approved event
            await self._publish_internal_transfer_event(
                event_type='internal_transfer.approved',
                transfer_data={
                    'po_id': po_id,
                    'child_tenant_id': po.get('tenant_id'),
                    'parent_tenant_id': po.get('source_tenant_id'),
                    'approved_by': approved_by_user_id
                }
            )
            
            return approved_po
            
        except Exception as e:
            logger.error(f"Error approving internal transfer: {e}", exc_info=True)
            raise

    async def _publish_internal_transfer_event(self, event_type: str, transfer_data: Dict[str, Any]):
        """
        Publish internal transfer event to message queue
        """
        # In a real implementation, this would publish to RabbitMQ
        logger.info(f"Internal transfer event published: {event_type} - {transfer_data}")

    async def get_pending_internal_transfers(self, tenant_id: str) -> List[Dict[str, Any]]:
        """
        Get all pending internal transfers for a tenant (as parent supplier or child requester)
        """
        try:
            pending_pos = await self.purchase_order_repository.get_purchase_orders_by_tenant_and_status(
                tenant_id=tenant_id,
                status=PurchaseOrderStatus.draft,
                is_internal=True
            )
            
            # Filter based on whether this tenant is parent or child
            parent_pos = []
            child_pos = []
            
            for po in pending_pos:
                if po.get('source_tenant_id') == tenant_id:
                    # This tenant is the supplier (parent) - needs to approve
                    parent_pos.append(po)
                elif po.get('destination_tenant_id') == tenant_id:
                    # This tenant is the requester (child) - tracking status
                    child_pos.append(po)
            
            return {
                'pending_approval_as_parent': parent_pos,
                'pending_status_as_child': child_pos
            }
            
        except Exception as e:
            logger.error(f"Error getting pending internal transfers: {e}", exc_info=True)
            raise

    async def get_internal_transfer_history(
        self,
        tenant_id: str,
        parent_tenant_id: Optional[str] = None,
        child_tenant_id: Optional[str] = None,
        start_date: Optional[date] = None,
        end_date: Optional[date] = None
    ) -> List[Dict[str, Any]]:
        """
        Get internal transfer history with filtering options
        """
        try:
            # Build filters
            filters = {'is_internal': True}
            
            if parent_tenant_id:
                filters['source_tenant_id'] = parent_tenant_id
            if child_tenant_id:
                filters['destination_tenant_id'] = child_tenant_id
            if start_date:
                filters['start_date'] = start_date
            if end_date:
                filters['end_date'] = end_date
            
            history = await self.purchase_order_repository.get_purchase_orders_by_tenant_and_filters(
                tenant_id=tenant_id,
                filters=filters
            )
            
            return history
            
        except Exception as e:
            logger.error(f"Error getting internal transfer history: {e}", exc_info=True)
            raise