from fastapi import APIRouter, Depends, HTTPException, status, Query
from sqlalchemy.orm import Session
from sqlalchemy import and_, or_, func, desc
from typing import List, Optional
from datetime import datetime
from ...shared.config.database import get_db
from ...shared.config.logging_config import get_logger
from ...security.middleware.auth import get_current_user, authorize_roles
from ...auth.models.user import User
from ...auth.models.role import Role
from ..models.inventory_item import InventoryItem, InventoryCategory, InventoryUnit
from ..models.inventory_transaction import InventoryTransaction, TransactionType
from ..models.inventory_reorder_request import InventoryReorderRequest, ReorderStatus
from ..models.inventory_task_consumption import InventoryTaskConsumption
from ...hotel_services.models.housekeeping_task import HousekeepingTask
from pydantic import BaseModel
from decimal import Decimal

logger = get_logger(__name__)
router = APIRouter(prefix='/inventory', tags=['inventory-management'])


# ==================== Pydantic Schemas ====================

class InventoryItemCreate(BaseModel):
    name: str
    description: Optional[str] = None
    category: str
    unit: str
    minimum_quantity: Decimal
    maximum_quantity: Optional[Decimal] = None
    reorder_quantity: Optional[Decimal] = None
    unit_cost: Optional[Decimal] = None
    supplier: Optional[str] = None
    supplier_contact: Optional[str] = None
    storage_location: Optional[str] = None
    barcode: Optional[str] = None
    sku: Optional[str] = None
    notes: Optional[str] = None

class InventoryItemUpdate(BaseModel):
    name: Optional[str] = None
    description: Optional[str] = None
    category: Optional[str] = None
    unit: Optional[str] = None
    minimum_quantity: Optional[Decimal] = None
    maximum_quantity: Optional[Decimal] = None
    reorder_quantity: Optional[Decimal] = None
    unit_cost: Optional[Decimal] = None
    supplier: Optional[str] = None
    supplier_contact: Optional[str] = None
    storage_location: Optional[str] = None
    is_active: Optional[bool] = None
    is_tracked: Optional[bool] = None
    barcode: Optional[str] = None
    sku: Optional[str] = None
    notes: Optional[str] = None

class InventoryTransactionCreate(BaseModel):
    item_id: int
    transaction_type: str
    quantity: Decimal
    notes: Optional[str] = None
    cost: Optional[Decimal] = None
    reference_type: Optional[str] = None
    reference_id: Optional[int] = None

class ReorderRequestCreate(BaseModel):
    item_id: int
    requested_quantity: Decimal
    priority: str = 'normal'
    notes: Optional[str] = None

class TaskConsumptionCreate(BaseModel):
    task_id: int
    item_id: int
    quantity: Decimal
    notes: Optional[str] = None


# ==================== Inventory Items ====================

@router.get('/items')
async def get_inventory_items(
    category: Optional[str] = Query(None),
    low_stock: Optional[bool] = Query(None),
    is_active: Optional[bool] = Query(None),
    page: int = Query(1, ge=1),
    limit: int = Query(20, ge=1, le=100),
    current_user: User = Depends(authorize_roles('admin', 'staff', 'housekeeping')),
    db: Session = Depends(get_db)
):
    """Get inventory items with filtering"""
    try:
        query = db.query(InventoryItem)
        
        if category:
            query = query.filter(InventoryItem.category == category)
        
        if is_active is not None:
            query = query.filter(InventoryItem.is_active == is_active)
        
        if low_stock:
            query = query.filter(InventoryItem.current_quantity <= InventoryItem.minimum_quantity)
        
        total = query.count()
        items = query.order_by(desc(InventoryItem.created_at)).offset((page - 1) * limit).limit(limit).all()
        
        return {
            'status': 'success',
            'data': {
                'items': [
                    {
                        'id': item.id,
                        'name': item.name,
                        'description': item.description,
                        'category': item.category.value,
                        'unit': item.unit.value,
                        'current_quantity': float(item.current_quantity),
                        'minimum_quantity': float(item.minimum_quantity),
                        'maximum_quantity': float(item.maximum_quantity) if item.maximum_quantity else None,
                        'reorder_quantity': float(item.reorder_quantity) if item.reorder_quantity else None,
                        'unit_cost': float(item.unit_cost) if item.unit_cost else None,
                        'supplier': item.supplier,
                        'storage_location': item.storage_location,
                        'is_active': item.is_active,
                        'is_tracked': item.is_tracked,
                        'barcode': item.barcode,
                        'sku': item.sku,
                        'is_low_stock': item.current_quantity <= item.minimum_quantity,
                        'created_at': item.created_at.isoformat() if item.created_at else None,
                    }
                    for item in items
                ],
                'pagination': {
                    'total': total,
                    'page': page,
                    'limit': limit,
                    'total_pages': (total + limit - 1) // limit
                }
            }
        }
    except Exception as e:
        logger.error(f'Error fetching inventory items: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail='Failed to fetch inventory items')

@router.post('/items')
async def create_inventory_item(
    item_data: InventoryItemCreate,
    current_user: User = Depends(authorize_roles('admin', 'staff')),
    db: Session = Depends(get_db)
):
    """Create a new inventory item"""
    try:
        item = InventoryItem(
            name=item_data.name,
            description=item_data.description,
            category=InventoryCategory(item_data.category),
            unit=InventoryUnit(item_data.unit),
            minimum_quantity=item_data.minimum_quantity,
            maximum_quantity=item_data.maximum_quantity,
            reorder_quantity=item_data.reorder_quantity,
            unit_cost=item_data.unit_cost,
            supplier=item_data.supplier,
            supplier_contact=item_data.supplier_contact,
            storage_location=item_data.storage_location,
            barcode=item_data.barcode,
            sku=item_data.sku,
            notes=item_data.notes,
            current_quantity=Decimal('0'),
            created_by=current_user.id,
        )
        db.add(item)
        db.commit()
        db.refresh(item)
        
        return {
            'status': 'success',
            'message': 'Inventory item created successfully',
            'data': {'id': item.id}
        }
    except ValueError as e:
        raise HTTPException(status_code=400, detail=f'Invalid enum value: {str(e)}')
    except Exception as e:
        logger.error(f'Error creating inventory item: {str(e)}', exc_info=True)
        db.rollback()
        raise HTTPException(status_code=500, detail='Failed to create inventory item')

@router.put('/items/{item_id}')
async def update_inventory_item(
    item_id: int,
    item_data: InventoryItemUpdate,
    current_user: User = Depends(authorize_roles('admin', 'staff')),
    db: Session = Depends(get_db)
):
    """Update an inventory item"""
    try:
        item = db.query(InventoryItem).filter(InventoryItem.id == item_id).first()
        if not item:
            raise HTTPException(status_code=404, detail='Inventory item not found')
        
        update_data = item_data.dict(exclude_unset=True)
        if 'category' in update_data:
            update_data['category'] = InventoryCategory(update_data['category'])
        if 'unit' in update_data:
            update_data['unit'] = InventoryUnit(update_data['unit'])
        
        for key, value in update_data.items():
            setattr(item, key, value)
        
        db.commit()
        db.refresh(item)
        
        return {
            'status': 'success',
            'message': 'Inventory item updated successfully'
        }
    except ValueError as e:
        raise HTTPException(status_code=400, detail=f'Invalid enum value: {str(e)}')
    except Exception as e:
        logger.error(f'Error updating inventory item: {str(e)}', exc_info=True)
        db.rollback()
        raise HTTPException(status_code=500, detail='Failed to update inventory item')

@router.get('/items/{item_id}')
async def get_inventory_item(
    item_id: int,
    current_user: User = Depends(authorize_roles('admin', 'staff', 'housekeeping')),
    db: Session = Depends(get_db)
):
    """Get a single inventory item"""
    try:
        item = db.query(InventoryItem).filter(InventoryItem.id == item_id).first()
        if not item:
            raise HTTPException(status_code=404, detail='Inventory item not found')
        
        return {
            'status': 'success',
            'data': {
                'id': item.id,
                'name': item.name,
                'description': item.description,
                'category': item.category.value,
                'unit': item.unit.value,
                'current_quantity': float(item.current_quantity),
                'minimum_quantity': float(item.minimum_quantity),
                'maximum_quantity': float(item.maximum_quantity) if item.maximum_quantity else None,
                'reorder_quantity': float(item.reorder_quantity) if item.reorder_quantity else None,
                'unit_cost': float(item.unit_cost) if item.unit_cost else None,
                'supplier': item.supplier,
                'supplier_contact': item.supplier_contact,
                'storage_location': item.storage_location,
                'is_active': item.is_active,
                'is_tracked': item.is_tracked,
                'barcode': item.barcode,
                'sku': item.sku,
                'notes': item.notes,
                'is_low_stock': item.current_quantity <= item.minimum_quantity,
                'created_at': item.created_at.isoformat() if item.created_at else None,
            }
        }
    except Exception as e:
        logger.error(f'Error fetching inventory item: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail='Failed to fetch inventory item')


# ==================== Inventory Transactions ====================

@router.post('/transactions')
async def create_transaction(
    transaction_data: InventoryTransactionCreate,
    current_user: User = Depends(authorize_roles('admin', 'staff', 'housekeeping')),
    db: Session = Depends(get_db)
):
    """Create an inventory transaction (consumption, adjustment, received, etc.)"""
    try:
        item = db.query(InventoryItem).filter(InventoryItem.id == transaction_data.item_id).first()
        if not item:
            raise HTTPException(status_code=404, detail='Inventory item not found')
        
        quantity_before = item.current_quantity
        transaction_type = TransactionType(transaction_data.transaction_type)
        
        # Calculate new quantity based on transaction type
        if transaction_type in [TransactionType.consumption, TransactionType.damaged, TransactionType.transfer]:
            quantity_change = -abs(transaction_data.quantity)
        elif transaction_type in [TransactionType.received, TransactionType.returned]:
            quantity_change = abs(transaction_data.quantity)
        else:  # adjustment
            quantity_change = transaction_data.quantity
        
        quantity_after = quantity_before + quantity_change
        
        # Create transaction
        transaction = InventoryTransaction(
            item_id=transaction_data.item_id,
            transaction_type=transaction_type,
            quantity=quantity_change,
            quantity_before=quantity_before,
            quantity_after=quantity_after,
            reference_type=transaction_data.reference_type,
            reference_id=transaction_data.reference_id,
            notes=transaction_data.notes,
            cost=transaction_data.cost,
            performed_by=current_user.id,
            transaction_date=datetime.utcnow(),
        )
        
        # Update item quantity
        item.current_quantity = quantity_after
        
        db.add(transaction)
        db.commit()
        db.refresh(transaction)
        
        return {
            'status': 'success',
            'message': 'Transaction created successfully',
            'data': {'id': transaction.id, 'new_quantity': float(quantity_after)}
        }
    except ValueError as e:
        raise HTTPException(status_code=400, detail=f'Invalid transaction type: {str(e)}')
    except Exception as e:
        logger.error(f'Error creating transaction: {str(e)}', exc_info=True)
        db.rollback()
        raise HTTPException(status_code=500, detail='Failed to create transaction')

@router.get('/items/{item_id}/transactions')
async def get_item_transactions(
    item_id: int,
    page: int = Query(1, ge=1),
    limit: int = Query(20, ge=1, le=100),
    current_user: User = Depends(authorize_roles('admin', 'staff', 'housekeeping')),
    db: Session = Depends(get_db)
):
    """Get transactions for a specific item"""
    try:
        query = db.query(InventoryTransaction).filter(InventoryTransaction.item_id == item_id)
        total = query.count()
        transactions = query.order_by(desc(InventoryTransaction.transaction_date)).offset((page - 1) * limit).limit(limit).all()
        
        return {
            'status': 'success',
            'data': {
                'transactions': [
                    {
                        'id': t.id,
                        'transaction_type': t.transaction_type.value,
                        'quantity': float(t.quantity),
                        'quantity_before': float(t.quantity_before),
                        'quantity_after': float(t.quantity_after),
                        'notes': t.notes,
                        'cost': float(t.cost) if t.cost else None,
                        'transaction_date': t.transaction_date.isoformat() if t.transaction_date else None,
                    }
                    for t in transactions
                ],
                'pagination': {
                    'total': total,
                    'page': page,
                    'limit': limit,
                    'total_pages': (total + limit - 1) // limit
                }
            }
        }
    except Exception as e:
        logger.error(f'Error fetching transactions: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail='Failed to fetch transactions')


# ==================== Reorder Requests ====================

@router.post('/reorder-requests')
async def create_reorder_request(
    request_data: ReorderRequestCreate,
    current_user: User = Depends(authorize_roles('admin', 'staff', 'housekeeping')),
    db: Session = Depends(get_db)
):
    """Create a reorder request for low stock items"""
    try:
        item = db.query(InventoryItem).filter(InventoryItem.id == request_data.item_id).first()
        if not item:
            raise HTTPException(status_code=404, detail='Inventory item not found')
        
        reorder_request = InventoryReorderRequest(
            item_id=request_data.item_id,
            requested_quantity=request_data.requested_quantity,
            current_quantity=item.current_quantity,
            minimum_quantity=item.minimum_quantity,
            priority=request_data.priority,
            notes=request_data.notes,
            requested_by=current_user.id,
        )
        
        db.add(reorder_request)
        db.commit()
        db.refresh(reorder_request)
        
        return {
            'status': 'success',
            'message': 'Reorder request created successfully',
            'data': {'id': reorder_request.id}
        }
    except Exception as e:
        logger.error(f'Error creating reorder request: {str(e)}', exc_info=True)
        db.rollback()
        raise HTTPException(status_code=500, detail='Failed to create reorder request')

@router.get('/reorder-requests')
async def get_reorder_requests(
    status: Optional[str] = Query(None),
    page: int = Query(1, ge=1),
    limit: int = Query(20, ge=1, le=100),
    current_user: User = Depends(authorize_roles('admin', 'staff', 'housekeeping')),
    db: Session = Depends(get_db)
):
    """Get reorder requests"""
    try:
        query = db.query(InventoryReorderRequest).join(InventoryItem)
        
        # Check if user is housekeeping - they can only see their own requests
        role = db.query(Role).filter(Role.id == current_user.role_id).first()
        is_housekeeping = role and role.name == 'housekeeping'
        
        if is_housekeeping:
            query = query.filter(InventoryReorderRequest.requested_by == current_user.id)
        
        if status:
            query = query.filter(InventoryReorderRequest.status == status)
        
        total = query.count()
        requests = query.order_by(desc(InventoryReorderRequest.requested_at)).offset((page - 1) * limit).limit(limit).all()
        
        return {
            'status': 'success',
            'data': {
                'requests': [
                    {
                        'id': r.id,
                        'item_id': r.item_id,
                        'item_name': r.item.name,
                        'requested_quantity': float(r.requested_quantity),
                        'current_quantity': float(r.current_quantity),
                        'minimum_quantity': float(r.minimum_quantity),
                        'status': r.status.value,
                        'priority': r.priority,
                        'notes': r.notes,
                        'requested_at': r.requested_at.isoformat() if r.requested_at else None,
                    }
                    for r in requests
                ],
                'pagination': {
                    'total': total,
                    'page': page,
                    'limit': limit,
                    'total_pages': (total + limit - 1) // limit
                }
            }
        }
    except Exception as e:
        logger.error(f'Error fetching reorder requests: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail='Failed to fetch reorder requests')


# ==================== Task Consumption ====================

@router.post('/task-consumption')
async def record_task_consumption(
    consumption_data: TaskConsumptionCreate,
    current_user: User = Depends(authorize_roles('admin', 'staff', 'housekeeping')),
    db: Session = Depends(get_db)
):
    """Record inventory consumption for a housekeeping task"""
    try:
        task = db.query(HousekeepingTask).filter(HousekeepingTask.id == consumption_data.task_id).first()
        if not task:
            raise HTTPException(status_code=404, detail='Housekeeping task not found')
        
        item = db.query(InventoryItem).filter(InventoryItem.id == consumption_data.item_id).first()
        if not item:
            raise HTTPException(status_code=404, detail='Inventory item not found')
        
        # Check if user is housekeeping - they can only record for their own tasks
        role = db.query(Role).filter(Role.id == current_user.role_id).first()
        is_housekeeping = role and role.name == 'housekeeping'
        
        if is_housekeeping and task.assigned_to != current_user.id:
            raise HTTPException(status_code=403, detail='You can only record consumption for your assigned tasks')
        
        # Create consumption record
        consumption = InventoryTaskConsumption(
            task_id=consumption_data.task_id,
            item_id=consumption_data.item_id,
            quantity=consumption_data.quantity,
            notes=consumption_data.notes,
            recorded_by=current_user.id,
            recorded_at=datetime.utcnow(),
        )
        
        # Create transaction and update stock
        quantity_before = item.current_quantity
        quantity_after = quantity_before - abs(consumption_data.quantity)
        
        transaction = InventoryTransaction(
            item_id=consumption_data.item_id,
            transaction_type=TransactionType.consumption,
            quantity=-abs(consumption_data.quantity),
            quantity_before=quantity_before,
            quantity_after=quantity_after,
            reference_type='housekeeping_task',
            reference_id=consumption_data.task_id,
            notes=f'Consumed for task {consumption_data.task_id}',
            performed_by=current_user.id,
            transaction_date=datetime.utcnow(),
        )
        
        item.current_quantity = quantity_after
        
        db.add(consumption)
        db.add(transaction)
        db.commit()
        
        return {
            'status': 'success',
            'message': 'Consumption recorded successfully'
        }
    except Exception as e:
        logger.error(f'Error recording consumption: {str(e)}', exc_info=True)
        db.rollback()
        raise HTTPException(status_code=500, detail='Failed to record consumption')

@router.get('/low-stock')
async def get_low_stock_items(
    current_user: User = Depends(authorize_roles('admin', 'staff', 'housekeeping')),
    db: Session = Depends(get_db)
):
    """Get all items with low stock (current_quantity <= minimum_quantity)"""
    try:
        items = db.query(InventoryItem).filter(
            InventoryItem.is_active == True,
            InventoryItem.current_quantity <= InventoryItem.minimum_quantity
        ).all()
        
        return {
            'status': 'success',
            'data': {
                'items': [
                    {
                        'id': item.id,
                        'name': item.name,
                        'category': item.category.value,
                        'current_quantity': float(item.current_quantity),
                        'minimum_quantity': float(item.minimum_quantity),
                        'unit': item.unit.value,
                        'reorder_quantity': float(item.reorder_quantity) if item.reorder_quantity else None,
                    }
                    for item in items
                ],
                'count': len(items)
            }
        }
    except Exception as e:
        logger.error(f'Error fetching low stock items: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail='Failed to fetch low stock items')

