# services/suppliers/app/api/suppliers.py
"""
Supplier CRUD API endpoints (ATOMIC)
"""

from fastapi import APIRouter, Depends, HTTPException, Query, Path
from typing import List, Optional, Dict, Any
from uuid import UUID
import structlog
import httpx

from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from app.core.database import get_db
from app.services.supplier_service import SupplierService
from app.models.suppliers import SupplierPriceList
from app.models import AuditLog
from app.schemas.suppliers import (
    SupplierCreate, SupplierUpdate, SupplierResponse, SupplierSummary,
    SupplierSearchParams, SupplierDeletionSummary,
    SupplierPriceListCreate, SupplierPriceListUpdate, SupplierPriceListResponse
)
from shared.auth.decorators import get_current_user_dep
from shared.routing import RouteBuilder
from shared.auth.access_control import require_user_role
from shared.security import create_audit_logger, AuditSeverity, AuditAction

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


router = APIRouter(tags=["suppliers"])
logger = structlog.get_logger()
audit_logger = create_audit_logger("suppliers-service", AuditLog)

@router.post(route_builder.build_base_route(""), response_model=SupplierResponse)
@require_user_role(['admin', 'owner', 'member'])
async def create_supplier(
    supplier_data: SupplierCreate,
    tenant_id: str = Path(..., description="Tenant ID"),
    current_user: Dict[str, Any] = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """Create a new supplier"""
    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}/suppliers/can-add",
                    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(
                            "Supplier limit exceeded",
                            tenant_id=tenant_id,
                            current=limit_check.get('current_count'),
                            max=limit_check.get('max_allowed'),
                            reason=limit_check.get('reason')
                        )
                        raise HTTPException(
                            status_code=402,
                            detail={
                                "error": "supplier_limit_exceeded",
                                "message": limit_check.get('reason', 'Supplier 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 supplier limit, allowing creation",
                        tenant_id=tenant_id,
                        status_code=limit_check_response.status_code
                    )
            except httpx.TimeoutException:
                logger.warning("Timeout checking supplier limit, allowing creation", tenant_id=tenant_id)
            except httpx.RequestError as e:
                logger.warning("Error checking supplier limit, allowing creation", tenant_id=tenant_id, error=str(e))

        service = SupplierService(db)

        # Get user role from current_user dict
        user_role = current_user.get("role", "member").lower()

        supplier = await service.create_supplier(
            tenant_id=UUID(tenant_id),
            supplier_data=supplier_data,
            created_by=current_user["user_id"],
            created_by_role=user_role
        )

        logger.info("Supplier created successfully", tenant_id=tenant_id, supplier_id=str(supplier.id), supplier_name=supplier.name)

        return SupplierResponse.from_orm(supplier)
    except HTTPException:
        raise
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        logger.error("Error creating supplier", error=str(e))
        raise HTTPException(status_code=500, detail="Failed to create supplier")


@router.get(route_builder.build_base_route(""), response_model=List[SupplierSummary])
async def list_suppliers(
    tenant_id: str = Path(..., description="Tenant ID"),
    search_term: Optional[str] = Query(None, description="Search term"),
    supplier_type: Optional[str] = Query(None, description="Supplier type filter"),
    status: Optional[str] = Query(None, description="Status filter"),
    limit: int = Query(50, ge=1, le=1000, description="Number of results to return"),
    offset: int = Query(0, ge=0, description="Number of results to skip"),
    db: AsyncSession = Depends(get_db)
):
    """List suppliers with optional filters"""
    try:
        service = SupplierService(db)
        search_params = SupplierSearchParams(
            search_term=search_term,
            supplier_type=supplier_type,
            status=status,
            limit=limit,
            offset=offset
        )
        suppliers = await service.search_suppliers(
            tenant_id=UUID(tenant_id),
            search_params=search_params
        )
        return [SupplierSummary.from_orm(supplier) for supplier in suppliers]
    except Exception as e:
        logger.error("Error listing suppliers", error=str(e))
        raise HTTPException(status_code=500, detail="Failed to retrieve suppliers")


@router.get(route_builder.build_base_route("batch"), response_model=List[SupplierSummary])
async def get_suppliers_batch(
    tenant_id: str = Path(..., description="Tenant ID"),
    ids: str = Query(..., description="Comma-separated supplier IDs"),
    db: AsyncSession = Depends(get_db)
):
    """
    Get multiple suppliers in a single call for performance optimization.

    This endpoint is designed to eliminate N+1 query patterns when fetching
    supplier data for multiple purchase orders or other entities.

    Args:
        tenant_id: Tenant ID
        ids: Comma-separated supplier IDs (e.g., "abc123,def456,xyz789")

    Returns:
        List of supplier summaries for the requested IDs
    """
    try:
        service = SupplierService(db)

        # Parse comma-separated IDs
        supplier_ids = [id.strip() for id in ids.split(",") if id.strip()]

        if not supplier_ids:
            return []

        if len(supplier_ids) > 100:
            raise HTTPException(
                status_code=400,
                detail="Maximum 100 supplier IDs allowed per batch request"
            )

        # Convert to UUIDs
        try:
            uuid_ids = [UUID(id) for id in supplier_ids]
        except ValueError as e:
            raise HTTPException(status_code=400, detail=f"Invalid supplier ID format: {e}")

        # Fetch suppliers
        suppliers = await service.get_suppliers_batch(tenant_id=UUID(tenant_id), supplier_ids=uuid_ids)

        logger.info(
            "Batch retrieved suppliers",
            tenant_id=tenant_id,
            requested_count=len(supplier_ids),
            found_count=len(suppliers)
        )

        return [SupplierSummary.from_orm(supplier) for supplier in suppliers]

    except HTTPException:
        raise
    except Exception as e:
        logger.error("Error batch retrieving suppliers", error=str(e), tenant_id=tenant_id)
        raise HTTPException(status_code=500, detail="Failed to retrieve suppliers")


@router.get(route_builder.build_resource_detail_route("", "supplier_id"), response_model=SupplierResponse)
async def get_supplier(
    supplier_id: UUID = Path(..., description="Supplier ID"),
    tenant_id: str = Path(..., description="Tenant ID"),
    db: AsyncSession = Depends(get_db)
):
    """Get supplier by ID"""
    try:
        service = SupplierService(db)
        supplier = await service.get_supplier(supplier_id)

        if not supplier:
            raise HTTPException(status_code=404, detail="Supplier not found")

        return SupplierResponse.from_orm(supplier)
    except HTTPException:
        raise
    except Exception as e:
        logger.error("Error getting supplier", supplier_id=str(supplier_id), error=str(e))
        raise HTTPException(status_code=500, detail="Failed to retrieve supplier")


@router.put(route_builder.build_resource_detail_route("", "supplier_id"), response_model=SupplierResponse)
@require_user_role(['admin', 'owner', 'member'])
async def update_supplier(
    supplier_data: SupplierUpdate,
    supplier_id: UUID = Path(..., description="Supplier ID"),
    current_user: Dict[str, Any] = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """Update supplier information"""
    try:
        service = SupplierService(db)

        # Check supplier exists
        existing_supplier = await service.get_supplier(supplier_id)
        if not existing_supplier:
            raise HTTPException(status_code=404, detail="Supplier not found")

        supplier = await service.update_supplier(
            supplier_id=supplier_id,
            supplier_data=supplier_data,
            updated_by=current_user["user_id"]
        )

        if not supplier:
            raise HTTPException(status_code=404, detail="Supplier not found")

        return SupplierResponse.from_orm(supplier)
    except HTTPException:
        raise
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        logger.error("Error updating supplier", supplier_id=str(supplier_id), error=str(e))
        raise HTTPException(status_code=500, detail="Failed to update supplier")


@router.delete(route_builder.build_resource_detail_route("", "supplier_id"))
@require_user_role(['admin', 'owner'])
async def delete_supplier(
    supplier_id: UUID = Path(..., description="Supplier ID"),
    tenant_id: str = Path(..., description="Tenant ID"),
    current_user: Dict[str, Any] = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """Delete supplier (soft delete, Admin+ only)"""
    try:
        service = SupplierService(db)

        # Check supplier exists
        existing_supplier = await service.get_supplier(supplier_id)
        if not existing_supplier:
            raise HTTPException(status_code=404, detail="Supplier not found")

        # Capture supplier data before deletion
        supplier_data = {
            "supplier_name": existing_supplier.name,
            "supplier_type": existing_supplier.supplier_type,
            "contact_person": existing_supplier.contact_person,
            "email": existing_supplier.email
        }

        success = await service.delete_supplier(supplier_id)
        if not success:
            raise HTTPException(status_code=404, detail="Supplier not found")

        # Log audit event for supplier deletion
        try:
            # Get sync db session for audit logging
            from app.core.database import SessionLocal
            sync_db = SessionLocal()
            try:
                await audit_logger.log_deletion(
                    db_session=sync_db,
                    tenant_id=tenant_id,
                    user_id=current_user["user_id"],
                    resource_type="supplier",
                    resource_id=str(supplier_id),
                    resource_data=supplier_data,
                    description=f"Admin {current_user.get('email', 'unknown')} deleted supplier",
                    endpoint=f"/suppliers/{supplier_id}",
                    method="DELETE"
                )
                sync_db.commit()
            finally:
                sync_db.close()
        except Exception as audit_error:
            logger.warning("Failed to log audit event", error=str(audit_error))

        logger.info("Deleted supplier",
                   supplier_id=str(supplier_id),
                   tenant_id=tenant_id,
                   user_id=current_user["user_id"])

        return {"message": "Supplier deleted successfully"}
    except HTTPException:
        raise
    except Exception as e:
        logger.error("Error deleting supplier", supplier_id=str(supplier_id), error=str(e))
        raise HTTPException(status_code=500, detail="Failed to delete supplier")


@router.delete(
    route_builder.build_resource_action_route("", "supplier_id", "hard"),
    response_model=SupplierDeletionSummary
)
@require_user_role(['admin', 'owner'])
async def hard_delete_supplier(
    supplier_id: UUID = Path(..., description="Supplier ID"),
    tenant_id: str = Path(..., description="Tenant ID"),
    current_user: Dict[str, Any] = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """Hard delete supplier and all associated data (Admin/Owner only, permanent)"""
    try:
        service = SupplierService(db)

        # Check supplier exists
        existing_supplier = await service.get_supplier(supplier_id)
        if not existing_supplier:
            raise HTTPException(status_code=404, detail="Supplier not found")

        # Capture supplier data before deletion
        supplier_data = {
            "id": str(existing_supplier.id),
            "name": existing_supplier.name,
            "status": existing_supplier.status.value,
            "supplier_code": existing_supplier.supplier_code
        }

        # Perform hard deletion
        deletion_summary = await service.hard_delete_supplier(supplier_id, UUID(tenant_id))

        # Log audit event for hard deletion
        try:
            # Get sync db session for audit logging
            from app.core.database import SessionLocal
            sync_db = SessionLocal()
            try:
                await audit_logger.log_deletion(
                    db_session=sync_db,
                    tenant_id=tenant_id,
                    user_id=current_user["user_id"],
                    resource_type="supplier",
                    resource_id=str(supplier_id),
                    resource_data=supplier_data,
                    description=f"Hard deleted supplier '{supplier_data['name']}' and all associated data",
                    endpoint=f"/suppliers/{supplier_id}/hard",
                    method="DELETE",
                    metadata=deletion_summary
                )
                sync_db.commit()
            finally:
                sync_db.close()
        except Exception as audit_error:
            logger.warning("Failed to log audit event", error=str(audit_error))

        logger.info("Hard deleted supplier",
                   supplier_id=str(supplier_id),
                   tenant_id=tenant_id,
                   user_id=current_user["user_id"],
                   deletion_summary=deletion_summary)

        return deletion_summary
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except HTTPException:
        raise
    except Exception as e:
        logger.error("Error hard deleting supplier", supplier_id=str(supplier_id), error=str(e))
        raise HTTPException(status_code=500, detail="Failed to hard delete supplier")


@router.get(
    route_builder.build_base_route("count"),
    response_model=dict
)
async def count_suppliers(
    tenant_id: str = Path(..., description="Tenant ID"),
    db: AsyncSession = Depends(get_db)
):
    """Get count of suppliers for a tenant"""
    try:
        service = SupplierService(db)

        # Use search with maximum allowed limit to get all suppliers
        search_params = SupplierSearchParams(limit=1000)
        suppliers = await service.search_suppliers(
            tenant_id=UUID(tenant_id),
            search_params=search_params
        )

        count = len(suppliers)
        logger.info("Retrieved supplier count", tenant_id=tenant_id, count=count)

        return {"count": count}

    except Exception as e:
        logger.error("Error counting suppliers", tenant_id=tenant_id, error=str(e))
        raise HTTPException(status_code=500, detail="Failed to count suppliers")


@router.get(
    route_builder.build_resource_action_route("", "supplier_id", "products"),
    response_model=List[Dict[str, Any]]
)
async def get_supplier_products(
    supplier_id: UUID = Path(..., description="Supplier ID"),
    tenant_id: str = Path(..., description="Tenant ID"),
    is_active: bool = Query(True, description="Filter by active price lists"),
    db: AsyncSession = Depends(get_db)
):
    """
    Get list of product IDs that a supplier provides
    Returns a list of inventory product IDs from the supplier's price list
    """
    try:
        # Query supplier price lists
        query = select(SupplierPriceList).where(
            SupplierPriceList.tenant_id == UUID(tenant_id),
            SupplierPriceList.supplier_id == supplier_id
        )

        if is_active:
            query = query.where(SupplierPriceList.is_active == True)

        result = await db.execute(query)
        price_lists = result.scalars().all()

        # Extract unique product IDs
        product_ids = list(set([str(pl.inventory_product_id) for pl in price_lists]))

        logger.info(
            "Retrieved supplier products",
            supplier_id=str(supplier_id),
            product_count=len(product_ids)
        )

        return [{"inventory_product_id": pid} for pid in product_ids]

    except Exception as e:
        logger.error(
            "Error getting supplier products",
            supplier_id=str(supplier_id),
            error=str(e)
        )
        raise HTTPException(
            status_code=500,
            detail="Failed to retrieve supplier products"
        )


@router.get(
    route_builder.build_resource_action_route("", "supplier_id", "price-lists"),
    response_model=List[SupplierPriceListResponse]
)
async def get_supplier_price_lists(
    supplier_id: UUID = Path(..., description="Supplier ID"),
    tenant_id: str = Path(..., description="Tenant ID"),
    is_active: bool = Query(True, description="Filter by active price lists"),
    db: AsyncSession = Depends(get_db)
):
    """Get all price list items for a supplier"""
    try:
        service = SupplierService(db)
        price_lists = await service.get_supplier_price_lists(
            supplier_id=supplier_id,
            tenant_id=UUID(tenant_id),
            is_active=is_active
        )
        
        logger.info(
            "Retrieved supplier price lists",
            supplier_id=str(supplier_id),
            count=len(price_lists)
        )

        return [SupplierPriceListResponse.from_orm(pl) for pl in price_lists]

    except Exception as e:
        logger.error(
            "Error getting supplier price lists",
            supplier_id=str(supplier_id),
            error=str(e)
        )
        raise HTTPException(
            status_code=500,
            detail="Failed to retrieve supplier price lists"
        )


@router.get(
    route_builder.build_resource_action_route("", "supplier_id", "price-lists/{price_list_id}"),
    response_model=SupplierPriceListResponse
)
async def get_supplier_price_list(
    supplier_id: UUID = Path(..., description="Supplier ID"),
    price_list_id: UUID = Path(..., description="Price List ID"),
    tenant_id: str = Path(..., description="Tenant ID"),
    db: AsyncSession = Depends(get_db)
):
    """Get specific price list item for a supplier"""
    try:
        service = SupplierService(db)
        price_list = await service.get_supplier_price_list(
            price_list_id=price_list_id,
            tenant_id=UUID(tenant_id)
        )
        
        if not price_list:
            raise HTTPException(status_code=404, detail="Price list item not found")

        logger.info(
            "Retrieved supplier price list item",
            supplier_id=str(supplier_id),
            price_list_id=str(price_list_id)
        )

        return SupplierPriceListResponse.from_orm(price_list)

    except HTTPException:
        raise
    except Exception as e:
        logger.error(
            "Error getting supplier price list item",
            supplier_id=str(supplier_id),
            price_list_id=str(price_list_id),
            error=str(e)
        )
        raise HTTPException(
            status_code=500,
            detail="Failed to retrieve supplier price list item"
        )


@router.post(
    route_builder.build_resource_action_route("", "supplier_id", "price-lists"),
    response_model=SupplierPriceListResponse
)
@require_user_role(['admin', 'owner', 'member'])
async def create_supplier_price_list(
    supplier_id: UUID = Path(..., description="Supplier ID"),
    price_list_data: SupplierPriceListCreate = None,
    tenant_id: str = Path(..., description="Tenant ID"),
    current_user: Dict[str, Any] = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """Create a new price list item for a supplier"""
    try:
        service = SupplierService(db)
        
        # Verify supplier exists
        supplier = await service.get_supplier(supplier_id)
        if not supplier:
            raise HTTPException(status_code=404, detail="Supplier not found")
        
        price_list = await service.create_supplier_price_list(
            supplier_id=supplier_id,
            price_list_data=price_list_data,
            tenant_id=UUID(tenant_id),
            created_by=UUID(current_user["user_id"])
        )
        
        logger.info(
            "Created supplier price list item",
            supplier_id=str(supplier_id),
            price_list_id=str(price_list.id)
        )

        return SupplierPriceListResponse.from_orm(price_list)

    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        logger.error(
            "Error creating supplier price list item",
            supplier_id=str(supplier_id),
            error=str(e)
        )
        raise HTTPException(
            status_code=500,
            detail="Failed to create supplier price list item"
        )


@router.put(
    route_builder.build_resource_action_route("", "supplier_id", "price-lists/{price_list_id}"),
    response_model=SupplierPriceListResponse
)
@require_user_role(['admin', 'owner', 'member'])
async def update_supplier_price_list(
    supplier_id: UUID = Path(..., description="Supplier ID"),
    price_list_id: UUID = Path(..., description="Price List ID"),
    price_list_data: SupplierPriceListUpdate = None,
    tenant_id: str = Path(..., description="Tenant ID"),
    current_user: Dict[str, Any] = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """Update a price list item for a supplier"""
    try:
        service = SupplierService(db)
        
        # Verify supplier and price list exist
        supplier = await service.get_supplier(supplier_id)
        if not supplier:
            raise HTTPException(status_code=404, detail="Supplier not found")
        
        price_list = await service.get_supplier_price_list(
            price_list_id=price_list_id,
            tenant_id=UUID(tenant_id)
        )
        if not price_list:
            raise HTTPException(status_code=404, detail="Price list item not found")
        
        updated_price_list = await service.update_supplier_price_list(
            price_list_id=price_list_id,
            price_list_data=price_list_data,
            tenant_id=UUID(tenant_id),
            updated_by=UUID(current_user["user_id"])
        )
        
        logger.info(
            "Updated supplier price list item",
            supplier_id=str(supplier_id),
            price_list_id=str(price_list_id)
        )

        return SupplierPriceListResponse.from_orm(updated_price_list)

    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        logger.error(
            "Error updating supplier price list item",
            supplier_id=str(supplier_id),
            price_list_id=str(price_list_id),
            error=str(e)
        )
        raise HTTPException(
            status_code=500,
            detail="Failed to update supplier price list item"
        )


@router.delete(
    route_builder.build_resource_action_route("", "supplier_id", "price-lists/{price_list_id}")
)
@require_user_role(['admin', 'owner'])
async def delete_supplier_price_list(
    supplier_id: UUID = Path(..., description="Supplier ID"),
    price_list_id: UUID = Path(..., description="Price List ID"),
    tenant_id: str = Path(..., description="Tenant ID"),
    current_user: Dict[str, Any] = Depends(get_current_user_dep),
    db: AsyncSession = Depends(get_db)
):
    """Delete a price list item for a supplier"""
    try:
        service = SupplierService(db)
        
        # Verify supplier and price list exist
        supplier = await service.get_supplier(supplier_id)
        if not supplier:
            raise HTTPException(status_code=404, detail="Supplier not found")
        
        price_list = await service.get_supplier_price_list(
            price_list_id=price_list_id,
            tenant_id=UUID(tenant_id)
        )
        if not price_list:
            raise HTTPException(status_code=404, detail="Price list item not found")
        
        success = await service.delete_supplier_price_list(
            price_list_id=price_list_id,
            tenant_id=UUID(tenant_id)
        )
        
        if not success:
            raise HTTPException(status_code=404, detail="Price list item not found")

        logger.info(
            "Deleted supplier price list item",
            supplier_id=str(supplier_id),
            price_list_id=str(price_list_id)
        )

        return {"message": "Price list item deleted successfully"}

    except Exception as e:
        logger.error(
            "Error deleting supplier price list item",
            supplier_id=str(supplier_id),
            price_list_id=str(price_list_id),
            error=str(e)
        )
        raise HTTPException(
            status_code=500,
            detail="Failed to delete supplier price list item"
        )
