# services/inventory/app/api/ingredients.py
"""
Base CRUD operations for inventory ingredients resources
Following standardized URL structure: /api/v1/tenants/{tenant_id}/inventory/{resource}
"""

from typing import List, Optional
from uuid import UUID
from fastapi import APIRouter, Depends, HTTPException, Query, Path, status
from sqlalchemy.ext.asyncio import AsyncSession
import httpx
import structlog

from app.core.database import get_db
from app.services.inventory_service import InventoryService
from app.models import AuditLog
from app.schemas.inventory import (
    IngredientCreate,
    IngredientUpdate,
    IngredientResponse,
    StockResponse,
    StockCreate,
    StockUpdate,
    BulkIngredientCreate,
    BulkIngredientResponse,
    BulkIngredientResult,
)
from shared.auth.decorators import get_current_user_dep
from shared.auth.access_control import require_user_role, admin_role_required, owner_role_required
from shared.routing import RouteBuilder
from shared.security import create_audit_logger, AuditSeverity, AuditAction

logger = structlog.get_logger()

# Create route builder for consistent URL structure
route_builder = RouteBuilder('inventory')

router = APIRouter(tags=["ingredients"])

# Initialize audit logger
audit_logger = create_audit_logger("inventory-service", AuditLog)

# Helper function to extract user ID from user object
def get_current_user_id(current_user: dict = Depends(get_current_user_dep)) -> UUID:
    """Extract user ID from current user context"""
    user_id = current_user.get('user_id')
    if not user_id:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="User ID not found in context"
        )
    return UUID(user_id)


# ===== INGREDIENTS ENDPOINTS =====

@router.post(
    route_builder.build_base_route("ingredients"),
    response_model=IngredientResponse,
    status_code=status.HTTP_201_CREATED
)
@require_user_role(['admin', 'owner'])
async def create_ingredient(
    ingredient_data: IngredientCreate,
    tenant_id: UUID = Path(..., description="Tenant ID"),
    current_user: dict = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """Create a new ingredient (Admin/Manager only)"""
    try:
        # CRITICAL: Check subscription limit before creating
        from app.core.config import settings

        async with httpx.AsyncClient(timeout=5.0) as client:
            try:
                limit_check_response = await client.get(
                    f"{settings.TENANT_SERVICE_URL}/api/v1/tenants/{tenant_id}/subscription/limits/products",
                    headers={
                        "x-user-id": str(current_user.get('user_id')),
                        "x-tenant-id": str(tenant_id)
                    }
                )

                if limit_check_response.status_code == 200:
                    limit_check = limit_check_response.json()

                    if not limit_check.get('can_add', False):
                        logger.warning(
                            "Product limit exceeded",
                            tenant_id=str(tenant_id),
                            current=limit_check.get('current_count'),
                            max=limit_check.get('max_allowed'),
                            reason=limit_check.get('reason')
                        )
                        raise HTTPException(
                            status_code=status.HTTP_402_PAYMENT_REQUIRED,
                            detail={
                                "error": "product_limit_exceeded",
                                "message": limit_check.get('reason', 'Product limit exceeded'),
                                "current_count": limit_check.get('current_count'),
                                "max_allowed": limit_check.get('max_allowed'),
                                "upgrade_required": True
                            }
                        )
                else:
                    logger.warning(
                        "Failed to check product limit, allowing creation",
                        tenant_id=str(tenant_id),
                        status_code=limit_check_response.status_code
                    )
            except httpx.TimeoutException:
                logger.warning(
                    "Timeout checking product limit, allowing creation",
                    tenant_id=str(tenant_id)
                )
            except httpx.RequestError as e:
                logger.warning(
                    "Error checking product limit, allowing creation",
                    tenant_id=str(tenant_id),
                    error=str(e)
                )

        # Extract user ID - handle service tokens
        raw_user_id = current_user.get('user_id')
        if current_user.get('type') == 'service':
            user_id = None
        else:
            try:
                user_id = UUID(raw_user_id)
            except (ValueError, TypeError):
                user_id = None

        service = InventoryService()
        ingredient = await service.create_ingredient(ingredient_data, tenant_id, user_id)

        logger.info(
            "Ingredient created successfully",
            tenant_id=str(tenant_id),
            ingredient_id=str(ingredient.id),
            ingredient_name=ingredient.name
        )

        return ingredient
    except HTTPException:
        raise
    except ValueError as e:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=str(e)
        )
    except Exception as e:
        logger.error(
            "Failed to create ingredient",
            tenant_id=str(tenant_id),
            error=str(e)
        )
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Failed to create ingredient"
        )


@router.post(
    route_builder.build_base_route("ingredients/bulk"),
    response_model=BulkIngredientResponse,
    status_code=status.HTTP_201_CREATED
)
@require_user_role(['admin', 'owner'])
async def bulk_create_ingredients(
    bulk_data: BulkIngredientCreate,
    tenant_id: UUID = Path(..., description="Tenant ID"),
    current_user: dict = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """Create multiple ingredients in a single transaction (Admin/Manager only)"""
    import uuid
    transaction_id = str(uuid.uuid4())

    try:
        # CRITICAL: Check subscription limit ONCE before creating any ingredients
        from app.core.config import settings
        total_requested = len(bulk_data.ingredients)

        async with httpx.AsyncClient(timeout=5.0) as client:
            try:
                # Check if we can add this many products
                limit_check_response = await client.get(
                    f"{settings.TENANT_SERVICE_URL}/api/v1/tenants/{tenant_id}/subscription/limits/products",
                    headers={
                        "x-user-id": str(current_user.get('user_id')),
                        "x-tenant-id": str(tenant_id)
                    }
                )

                if limit_check_response.status_code == 200:
                    limit_check = limit_check_response.json()

                    if not limit_check.get('can_add', False):
                        logger.warning(
                            "Bulk product limit exceeded",
                            tenant_id=str(tenant_id),
                            requested=total_requested,
                            current=limit_check.get('current_count'),
                            max=limit_check.get('max_allowed'),
                            reason=limit_check.get('reason')
                        )
                        raise HTTPException(
                            status_code=status.HTTP_402_PAYMENT_REQUIRED,
                            detail={
                                "error": "product_limit_exceeded",
                                "message": limit_check.get('reason', 'Product limit exceeded'),
                                "requested": total_requested,
                                "current_count": limit_check.get('current_count'),
                                "max_allowed": limit_check.get('max_allowed'),
                                "upgrade_required": True
                            }
                        )
                else:
                    logger.warning(
                        "Failed to check product limit, allowing bulk creation",
                        tenant_id=str(tenant_id),
                        status_code=limit_check_response.status_code
                    )
            except httpx.TimeoutException:
                logger.warning(
                    "Timeout checking product limit, allowing bulk creation",
                    tenant_id=str(tenant_id)
                )
            except httpx.RequestError as e:
                logger.warning(
                    "Error checking product limit, allowing bulk creation",
                    tenant_id=str(tenant_id),
                    error=str(e)
                )

        # Extract user ID - handle service tokens
        raw_user_id = current_user.get('user_id')
        if current_user.get('type') == 'service':
            user_id = None
        else:
            try:
                user_id = UUID(raw_user_id)
            except (ValueError, TypeError):
                user_id = None

        # Create all ingredients
        service = InventoryService()
        results: List[BulkIngredientResult] = []
        total_created = 0
        total_failed = 0

        for index, ingredient_data in enumerate(bulk_data.ingredients):
            try:
                ingredient = await service.create_ingredient(ingredient_data, tenant_id, user_id)
                results.append(BulkIngredientResult(
                    index=index,
                    success=True,
                    ingredient=IngredientResponse.from_orm(ingredient),
                    error=None
                ))
                total_created += 1

                logger.debug(
                    "Ingredient created in bulk operation",
                    tenant_id=str(tenant_id),
                    ingredient_id=str(ingredient.id),
                    ingredient_name=ingredient.name,
                    index=index,
                    transaction_id=transaction_id
                )
            except Exception as e:
                results.append(BulkIngredientResult(
                    index=index,
                    success=False,
                    ingredient=None,
                    error=str(e)
                ))
                total_failed += 1

                logger.warning(
                    "Failed to create ingredient in bulk operation",
                    tenant_id=str(tenant_id),
                    index=index,
                    error=str(e),
                    transaction_id=transaction_id
                )

        logger.info(
            "Bulk ingredient creation completed",
            tenant_id=str(tenant_id),
            total_requested=total_requested,
            total_created=total_created,
            total_failed=total_failed,
            transaction_id=transaction_id
        )

        return BulkIngredientResponse(
            total_requested=total_requested,
            total_created=total_created,
            total_failed=total_failed,
            results=results,
            transaction_id=transaction_id
        )
    except HTTPException:
        raise
    except Exception as e:
        logger.error(
            "Failed to process bulk ingredient creation",
            tenant_id=str(tenant_id),
            error=str(e),
            transaction_id=transaction_id
        )
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Failed to process bulk ingredient creation"
        )


@router.get(
    route_builder.build_base_route("ingredients/count"),
    response_model=dict
)
async def count_ingredients(
    tenant_id: UUID = Path(...),
    current_user: dict = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """Get count of ingredients for a tenant (All users)"""
    try:
        service = InventoryService()
        count = await service.count_ingredients_by_tenant(tenant_id)

        return {
            "tenant_id": str(tenant_id),
            "ingredient_count": count
        }

    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Failed to count ingredients: {str(e)}"
        )


@router.get(
    route_builder.build_resource_detail_route("ingredients", "ingredient_id"),
    response_model=IngredientResponse
)
async def get_ingredient(
    ingredient_id: UUID,
    tenant_id: UUID = Path(..., description="Tenant ID"),
    current_user: dict = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """Get ingredient by ID (All users)"""
    try:
        service = InventoryService()
        ingredient = await service.get_ingredient(ingredient_id, tenant_id)

        if not ingredient:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Ingredient not found"
            )

        return ingredient
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Failed to get ingredient"
        )


@router.put(
    route_builder.build_resource_detail_route("ingredients", "ingredient_id"),
    response_model=IngredientResponse
)
@require_user_role(['admin', 'owner', 'member'])
async def update_ingredient(
    ingredient_id: UUID,
    ingredient_data: IngredientUpdate,
    tenant_id: UUID = Path(..., description="Tenant ID"),
    current_user: dict = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """Update ingredient (Admin/Manager/User)"""
    try:
        service = InventoryService()
        ingredient = await service.update_ingredient(ingredient_id, ingredient_data, tenant_id)

        if not ingredient:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Ingredient not found"
            )

        return ingredient
    except ValueError as e:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=str(e)
        )
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Failed to update ingredient"
        )


@router.get(
    route_builder.build_base_route("ingredients"),
    response_model=List[IngredientResponse]
)
async def list_ingredients(
    tenant_id: UUID = Path(..., description="Tenant ID"),
    skip: int = Query(0, ge=0, description="Number of records to skip"),
    limit: int = Query(100, ge=1, le=1000, description="Number of records to return"),
    category: Optional[str] = Query(None, description="Filter by category"),
    product_type: Optional[str] = Query(None, description="Filter by product type"),
    is_active: Optional[bool] = Query(None, description="Filter by active status"),
    is_low_stock: Optional[bool] = Query(None, description="Filter by low stock status"),
    needs_reorder: Optional[bool] = Query(None, description="Filter by reorder needed"),
    search: Optional[str] = Query(None, description="Search in name, SKU, or barcode"),
    current_user: dict = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """List ingredients with filtering (All users)"""
    try:
        service = InventoryService()

        # Build filters
        filters = {}
        if category:
            filters['category'] = category
        if product_type:
            filters['product_type'] = product_type
        if is_active is not None:
            filters['is_active'] = is_active
        if is_low_stock is not None:
            filters['is_low_stock'] = is_low_stock
        if needs_reorder is not None:
            filters['needs_reorder'] = needs_reorder
        if search:
            filters['search'] = search

        ingredients = await service.get_ingredients(tenant_id, skip, limit, filters)
        return ingredients
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Failed to list ingredients"
        )


@router.delete(
    route_builder.build_resource_detail_route("ingredients", "ingredient_id"),
    status_code=status.HTTP_204_NO_CONTENT
)
@admin_role_required
async def soft_delete_ingredient(
    ingredient_id: UUID,
    tenant_id: UUID = Path(..., description="Tenant ID"),
    current_user: dict = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """Soft delete ingredient - mark as inactive (Admin only)"""
    try:
        service = InventoryService()
        result = await service.soft_delete_ingredient(ingredient_id, tenant_id)
        return None
    except ValueError as e:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=str(e)
        )
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Failed to soft delete ingredient"
        )


@router.delete(
    route_builder.build_nested_resource_route("ingredients", "ingredient_id", "hard"),
    response_model=dict
)
@admin_role_required
async def hard_delete_ingredient(
    ingredient_id: UUID,
    tenant_id: UUID = Path(..., description="Tenant ID"),
    current_user: dict = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """Hard delete ingredient and all associated data (Admin only)"""
    try:
        service = InventoryService()
        deletion_summary = await service.hard_delete_ingredient(ingredient_id, tenant_id)

        # Log audit event for hard deletion
        try:
            await audit_logger.log_deletion(
                db_session=db,
                tenant_id=str(tenant_id),
                user_id=current_user["user_id"],
                resource_type="ingredient",
                resource_id=str(ingredient_id),
                resource_data=deletion_summary,
                description=f"Hard deleted ingredient and all associated data",
                endpoint=f"/ingredients/{ingredient_id}/hard",
                method="DELETE"
            )
        except Exception as audit_error:
            import structlog
            logger = structlog.get_logger()
            logger.warning("Failed to log audit event", error=str(audit_error))

        return deletion_summary
    except ValueError as e:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=str(e)
        )
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Failed to hard delete ingredient"
        )


@router.get(
    route_builder.build_nested_resource_route("ingredients", "ingredient_id", "stock"),
    response_model=List[StockResponse]
)
async def get_ingredient_stock(
    ingredient_id: UUID,
    tenant_id: UUID = Path(..., description="Tenant ID"),
    include_unavailable: bool = Query(False, description="Include unavailable stock"),
    current_user: dict = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """Get stock entries for an ingredient (All users)"""
    try:
        service = InventoryService()
        stock_entries = await service.get_stock_by_ingredient(
            ingredient_id, tenant_id, include_unavailable
        )
        return stock_entries
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Failed to get ingredient stock"
        )
