# services/tenant/app/services/tenant_settings_service.py
"""
Tenant Settings Service
Business logic for managing tenant-specific operational settings
"""

import structlog
from sqlalchemy.ext.asyncio import AsyncSession
from uuid import UUID
from typing import Optional, Dict, Any
from fastapi import HTTPException, status

from ..models.tenant_settings import TenantSettings
from ..repositories.tenant_settings_repository import TenantSettingsRepository
from ..schemas.tenant_settings import (
    TenantSettingsUpdate,
    ProcurementSettings,
    InventorySettings,
    ProductionSettings,
    SupplierSettings,
    POSSettings,
    OrderSettings,
    ReplenishmentSettings,
    SafetyStockSettings,
    MOQSettings,
    SupplierSelectionSettings,
    NotificationSettings
)

logger = structlog.get_logger()


class TenantSettingsService:
    """
    Service for managing tenant settings
    Handles validation, CRUD operations, and default value management
    """

    # Map category names to schema validators
    CATEGORY_SCHEMAS = {
        "procurement": ProcurementSettings,
        "inventory": InventorySettings,
        "production": ProductionSettings,
        "supplier": SupplierSettings,
        "pos": POSSettings,
        "order": OrderSettings,
        "replenishment": ReplenishmentSettings,
        "safety_stock": SafetyStockSettings,
        "moq": MOQSettings,
        "supplier_selection": SupplierSelectionSettings,
        "notification": NotificationSettings
    }

    # Map category names to database column names
    CATEGORY_COLUMNS = {
        "procurement": "procurement_settings",
        "inventory": "inventory_settings",
        "production": "production_settings",
        "supplier": "supplier_settings",
        "pos": "pos_settings",
        "order": "order_settings",
        "replenishment": "replenishment_settings",
        "safety_stock": "safety_stock_settings",
        "moq": "moq_settings",
        "supplier_selection": "supplier_selection_settings",
        "notification": "notification_settings"
    }

    def __init__(self, db: AsyncSession):
        self.db = db
        self.repository = TenantSettingsRepository(db)

    async def get_settings(self, tenant_id: UUID) -> TenantSettings:
        """
        Get tenant settings, creating defaults if they don't exist

        Args:
            tenant_id: UUID of the tenant

        Returns:
            TenantSettings object

        Raises:
            HTTPException: If tenant not found
        """
        try:
            # Try to get existing settings using repository
            settings = await self.repository.get_by_tenant_id(tenant_id)

            logger.info(f"Existing settings lookup for tenant {tenant_id}: {'found' if settings else 'not found'}")

            # Create default settings if they don't exist
            if not settings:
                logger.info(f"Creating default settings for tenant {tenant_id}")
                settings = await self._create_default_settings(tenant_id)
                logger.info(f"Successfully created default settings for tenant {tenant_id}")

            return settings
        except Exception as e:
            logger.error(f"Failed to get or create tenant settings, tenant_id={tenant_id}, error={str(e)}", exc_info=True)
            # Re-raise as HTTPException to match the expected behavior
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail=f"Failed to get tenant settings: {str(e)}"
            )

    async def update_settings(
        self,
        tenant_id: UUID,
        updates: TenantSettingsUpdate
    ) -> TenantSettings:
        """
        Update tenant settings

        Args:
            tenant_id: UUID of the tenant
            updates: TenantSettingsUpdate object with new values

        Returns:
            Updated TenantSettings object
        """
        settings = await self.get_settings(tenant_id)

        # Update each category if provided
        if updates.procurement_settings is not None:
            settings.procurement_settings = updates.procurement_settings.dict()

        if updates.inventory_settings is not None:
            settings.inventory_settings = updates.inventory_settings.dict()

        if updates.production_settings is not None:
            settings.production_settings = updates.production_settings.dict()

        if updates.supplier_settings is not None:
            settings.supplier_settings = updates.supplier_settings.dict()

        if updates.pos_settings is not None:
            settings.pos_settings = updates.pos_settings.dict()

        if updates.order_settings is not None:
            settings.order_settings = updates.order_settings.dict()

        if updates.replenishment_settings is not None:
            settings.replenishment_settings = updates.replenishment_settings.dict()

        if updates.safety_stock_settings is not None:
            settings.safety_stock_settings = updates.safety_stock_settings.dict()

        if updates.moq_settings is not None:
            settings.moq_settings = updates.moq_settings.dict()

        if updates.supplier_selection_settings is not None:
            settings.supplier_selection_settings = updates.supplier_selection_settings.dict()

        return await self.repository.update(settings)

    async def get_category(self, tenant_id: UUID, category: str) -> Dict[str, Any]:
        """
        Get settings for a specific category

        Args:
            tenant_id: UUID of the tenant
            category: Category name (procurement, inventory, production, etc.)

        Returns:
            Dictionary with category settings

        Raises:
            HTTPException: If category is invalid
        """
        if category not in self.CATEGORY_COLUMNS:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=f"Invalid category: {category}. Valid categories: {', '.join(self.CATEGORY_COLUMNS.keys())}"
            )

        settings = await self.get_settings(tenant_id)
        column_name = self.CATEGORY_COLUMNS[category]

        return getattr(settings, column_name)

    async def update_category(
        self,
        tenant_id: UUID,
        category: str,
        updates: Dict[str, Any]
    ) -> TenantSettings:
        """
        Update settings for a specific category

        Args:
            tenant_id: UUID of the tenant
            category: Category name
            updates: Dictionary with new values

        Returns:
            Updated TenantSettings object

        Raises:
            HTTPException: If category is invalid or validation fails
        """
        if category not in self.CATEGORY_COLUMNS:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=f"Invalid category: {category}"
            )

        # Validate updates using the appropriate schema
        schema = self.CATEGORY_SCHEMAS[category]
        try:
            validated_data = schema(**updates)
        except Exception as e:
            raise HTTPException(
                status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
                detail=f"Validation error: {str(e)}"
            )

        # Get existing settings and update the category
        settings = await self.get_settings(tenant_id)
        column_name = self.CATEGORY_COLUMNS[category]
        setattr(settings, column_name, validated_data.dict())

        return await self.repository.update(settings)

    async def reset_category(self, tenant_id: UUID, category: str) -> Dict[str, Any]:
        """
        Reset a category to default values

        Args:
            tenant_id: UUID of the tenant
            category: Category name

        Returns:
            Dictionary with reset category settings

        Raises:
            HTTPException: If category is invalid
        """
        if category not in self.CATEGORY_COLUMNS:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=f"Invalid category: {category}"
            )

        # Get default settings for the category
        defaults = TenantSettings.get_default_settings()
        column_name = self.CATEGORY_COLUMNS[category]
        default_category_settings = defaults[column_name]

        # Update the category with defaults
        settings = await self.get_settings(tenant_id)
        setattr(settings, column_name, default_category_settings)

        await self.repository.update(settings)

        return default_category_settings

    async def _create_default_settings(self, tenant_id: UUID) -> TenantSettings:
        """
        Create default settings for a new tenant

        Args:
            tenant_id: UUID of the tenant

        Returns:
            Newly created TenantSettings object
        """
        defaults = TenantSettings.get_default_settings()

        settings = TenantSettings(
            tenant_id=tenant_id,
            procurement_settings=defaults["procurement_settings"],
            inventory_settings=defaults["inventory_settings"],
            production_settings=defaults["production_settings"],
            supplier_settings=defaults["supplier_settings"],
            pos_settings=defaults["pos_settings"],
            order_settings=defaults["order_settings"],
            replenishment_settings=defaults["replenishment_settings"],
            safety_stock_settings=defaults["safety_stock_settings"],
            moq_settings=defaults["moq_settings"],
            supplier_selection_settings=defaults["supplier_selection_settings"]
        )

        return await self.repository.create(settings)

    async def delete_settings(self, tenant_id: UUID) -> None:
        """
        Delete tenant settings (used when tenant is deleted)

        Args:
            tenant_id: UUID of the tenant
        """
        await self.repository.delete(tenant_id)
