# services/inventory/app/repositories/transformation_repository.py
"""
Product Transformation Repository using Repository Pattern
"""

from typing import List, Optional, Dict, Any
from uuid import UUID
from datetime import datetime, timedelta
from sqlalchemy import select, func, and_, or_, desc, asc
from sqlalchemy.ext.asyncio import AsyncSession
import structlog
import json
import uuid

from app.models.inventory import ProductTransformation, Ingredient, ProductionStage
from app.schemas.inventory import ProductTransformationCreate
from shared.database.repository import BaseRepository

logger = structlog.get_logger()


class TransformationRepository(BaseRepository[ProductTransformation, ProductTransformationCreate, dict]):
    """Repository for product transformation operations"""

    def __init__(self, session: AsyncSession):
        super().__init__(ProductTransformation, session)

    async def create_transformation(
        self,
        transformation_data: ProductTransformationCreate,
        tenant_id: UUID,
        created_by: Optional[UUID] = None,
        source_batch_numbers: Optional[List[str]] = None
    ) -> ProductTransformation:
        """Create a new product transformation record"""
        try:
            # Generate transformation reference
            transformation_ref = f"TRANS-{datetime.now().strftime('%Y%m%d')}-{str(uuid.uuid4())[:8].upper()}"

            # Prepare data
            create_data = transformation_data.model_dump()
            create_data['tenant_id'] = tenant_id
            create_data['created_by'] = created_by
            create_data['transformation_reference'] = transformation_ref

            # Calculate conversion ratio if not provided
            if not create_data.get('conversion_ratio'):
                create_data['conversion_ratio'] = create_data['target_quantity'] / create_data['source_quantity']

            # Store source batch numbers as JSON
            if source_batch_numbers:
                create_data['source_batch_numbers'] = json.dumps(source_batch_numbers)

            # Create record
            record = await self.create(create_data)
            logger.info(
                "Created product transformation",
                transformation_id=record.id,
                reference=record.transformation_reference,
                source_stage=record.source_stage.value,
                target_stage=record.target_stage.value,
                source_quantity=record.source_quantity,
                target_quantity=record.target_quantity,
                tenant_id=tenant_id
            )
            return record

        except Exception as e:
            logger.error("Failed to create transformation", error=str(e), tenant_id=tenant_id)
            raise

    async def get_transformations_by_ingredient(
        self,
        tenant_id: UUID,
        ingredient_id: UUID,
        is_source: bool = True,
        skip: int = 0,
        limit: int = 100,
        days_back: Optional[int] = None
    ) -> List[ProductTransformation]:
        """Get transformations for a specific ingredient"""
        try:
            if is_source:
                query = select(self.model).where(
                    and_(
                        self.model.tenant_id == tenant_id,
                        self.model.source_ingredient_id == ingredient_id
                    )
                )
            else:
                query = select(self.model).where(
                    and_(
                        self.model.tenant_id == tenant_id,
                        self.model.target_ingredient_id == ingredient_id
                    )
                )

            # Filter by date range if specified
            if days_back:
                start_date = datetime.now() - timedelta(days=days_back)
                query = query.where(self.model.transformation_date >= start_date)

            query = query.order_by(desc(self.model.transformation_date)).offset(skip).limit(limit)

            result = await self.session.execute(query)
            return result.scalars().all()

        except Exception as e:
            logger.error("Failed to get transformations by ingredient", error=str(e), ingredient_id=ingredient_id)
            raise

    async def get_transformations_by_stage(
        self,
        tenant_id: UUID,
        source_stage: Optional[ProductionStage] = None,
        target_stage: Optional[ProductionStage] = None,
        skip: int = 0,
        limit: int = 100,
        days_back: Optional[int] = None
    ) -> List[ProductTransformation]:
        """Get transformations by production stage"""
        try:
            conditions = [self.model.tenant_id == tenant_id]

            if source_stage:
                conditions.append(self.model.source_stage == source_stage)
            if target_stage:
                conditions.append(self.model.target_stage == target_stage)

            query = select(self.model).where(and_(*conditions))

            # Filter by date range if specified
            if days_back:
                start_date = datetime.now() - timedelta(days=days_back)
                query = query.where(self.model.transformation_date >= start_date)

            query = query.order_by(desc(self.model.transformation_date)).offset(skip).limit(limit)

            result = await self.session.execute(query)
            return result.scalars().all()

        except Exception as e:
            logger.error("Failed to get transformations by stage", error=str(e))
            raise

    async def get_transformation_by_reference(
        self,
        tenant_id: UUID,
        transformation_reference: str
    ) -> Optional[ProductTransformation]:
        """Get transformation by reference number"""
        try:
            result = await self.session.execute(
                select(self.model).where(
                    and_(
                        self.model.tenant_id == tenant_id,
                        self.model.transformation_reference == transformation_reference
                    )
                )
            )
            return result.scalar_one_or_none()

        except Exception as e:
            logger.error("Failed to get transformation by reference", error=str(e), reference=transformation_reference)
            raise

    async def get_transformation_summary_by_period(
        self,
        tenant_id: UUID,
        days_back: int = 30
    ) -> Dict[str, Any]:
        """Get transformation summary for specified period"""
        try:
            start_date = datetime.now() - timedelta(days=days_back)

            # Get transformation counts by stage combination
            result = await self.session.execute(
                select(
                    self.model.source_stage,
                    self.model.target_stage,
                    func.count(self.model.id).label('count'),
                    func.coalesce(func.sum(self.model.source_quantity), 0).label('total_source_quantity'),
                    func.coalesce(func.sum(self.model.target_quantity), 0).label('total_target_quantity')
                ).where(
                    and_(
                        self.model.tenant_id == tenant_id,
                        self.model.transformation_date >= start_date
                    )
                ).group_by(self.model.source_stage, self.model.target_stage)
            )

            summary = {}
            total_transformations = 0

            for row in result:
                source_stage = row.source_stage.value if row.source_stage else "unknown"
                target_stage = row.target_stage.value if row.target_stage else "unknown"

                stage_key = f"{source_stage}_to_{target_stage}"
                summary[stage_key] = {
                    'count': row.count,
                    'total_source_quantity': float(row.total_source_quantity),
                    'total_target_quantity': float(row.total_target_quantity),
                    'average_conversion_ratio': float(row.total_target_quantity) / float(row.total_source_quantity) if row.total_source_quantity > 0 else 0
                }
                total_transformations += row.count

            summary['total_transformations'] = total_transformations
            summary['period_days'] = days_back

            return summary

        except Exception as e:
            logger.error("Failed to get transformation summary", error=str(e), tenant_id=tenant_id)
            raise

    async def calculate_transformation_efficiency(
        self,
        tenant_id: UUID,
        source_ingredient_id: UUID,
        target_ingredient_id: UUID,
        days_back: int = 30
    ) -> Dict[str, float]:
        """Calculate transformation efficiency between ingredients"""
        try:
            start_date = datetime.now() - timedelta(days=days_back)

            result = await self.session.execute(
                select(
                    func.count(self.model.id).label('transformation_count'),
                    func.coalesce(func.sum(self.model.source_quantity), 0).label('total_source'),
                    func.coalesce(func.sum(self.model.target_quantity), 0).label('total_target'),
                    func.coalesce(func.avg(self.model.conversion_ratio), 0).label('avg_conversion_ratio')
                ).where(
                    and_(
                        self.model.tenant_id == tenant_id,
                        self.model.source_ingredient_id == source_ingredient_id,
                        self.model.target_ingredient_id == target_ingredient_id,
                        self.model.transformation_date >= start_date
                    )
                )
            )

            row = result.first()

            return {
                'transformation_count': row.transformation_count or 0,
                'total_source_quantity': float(row.total_source) if row.total_source else 0.0,
                'total_target_quantity': float(row.total_target) if row.total_target else 0.0,
                'average_conversion_ratio': float(row.avg_conversion_ratio) if row.avg_conversion_ratio else 0.0,
                'efficiency_percentage': (float(row.total_target) / float(row.total_source) * 100) if row.total_source and row.total_source > 0 else 0.0,
                'period_days': days_back
            }

        except Exception as e:
            logger.error("Failed to calculate transformation efficiency", error=str(e))
            raise