"""
Enterprise Inventory Service
Business logic for enterprise-level inventory management across outlets
"""

from typing import List, Dict, Any, Optional
from datetime import datetime, timedelta
import uuid
import structlog

logger = structlog.get_logger()


class EnterpriseInventoryService:
    """
    Service for managing inventory across enterprise networks
    """
    
    def __init__(self, inventory_client, tenant_client):
        self.inventory_client = inventory_client
        self.tenant_client = tenant_client
    
    async def get_child_outlets(self, parent_id: str) -> List[Dict[str, Any]]:
        """
        Get all child outlets for a parent tenant
        """
        try:
            # Get child tenants from tenant service
            children = await self.tenant_client.get_child_tenants(parent_id)
            
            # Enrich with location data
            enriched_outlets = []
            for child in children:
                # Get location data for this outlet
                locations = await self.tenant_client.get_tenant_locations(child['id'])
                
                outlet_info = {
                    'id': child['id'],
                    'name': child['name'],
                    'subdomain': child.get('subdomain'),
                    'location': locations[0] if locations else None
                }
                enriched_outlets.append(outlet_info)
            
            return enriched_outlets
            
        except Exception as e:
            logger.error("Failed to get child outlets", parent_id=parent_id, error=str(e))
            raise Exception(f"Failed to get child outlets: {str(e)}")
    
    async def get_inventory_coverage(self, outlet_id: str) -> Dict[str, Any]:
        """
        Get inventory coverage metrics for a specific outlet
        """
        try:
            # Get current inventory data
            inventory_data = await self.inventory_client.get_current_inventory(outlet_id)
            
            if not inventory_data or not inventory_data.get('items'):
                return None
            
            # Calculate coverage metrics
            total_items = len(inventory_data['items'])
            critical_count = 0
            high_risk_count = 0
            medium_risk_count = 0
            low_risk_count = 0
            total_coverage = 0
            
            for item in inventory_data['items']:
                current_stock = item.get('current_stock', 0)
                safety_stock = item.get('safety_stock', 1)  # Avoid division by zero
                
                if safety_stock <= 0:
                    safety_stock = 1
                
                coverage = min(100, (current_stock / safety_stock) * 100)
                total_coverage += coverage
                
                # Determine risk level
                if coverage < 30:
                    critical_count += 1
                elif coverage < 50:
                    high_risk_count += 1
                elif coverage < 70:
                    medium_risk_count += 1
                else:
                    low_risk_count += 1
            
            # Calculate average coverage
            avg_coverage = total_coverage / total_items if total_items > 0 else 0
            
            # Get fulfillment rate (simplified - in real implementation this would come from orders service)
            fulfillment_rate = await self._calculate_fulfillment_rate(outlet_id)
            
            # Determine overall status
            status = self._determine_inventory_status(critical_count, high_risk_count, avg_coverage)
            
            return {
                'outlet_id': outlet_id,
                'outlet_name': inventory_data.get('tenant_name', f'Outlet {outlet_id}'),
                'overall_coverage': round(avg_coverage, 1),
                'critical_items_count': critical_count,
                'high_risk_items_count': high_risk_count,
                'medium_risk_items_count': medium_risk_count,
                'low_risk_items_count': low_risk_count,
                'fulfillment_rate': round(fulfillment_rate, 1),
                'last_updated': datetime.now().isoformat(),
                'status': status
            }
            
        except Exception as e:
            logger.error("Failed to get inventory coverage", outlet_id=outlet_id, error=str(e))
            raise Exception(f"Failed to get inventory coverage: {str(e)}")
    
    async def _calculate_fulfillment_rate(self, outlet_id: str) -> float:
        """
        Calculate fulfillment rate for an outlet (simplified)
        In a real implementation, this would query the orders service
        """
        # This is a placeholder - real implementation would:
        # 1. Get recent orders from orders service
        # 2. Calculate % successfully fulfilled
        # 3. Return the rate
        
        # For demo purposes, return a reasonable default
        return 95.0
    
    def _determine_inventory_status(self, critical_count: int, high_risk_count: int, avg_coverage: float) -> str:
        """
        Determine overall inventory status based on risk factors
        """
        if critical_count > 5 or (critical_count > 0 and avg_coverage < 40):
            return 'critical'
        elif high_risk_count > 3 or (high_risk_count > 0 and avg_coverage < 60):
            return 'warning'
        else:
            return 'normal'
    
    async def get_network_inventory_summary(self, parent_id: str) -> Dict[str, Any]:
        """
        Get aggregated inventory summary across the entire network
        """
        try:
            # Get all child outlets
            child_outlets = await self.get_child_outlets(parent_id)
            
            if not child_outlets:
                return {
                    'total_outlets': 0,
                    'average_coverage': 0,
                    'average_fulfillment_rate': 0,
                    'critical_outlets': 0,
                    'warning_outlets': 0,
                    'normal_outlets': 0,
                    'total_critical_items': 0,
                    'network_health_score': 0
                }
            
            # Get coverage for each outlet
            coverage_data = []
            for outlet in child_outlets:
                coverage = await self.get_inventory_coverage(outlet['id'])
                if coverage:
                    coverage_data.append(coverage)
            
            if not coverage_data:
                return {
                    'total_outlets': len(child_outlets),
                    'average_coverage': 0,
                    'average_fulfillment_rate': 0,
                    'critical_outlets': 0,
                    'warning_outlets': 0,
                    'normal_outlets': len(child_outlets),
                    'total_critical_items': 0,
                    'network_health_score': 0
                }
            
            # Calculate network metrics
            total_coverage = sum(c['overall_coverage'] for c in coverage_data)
            total_fulfillment = sum(c['fulfillment_rate'] for c in coverage_data)
            
            avg_coverage = total_coverage / len(coverage_data)
            avg_fulfillment = total_fulfillment / len(coverage_data)
            
            critical_outlets = sum(1 for c in coverage_data if c['status'] == 'critical')
            warning_outlets = sum(1 for c in coverage_data if c['status'] == 'warning')
            normal_outlets = sum(1 for c in coverage_data if c['status'] == 'normal')
            
            total_critical_items = sum(c['critical_items_count'] for c in coverage_data)
            
            # Calculate network health score (weighted average)
            network_health = round(avg_coverage * 0.6 + avg_fulfillment * 0.4, 1)
            
            return {
                'total_outlets': len(child_outlets),
                'average_coverage': round(avg_coverage, 1),
                'average_fulfillment_rate': round(avg_fulfillment, 1),
                'critical_outlets': critical_outlets,
                'warning_outlets': warning_outlets,
                'normal_outlets': normal_outlets,
                'total_critical_items': total_critical_items,
                'network_health_score': network_health
            }
            
        except Exception as e:
            logger.error("Failed to get network inventory summary", parent_id=parent_id, error=str(e))
            raise Exception(f"Failed to get network inventory summary: {str(e)}")
    
    async def get_outlet_inventory_details(self, outlet_id: str, product_id: Optional[str] = None, risk_level: Optional[str] = None) -> Dict[str, Any]:
        """
        Get detailed product-level inventory data for a specific outlet
        """
        try:
            # Get current inventory data
            inventory_data = await self.inventory_client.get_current_inventory(outlet_id)
            
            if not inventory_data or not inventory_data.get('items'):
                return {
                    'outlet_id': outlet_id,
                    'outlet_name': inventory_data.get('tenant_name', f'Outlet {outlet_id}'),
                    'overall_coverage': 0,
                    'products': [],
                    'last_updated': datetime.now().isoformat()
                }
            
            # Process product details
            products = []
            total_coverage = 0
            
            for item in inventory_data['items']:
                # Filter by product_id if specified
                if product_id and item.get('product_id') != product_id:
                    continue
                
                current_stock = item.get('current_stock', 0)
                safety_stock = item.get('safety_stock', 1)
                
                if safety_stock <= 0:
                    safety_stock = 1
                
                coverage = min(100, (current_stock / safety_stock) * 100)
                total_coverage += coverage
                
                # Determine risk level
                if coverage < 30:
                    risk = 'critical'
                elif coverage < 50:
                    risk = 'high'
                elif coverage < 70:
                    risk = 'medium'
                else:
                    risk = 'low'
                
                # Filter by risk level if specified
                if risk_level and risk != risk_level:
                    continue
                
                # Calculate days until stockout (simplified)
                daily_usage = item.get('average_daily_usage', 1)
                days_until_stockout = None
                
                if daily_usage > 0:
                    days_until_stockout = max(0, int((current_stock - safety_stock) / daily_usage))
                    if days_until_stockout < 0:
                        days_until_stockout = 0
                
                product_detail = {
                    'product_id': item.get('product_id'),
                    'product_name': item.get('product_name', 'Unknown Product'),
                    'current_stock': current_stock,
                    'safety_stock': safety_stock,
                    'coverage_percentage': round(coverage, 1),
                    'risk_level': risk,
                    'days_until_stockout': days_until_stockout
                }
                
                products.append(product_detail)
            
            # Calculate overall coverage
            avg_coverage = total_coverage / len(inventory_data['items']) if inventory_data['items'] else 0
            
            return {
                'outlet_id': outlet_id,
                'outlet_name': inventory_data.get('tenant_name', f'Outlet {outlet_id}'),
                'overall_coverage': round(avg_coverage, 1),
                'products': products,
                'last_updated': datetime.now().isoformat()
            }
            
        except Exception as e:
            logger.error("Failed to get outlet inventory details", outlet_id=outlet_id, error=str(e))
            raise Exception(f"Failed to get outlet inventory details: {str(e)}")
    
    async def get_inventory_alerts(self, parent_id: str) -> List[Dict[str, Any]]:
        """
        Get real-time inventory alerts across all outlets
        """
        try:
            # Get all child outlets
            child_outlets = await self.get_child_outlets(parent_id)
            
            alerts = []
            
            for outlet in child_outlets:
                outlet_id = outlet['id']
                outlet_name = outlet['name']
                
                # Get inventory coverage for this outlet
                coverage = await self.get_inventory_coverage(outlet_id)
                
                if coverage:
                    # Create alerts for critical items
                    if coverage['critical_items_count'] > 0:
                        alerts.append({
                            'alert_id': str(uuid.uuid4()),
                            'outlet_id': outlet_id,
                            'outlet_name': outlet_name,
                            'product_id': None,
                            'product_name': None,
                            'alert_type': 'low_coverage',
                            'severity': 'critical',
                            'current_coverage': coverage['overall_coverage'],
                            'threshold': 30,
                            'timestamp': datetime.now().isoformat(),
                            'message': f"Critical inventory coverage: {coverage['overall_coverage']}% (threshold: 30%)"
                        })
                    
                    # Create alerts for high risk items
                    if coverage['high_risk_items_count'] > 0:
                        alerts.append({
                            'alert_id': str(uuid.uuid4()),
                            'outlet_id': outlet_id,
                            'outlet_name': outlet_name,
                            'product_id': None,
                            'product_name': None,
                            'alert_type': 'stockout_risk',
                            'severity': 'high',
                            'current_coverage': coverage['overall_coverage'],
                            'threshold': 50,
                            'timestamp': datetime.now().isoformat(),
                            'message': f"High stockout risk: {coverage['overall_coverage']}% coverage"
                        })
            
            return alerts
            
        except Exception as e:
            logger.error("Failed to get inventory alerts", parent_id=parent_id, error=str(e))
            raise Exception(f"Failed to get inventory alerts: {str(e)}")
    
    async def get_transfer_recommendations(self, parent_id: str, urgency: str = "medium") -> List[Dict[str, Any]]:
        """
        Get AI-powered inventory transfer recommendations
        """
        try:
            # Get inventory coverage for all outlets
            child_outlets = await self.get_child_outlets(parent_id)
            coverage_data = []
            
            for outlet in child_outlets:
                coverage = await self.get_inventory_coverage(outlet['id'])
                if coverage:
                    coverage_data.append(coverage)
            
            # Simple recommendation algorithm (in real implementation, this would be more sophisticated)
            recommendations = []
            
            # Find outlets with surplus and deficit
            surplus_outlets = [c for c in coverage_data if c['overall_coverage'] > 85]
            deficit_outlets = [c for c in coverage_data if c['overall_coverage'] < 60]
            
            # Generate transfer recommendations
            for deficit in deficit_outlets:
                for surplus in surplus_outlets:
                    # Calculate transfer amount (simplified)
                    transfer_amount = min(10, (deficit['overall_coverage'] - 60) * -2)  # Transfer 2% per missing %
                    
                    if transfer_amount > 0:
                        recommendations.append({
                            'recommendation_id': str(uuid.uuid4()),
                            'from_outlet_id': surplus['outlet_id'],
                            'from_outlet_name': surplus['outlet_name'],
                            'to_outlet_id': deficit['outlet_id'],
                            'to_outlet_name': deficit['outlet_name'],
                            'transfer_amount': transfer_amount,
                            'priority': self._calculate_priority(deficit, urgency),
                            'reason': f"Balance inventory: {surplus['outlet_name']} has {surplus['overall_coverage']}% coverage, {deficit['outlet_name']} has {deficit['overall_coverage']}% coverage",
                            'estimated_impact': f"Improve {deficit['outlet_name']} coverage by ~{transfer_amount}%"
                        })
            
            # Sort by priority
            recommendations.sort(key=lambda x: x['priority'], reverse=True)
            
            return recommendations
            
        except Exception as e:
            logger.error("Failed to get transfer recommendations", parent_id=parent_id, error=str(e))
            raise Exception(f"Failed to get transfer recommendations: {str(e)}")
    
    def _calculate_priority(self, deficit_coverage: Dict[str, Any], urgency: str) -> int:
        """
        Calculate priority score for transfer recommendation
        """
        priority_scores = {
            'critical': 4,
            'high': 3,
            'medium': 2,
            'low': 1
        }
        
        urgency_score = priority_scores.get(urgency, 2)
        
        # Higher priority for lower coverage
        coverage_score = max(1, 5 - int(deficit_coverage['overall_coverage'] / 20))
        
        return urgency_score * coverage_score
    
    async def get_coverage_trends(self, parent_id: str, days: int = 30) -> List[Dict[str, Any]]:
        """
        Get historical inventory coverage trends
        """
        try:
            # In a real implementation, this would query historical data
            # For demo purposes, generate some sample trend data
            
            trends = []
            end_date = datetime.now()
            
            for i in range(days):
                date = end_date - timedelta(days=i)
                
                # Generate sample data with some variation
                base_coverage = 75
                variation = (i % 7) - 3  # Weekly pattern
                daily_variation = (i % 3) - 1  # Daily noise
                
                coverage = max(50, min(95, base_coverage + variation + daily_variation))
                
                trends.append({
                    'date': date.strftime('%Y-%m-%d'),
                    'average_coverage': round(coverage, 1),
                    'min_coverage': max(40, coverage - 15),
                    'max_coverage': min(95, coverage + 10)
                })
            
            # Sort by date (oldest first)
            trends.sort(key=lambda x: x['date'])
            
            return trends
            
        except Exception as e:
            logger.error("Failed to get coverage trends", parent_id=parent_id, error=str(e))
            raise Exception(f"Failed to get coverage trends: {str(e)}")
    
    async def verify_parent_child_relationship(self, parent_id: str, child_id: str) -> bool:
        """
        Verify that a child tenant belongs to a parent tenant
        """
        try:
            # Get child tenant info
            child_info = await self.tenant_client.get_tenant(child_id)
            
            if child_info.get('parent_tenant_id') != parent_id:
                raise HTTPException(
                    status_code=403,
                    detail="Child tenant does not belong to specified parent"
                )
            
            return True
            
        except Exception as e:
            logger.error("Failed to verify parent-child relationship", parent_id=parent_id, child_id=child_id, error=str(e))
            raise Exception(f"Failed to verify relationship: {str(e)}")
