"""
Shipment Repository
"""

from typing import List, Dict, Any, Optional
from datetime import date, datetime
import uuid
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.future import select

from app.models.distribution import Shipment, ShipmentStatus
from shared.database.base import Base


class ShipmentRepository:
    def __init__(self, db_session: AsyncSession):
        self.db_session = db_session

    async def create_shipment(self, shipment_data: Dict[str, Any]) -> Dict[str, Any]:
        """
        Create a new shipment
        """
        # Define system user ID to use when user_id is not provided
        SYSTEM_USER_ID = uuid.UUID("50000000-0000-0000-0000-000000000004")

        shipment = Shipment(
            id=uuid.uuid4(),
            tenant_id=shipment_data['tenant_id'],
            parent_tenant_id=shipment_data['parent_tenant_id'],
            child_tenant_id=shipment_data['child_tenant_id'],
            purchase_order_id=shipment_data.get('purchase_order_id'),
            delivery_route_id=shipment_data.get('delivery_route_id'),
            shipment_number=shipment_data['shipment_number'],
            shipment_date=shipment_data['shipment_date'],
            status=shipment_data.get('status', 'pending'),
            total_weight_kg=shipment_data.get('total_weight_kg'),
            total_volume_m3=shipment_data.get('total_volume_m3'),
            created_by=shipment_data.get('created_by', SYSTEM_USER_ID),
            updated_by=shipment_data.get('updated_by', SYSTEM_USER_ID)
        )
        
        self.db_session.add(shipment)
        await self.db_session.commit()
        await self.db_session.refresh(shipment)
        
        # Convert SQLAlchemy object to dict for return
        return {
            'id': str(shipment.id),
            'tenant_id': str(shipment.tenant_id),
            'parent_tenant_id': str(shipment.parent_tenant_id),
            'child_tenant_id': str(shipment.child_tenant_id),
            'purchase_order_id': str(shipment.purchase_order_id) if shipment.purchase_order_id else None,
            'delivery_route_id': str(shipment.delivery_route_id) if shipment.delivery_route_id else None,
            'shipment_number': shipment.shipment_number,
            'shipment_date': shipment.shipment_date,
            'current_location_lat': shipment.current_location_lat,
            'current_location_lng': shipment.current_location_lng,
            'last_tracked_at': shipment.last_tracked_at,
            'status': shipment.status.value if hasattr(shipment.status, 'value') else shipment.status,
            'actual_delivery_time': shipment.actual_delivery_time,
            'signature': shipment.signature,
            'photo_url': shipment.photo_url,
            'received_by_name': shipment.received_by_name,
            'delivery_notes': shipment.delivery_notes,
            'total_weight_kg': shipment.total_weight_kg,
            'total_volume_m3': shipment.total_volume_m3,
            'created_at': shipment.created_at,
            'updated_at': shipment.updated_at
        }

    async def get_shipments_by_date(self, tenant_id: str, target_date: date) -> List[Dict[str, Any]]:
        """
        Get all shipments for a specific date and tenant
        """
        stmt = select(Shipment).where(
            (Shipment.tenant_id == tenant_id) & 
            (Shipment.shipment_date >= datetime.combine(target_date, datetime.min.time())) &
            (Shipment.shipment_date < datetime.combine(target_date, datetime.max.time().replace(hour=23, minute=59, second=59)))
        )
        
        result = await self.db_session.execute(stmt)
        shipments = result.scalars().all()
        
        return [
            {
                'id': str(shipment.id),
                'tenant_id': str(shipment.tenant_id),
                'parent_tenant_id': str(shipment.parent_tenant_id),
                'child_tenant_id': str(shipment.child_tenant_id),
                'purchase_order_id': str(shipment.purchase_order_id) if shipment.purchase_order_id else None,
                'delivery_route_id': str(shipment.delivery_route_id) if shipment.delivery_route_id else None,
                'shipment_number': shipment.shipment_number,
                'shipment_date': shipment.shipment_date,
                'current_location_lat': shipment.current_location_lat,
                'current_location_lng': shipment.current_location_lng,
                'last_tracked_at': shipment.last_tracked_at,
                'status': shipment.status.value if hasattr(shipment.status, 'value') else shipment.status,
                'actual_delivery_time': shipment.actual_delivery_time,
                'signature': shipment.signature,
                'photo_url': shipment.photo_url,
                'received_by_name': shipment.received_by_name,
                'delivery_notes': shipment.delivery_notes,
                'total_weight_kg': shipment.total_weight_kg,
                'total_volume_m3': shipment.total_volume_m3,
                'created_at': shipment.created_at,
                'updated_at': shipment.updated_at
            }
            for shipment in shipments
        ]

    async def get_shipments_by_date_range(self, tenant_id: str, start_date: date, end_date: date) -> List[Dict[str, Any]]:
        """
        Get all shipments for a specific date range and tenant
        """
        stmt = select(Shipment).where(
            (Shipment.tenant_id == tenant_id) & 
            (Shipment.shipment_date >= datetime.combine(start_date, datetime.min.time())) &
            (Shipment.shipment_date <= datetime.combine(end_date, datetime.max.time().replace(hour=23, minute=59, second=59)))
        )
        
        result = await self.db_session.execute(stmt)
        shipments = result.scalars().all()
        
        return [
            {
                'id': str(shipment.id),
                'tenant_id': str(shipment.tenant_id),
                'parent_tenant_id': str(shipment.parent_tenant_id),
                'child_tenant_id': str(shipment.child_tenant_id),
                'purchase_order_id': str(shipment.purchase_order_id) if shipment.purchase_order_id else None,
                'delivery_route_id': str(shipment.delivery_route_id) if shipment.delivery_route_id else None,
                'shipment_number': shipment.shipment_number,
                'shipment_date': shipment.shipment_date,
                'current_location_lat': shipment.current_location_lat,
                'current_location_lng': shipment.current_location_lng,
                'last_tracked_at': shipment.last_tracked_at,
                'status': shipment.status.value if hasattr(shipment.status, 'value') else shipment.status,
                'actual_delivery_time': shipment.actual_delivery_time,
                'signature': shipment.signature,
                'photo_url': shipment.photo_url,
                'received_by_name': shipment.received_by_name,
                'delivery_notes': shipment.delivery_notes,
                'total_weight_kg': shipment.total_weight_kg,
                'total_volume_m3': shipment.total_volume_m3,
                'created_at': shipment.created_at,
                'updated_at': shipment.updated_at
            }
            for shipment in shipments
        ]

    async def get_shipment_by_id(self, shipment_id: str) -> Optional[Dict[str, Any]]:
        """
        Get a specific shipment by ID
        """
        stmt = select(Shipment).where(Shipment.id == shipment_id)
        result = await self.db_session.execute(stmt)
        shipment = result.scalar_one_or_none()
        
        if shipment:
            return {
                'id': str(shipment.id),
                'tenant_id': str(shipment.tenant_id),
                'parent_tenant_id': str(shipment.parent_tenant_id),
                'child_tenant_id': str(shipment.child_tenant_id),
                'purchase_order_id': str(shipment.purchase_order_id) if shipment.purchase_order_id else None,
                'delivery_route_id': str(shipment.delivery_route_id) if shipment.delivery_route_id else None,
                'shipment_number': shipment.shipment_number,
                'shipment_date': shipment.shipment_date,
                'current_location_lat': shipment.current_location_lat,
                'current_location_lng': shipment.current_location_lng,
                'last_tracked_at': shipment.last_tracked_at,
                'status': shipment.status.value if hasattr(shipment.status, 'value') else shipment.status,
                'actual_delivery_time': shipment.actual_delivery_time,
                'signature': shipment.signature,
                'photo_url': shipment.photo_url,
                'received_by_name': shipment.received_by_name,
                'delivery_notes': shipment.delivery_notes,
                'total_weight_kg': shipment.total_weight_kg,
                'total_volume_m3': shipment.total_volume_m3,
                'created_at': shipment.created_at,
                'updated_at': shipment.updated_at
            }
        return None

    async def update_shipment_status(self, shipment_id: str, status: str, user_id: str, metadata: Optional[Dict[str, Any]] = None) -> Optional[Dict[str, Any]]:
        """
        Update shipment status
        """
        stmt = select(Shipment).where(Shipment.id == shipment_id)
        result = await self.db_session.execute(stmt)
        shipment = result.scalar_one_or_none()

        if not shipment:
            return None

        # Handle system user ID if passed as string
        if user_id == 'system':
            SYSTEM_USER_ID = uuid.UUID("50000000-0000-0000-0000-000000000004")
            shipment.updated_by = SYSTEM_USER_ID
        else:
            shipment.updated_by = user_id
        shipment.status = status

        # Update tracking information if provided in metadata
        if metadata:
            if 'current_location_lat' in metadata:
                shipment.current_location_lat = metadata['current_location_lat']
            if 'current_location_lng' in metadata:
                shipment.current_location_lng = metadata['current_location_lng']
            if 'last_tracked_at' in metadata:
                from datetime import datetime
                shipment.last_tracked_at = datetime.fromisoformat(metadata['last_tracked_at']) if isinstance(metadata['last_tracked_at'], str) else metadata['last_tracked_at']
            if 'signature' in metadata:
                shipment.signature = metadata['signature']
            if 'photo_url' in metadata:
                shipment.photo_url = metadata['photo_url']
            if 'received_by_name' in metadata:
                shipment.received_by_name = metadata['received_by_name']
            if 'delivery_notes' in metadata:
                shipment.delivery_notes = metadata['delivery_notes']
            if 'actual_delivery_time' in metadata:
                from datetime import datetime
                shipment.actual_delivery_time = datetime.fromisoformat(metadata['actual_delivery_time']) if isinstance(metadata['actual_delivery_time'], str) else metadata['actual_delivery_time']

        await self.db_session.commit()
        await self.db_session.refresh(shipment)
        
        return {
            'id': str(shipment.id),
            'tenant_id': str(shipment.tenant_id),
            'parent_tenant_id': str(shipment.parent_tenant_id),
            'child_tenant_id': str(shipment.child_tenant_id),
            'purchase_order_id': str(shipment.purchase_order_id) if shipment.purchase_order_id else None,
            'delivery_route_id': str(shipment.delivery_route_id) if shipment.delivery_route_id else None,
            'shipment_number': shipment.shipment_number,
            'shipment_date': shipment.shipment_date,
            'current_location_lat': shipment.current_location_lat,
            'current_location_lng': shipment.current_location_lng,
            'last_tracked_at': shipment.last_tracked_at,
            'status': shipment.status.value if hasattr(shipment.status, 'value') else shipment.status,
            'actual_delivery_time': shipment.actual_delivery_time,
            'signature': shipment.signature,
            'photo_url': shipment.photo_url,
            'received_by_name': shipment.received_by_name,
            'delivery_notes': shipment.delivery_notes,
            'total_weight_kg': shipment.total_weight_kg,
            'total_volume_m3': shipment.total_volume_m3,
            'created_at': shipment.created_at,
            'updated_at': shipment.updated_at
        }

    async def assign_shipments_to_route(self, route_id: str, shipment_ids: List[str], user_id: str) -> Dict[str, Any]:
        """
        Assign multiple shipments to a specific route
        """
        stmt = select(Shipment).where(Shipment.id.in_(shipment_ids))
        result = await self.db_session.execute(stmt)
        shipments = result.scalars().all()

        # Handle system user ID if passed as string
        actual_user_id = user_id
        if user_id == 'system':
            actual_user_id = uuid.UUID("50000000-0000-0000-0000-000000000004")

        updated_shipments = []
        for shipment in shipments:
            shipment.delivery_route_id = route_id
            shipment.updated_by = actual_user_id
            await self.db_session.refresh(shipment)

            updated_shipments.append({
                'id': str(shipment.id),
                'shipment_number': shipment.shipment_number,
                'status': shipment.status.value if hasattr(shipment.status, 'value') else shipment.status,
                'delivery_route_id': str(shipment.delivery_route_id)
            })

        await self.db_session.commit()

        return {
            'route_id': route_id,
            'updated_shipments': updated_shipments,
            'count': len(updated_shipments)
        }

    async def get_all_shipments_for_tenant(self, tenant_id: str) -> List[Dict[str, Any]]:
        """
        Get all shipments for a tenant
        """
        stmt = select(Shipment).where(Shipment.tenant_id == tenant_id)
        
        result = await self.db_session.execute(stmt)
        shipments = result.scalars().all()
        
        return [
            {
                'id': str(shipment.id),
                'tenant_id': str(shipment.tenant_id),
                'parent_tenant_id': str(shipment.parent_tenant_id),
                'child_tenant_id': str(shipment.child_tenant_id),
                'purchase_order_id': str(shipment.purchase_order_id) if shipment.purchase_order_id else None,
                'delivery_route_id': str(shipment.delivery_route_id) if shipment.delivery_route_id else None,
                'shipment_number': shipment.shipment_number,
                'shipment_date': shipment.shipment_date,
                'current_location_lat': shipment.current_location_lat,
                'current_location_lng': shipment.current_location_lng,
                'last_tracked_at': shipment.last_tracked_at,
                'status': shipment.status.value if hasattr(shipment.status, 'value') else shipment.status,
                'actual_delivery_time': shipment.actual_delivery_time,
                'signature': shipment.signature,
                'photo_url': shipment.photo_url,
                'received_by_name': shipment.received_by_name,
                'delivery_notes': shipment.delivery_notes,
                'total_weight_kg': shipment.total_weight_kg,
                'total_volume_m3': shipment.total_volume_m3,
                'created_at': shipment.created_at,
                'updated_at': shipment.updated_at
            }
            for shipment in shipments
        ]

    async def delete_demo_shipments_for_tenant(self, tenant_id: str) -> int:
        """
        Delete all demo shipments for a tenant
        Used for demo session cleanup

        Args:
            tenant_id: The tenant ID to delete shipments for

        Returns:
            Number of shipments deleted
        """
        from sqlalchemy import delete

        # Delete shipments with DEMOSHP- prefix in shipment_number
        stmt = delete(Shipment).where(
            (Shipment.tenant_id == uuid.UUID(tenant_id)) &
            (Shipment.shipment_number.like('DEMOSHP-%'))
        )

        result = await self.db_session.execute(stmt)
        await self.db_session.commit()

        deleted_count = result.rowcount
        return deleted_count