"""
Enhanced Tenant Service
Business logic layer using repository pattern for tenant operations
"""

import structlog
from datetime import datetime, timezone
from typing import Optional, List, Dict, Any
from sqlalchemy.ext.asyncio import AsyncSession
from fastapi import HTTPException, status

from app.repositories import TenantRepository, TenantMemberRepository, SubscriptionRepository
from app.models.tenants import Tenant, TenantMember, Subscription
from app.schemas.tenants import (
    BakeryRegistration, TenantResponse, TenantAccessResponse,
    TenantUpdate, TenantMemberResponse
)
from shared.database.exceptions import DatabaseError, ValidationError, DuplicateRecordError
from shared.database.base import create_database_manager
from shared.database.unit_of_work import UnitOfWork
from shared.clients.nominatim_client import NominatimClient

logger = structlog.get_logger()


class EnhancedTenantService:
    """Enhanced tenant management business logic using repository pattern with dependency injection"""

    def __init__(self, database_manager=None, event_publisher=None):
        self.database_manager = database_manager or create_database_manager()
        self.event_publisher = event_publisher

    async def _init_repositories(self, session):
        """Initialize repositories with session"""
        self.tenant_repo = TenantRepository(Tenant, session)
        self.member_repo = TenantMemberRepository(TenantMember, session)
        self.subscription_repo = SubscriptionRepository(Subscription, session)
        return {
            'tenant': self.tenant_repo,
            'member': self.member_repo,
            'subscription': self.subscription_repo
        }

    async def create_bakery(
        self,
        bakery_data: BakeryRegistration,
        owner_id: str,
        session=None
    ) -> TenantResponse:
        """Create a new bakery/tenant with enhanced validation and features using repository pattern"""

        try:
            async with self.database_manager.get_session() as db_session:
                async with UnitOfWork(db_session) as uow:
                    # Register repositories
                    tenant_repo = uow.register_repository("tenants", TenantRepository, Tenant)
                    member_repo = uow.register_repository("members", TenantMemberRepository, TenantMember)
                    subscription_repo = uow.register_repository("subscriptions", SubscriptionRepository, Subscription)

                    # Geocode address using Nominatim
                    latitude = getattr(bakery_data, 'latitude', None)
                    longitude = getattr(bakery_data, 'longitude', None)

                    if not latitude or not longitude:
                        try:
                            from app.core.config import settings
                            nominatim_client = NominatimClient(settings)

                            location = await nominatim_client.geocode_address(
                                street=bakery_data.address,
                                city=bakery_data.city,
                                postal_code=bakery_data.postal_code,
                                country="Spain"
                            )

                            if location:
                                latitude = float(location["lat"])
                                longitude = float(location["lon"])
                                logger.info(
                                    "Address geocoded successfully",
                                    address=bakery_data.address,
                                    city=bakery_data.city,
                                    latitude=latitude,
                                    longitude=longitude
                                )
                            else:
                                logger.warning(
                                    "Could not geocode address, using default Madrid coordinates",
                                    address=bakery_data.address,
                                    city=bakery_data.city
                                )
                                latitude = 40.4168
                                longitude = -3.7038

                        except Exception as e:
                            logger.warning(
                                "Geocoding failed, using default coordinates",
                                address=bakery_data.address,
                                error=str(e)
                            )
                            latitude = 40.4168
                            longitude = -3.7038

                    # Prepare tenant data
                    tenant_data = {
                        "name": bakery_data.name,
                "business_type": bakery_data.business_type,
                "address": bakery_data.address,
                "city": bakery_data.city,
                "postal_code": bakery_data.postal_code,
                "phone": bakery_data.phone,
                "owner_id": owner_id,
                "email": getattr(bakery_data, 'email', None),
                "latitude": latitude,
                "longitude": longitude,
                "is_active": True
            }

            # Create tenant using repository
            tenant = await tenant_repo.create_tenant(tenant_data)

            # Create owner membership
            membership_data = {
                "tenant_id": str(tenant.id),
                "user_id": owner_id,
                "role": "owner",
                "is_active": True
            }

            owner_membership = await member_repo.create_membership(membership_data)

            # Get subscription plan from user's registration using standardized auth client
            selected_plan = "starter"  # Default fallback
            try:
                from shared.clients.auth_client import AuthServiceClient
                from app.core.config import settings

                auth_client = AuthServiceClient(settings)
                selected_plan = await auth_client.get_subscription_plan_from_registration(owner_id)

                logger.info("Retrieved subscription plan from registration",
                           tenant_id=tenant.id,
                           owner_id=owner_id,
                           plan=selected_plan)

            except Exception as e:
                logger.warning("Could not retrieve subscription plan from auth service, using default",
                             error=str(e),
                             owner_id=owner_id,
                             default_plan=selected_plan)

            # Create subscription with selected or default plan
            # When tenant_id is set, is_tenant_linked must be True (database constraint)
            subscription_data = {
                "tenant_id": str(tenant.id),
                "plan": selected_plan,
                "status": "active",
                "is_tenant_linked": True,  # Required when tenant_id is set
                "tenant_linking_status": "completed"  # Mark as completed since tenant is already created
            }

            subscription = await subscription_repo.create_subscription(subscription_data)

            logger.info("Subscription created",
                       tenant_id=tenant.id,
                       plan=selected_plan)

            # Commit the transaction
            await uow.commit()

            # Publish tenant created event
            if self.event_publisher:
                try:
                    await self.event_publisher.publish_business_event(
                        event_type="tenant.created",
                        tenant_id=str(tenant.id),
                        data={
                            "tenant_id": str(tenant.id),
                            "owner_id": owner_id,
                            "name": bakery_data.name,
                            "created_at": datetime.now(timezone.utc).isoformat()
                        }
                    )
                except Exception as e:
                    logger.warning("Failed to publish tenant created event", error=str(e))

            # Automatically create location-context with city information
            # This is non-blocking - failure won't prevent tenant creation
            try:
                from shared.clients.external_client import ExternalServiceClient
                from shared.utils.city_normalization import normalize_city_id
                from app.core.config import settings

                external_client = ExternalServiceClient(settings, "tenant")
                city_id = normalize_city_id(bakery_data.city)

                if city_id:
                    await external_client.create_tenant_location_context(
                        tenant_id=str(tenant.id),
                        city_id=city_id,
                        notes="Auto-created during tenant registration"
                    )
                    logger.info(
                        "Automatically created location-context",
                        tenant_id=str(tenant.id),
                        city_id=city_id
                    )
                else:
                    logger.warning(
                        "Could not normalize city for location-context",
                        tenant_id=str(tenant.id),
                        city=bakery_data.city
                    )
            except Exception as e:
                logger.warning(
                    "Failed to auto-create location-context (non-blocking)",
                    tenant_id=str(tenant.id),
                    city=bakery_data.city,
                    error=str(e)
                )
                # Don't fail tenant creation if location-context creation fails

            # Update user's tenant_id in auth service
            try:
                from shared.clients.auth_client import AuthServiceClient
                from app.core.config import settings

                auth_client = AuthServiceClient(settings)
                await auth_client.update_user_tenant_id(owner_id, str(tenant.id))

                logger.info("Updated user tenant_id in auth service",
                           user_id=owner_id,
                           tenant_id=str(tenant.id))
            except Exception as e:
                logger.error("Failed to update user tenant_id (non-blocking)",
                           user_id=owner_id,
                           tenant_id=str(tenant.id),
                           error=str(e))
                # Don't fail tenant creation if user update fails

            logger.info("Bakery created successfully",
                       tenant_id=tenant.id,
                       name=bakery_data.name,
                       owner_id=owner_id,
                       subdomain=tenant.subdomain)

            return TenantResponse.from_orm(tenant)

        except (ValidationError, DuplicateRecordError) as e:
            logger.error("Validation error creating bakery",
                        name=bakery_data.name,
                        owner_id=owner_id,
                        error=str(e))
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=str(e)
            )
        except Exception as e:
            logger.error("Error creating bakery",
                        name=bakery_data.name,
                        owner_id=owner_id,
                        error=str(e))
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="Failed to create bakery"
            )

    async def verify_user_access(
        self,
        user_id: str,
        tenant_id: str
    ) -> TenantAccessResponse:
        """Verify if user has access to tenant with enhanced permissions"""

        try:
            async with self.database_manager.get_session() as db_session:
                await self._init_repositories(db_session)
                access_info = await self.member_repo.verify_user_access(user_id, tenant_id)

                return TenantAccessResponse(
                    has_access=access_info["has_access"],
                    role=access_info["role"],
                    permissions=access_info["permissions"],
                    membership_id=access_info.get("membership_id"),
                    joined_at=access_info.get("joined_at")
                )

        except Exception as e:
            logger.error("Error verifying user access",
                        user_id=user_id,
                        tenant_id=tenant_id,
                        error=str(e))
            return TenantAccessResponse(
                has_access=False,
                role="none",
                permissions=[]
            )

    async def get_tenant_by_id(self, tenant_id: str) -> Optional[TenantResponse]:
        """Get tenant by ID with enhanced data"""

        try:
            async with self.database_manager.get_session() as db_session:
                await self._init_repositories(db_session)
                tenant = await self.tenant_repo.get_by_id(tenant_id)
                if tenant:
                    return TenantResponse.from_orm(tenant)
                return None

        except Exception as e:
            logger.error("Error getting tenant",
                        tenant_id=tenant_id,
                        error=str(e))
            return None

    async def get_tenant_by_subdomain(self, subdomain: str) -> Optional[TenantResponse]:
        """Get tenant by subdomain"""

        try:
            async with self.database_manager.get_session() as db_session:
                await self._init_repositories(db_session)
                tenant = await self.tenant_repo.get_by_subdomain(subdomain)
                if tenant:
                    return TenantResponse.from_orm(tenant)
                return None

        except Exception as e:
            logger.error("Error getting tenant by subdomain",
                        subdomain=subdomain,
                        error=str(e))
            return None

    async def get_user_tenants(self, user_id: str) -> List[TenantResponse]:
        """Get all tenants accessible by a user (both owned and member tenants)"""

        try:
            async with self.database_manager.get_session() as db_session:
                await self._init_repositories(db_session)

                # Get tenants where user is the owner
                owned_tenants = await self.tenant_repo.get_tenants_by_owner(user_id)

                # Get tenants where user is a member (but not owner)
                memberships = await self.member_repo.get_user_memberships(user_id, active_only=True)

                # Get tenant details for each membership
                member_tenant_ids = [str(membership.tenant_id) for membership in memberships]
                member_tenants = []

                if member_tenant_ids:
                    # Get tenant details for each membership
                    for tenant_id in member_tenant_ids:
                        tenant = await self.tenant_repo.get_by_id(tenant_id)
                        if tenant:
                            member_tenants.append(tenant)

                # Combine and deduplicate (in case user is both owner and member)
                all_tenants = owned_tenants + member_tenants

                # Remove duplicates by tenant ID
                unique_tenants = []
                seen_ids = set()
                for tenant in all_tenants:
                    if str(tenant.id) not in seen_ids:
                        seen_ids.add(str(tenant.id))
                        unique_tenants.append(tenant)

                logger.info(
                    "Retrieved user tenants",
                    user_id=user_id,
                    owned_count=len(owned_tenants),
                    member_count=len(member_tenants),
                    total_count=len(unique_tenants)
                )

                return [TenantResponse.from_orm(tenant) for tenant in unique_tenants]

        except Exception as e:
            logger.error("Error getting user tenants",
                        user_id=user_id,
                        error=str(e))
            return []

    async def get_virtual_tenants_for_session(self, demo_session_id: str, demo_account_type: str) -> List[TenantResponse]:
        """
        Get virtual tenants associated with a specific demo session.
        This method handles the special demo session access patterns:
        - Individual bakery demo user: should have access to professional demo tenant (1 tenant)
        - Enterprise demo session: should have access to parent tenant and its children (4 tenants)

        Now properly filters by demo_session_id field which is populated during tenant cloning.
        """
        try:
            async with self.database_manager.get_session() as db_session:
                await self._init_repositories(db_session)

                # Query all tenants by demo_session_id (now properly populated during cloning)
                virtual_tenants = await self.tenant_repo.get_tenants_by_session_id(demo_session_id)

                if not virtual_tenants:
                    logger.warning(
                        "No virtual tenants found for demo session - session may not exist or tenants not yet created",
                        demo_session_id=demo_session_id,
                        demo_account_type=demo_account_type
                    )
                    return []

                logger.info(
                    "Retrieved virtual tenants for demo session",
                    demo_session_id=demo_session_id,
                    demo_account_type=demo_account_type,
                    tenant_count=len(virtual_tenants)
                )

                return [TenantResponse.from_orm(tenant) for tenant in virtual_tenants]

        except Exception as e:
            logger.error("Error getting virtual tenants for demo session",
                        demo_session_id=demo_session_id,
                        demo_account_type=demo_account_type,
                        error=str(e))
            # Fallback: return empty list instead of all demo tenants
            return []

    async def get_demo_tenants_by_session_type(self, demo_account_type: str, current_user_id: str) -> List[TenantResponse]:
        """
        DEPRECATED: Fallback method for old demo sessions without demo_session_id.

        Get demo tenants based on session type rather than user ownership.
        This implements the specific requirements:
        - Individual bakery demo user: access to professional demo tenant
        - Enterprise demo session: access only to enterprise parent tenant and its child

        WARNING: This method returns ALL demo tenants of a given type, not session-specific ones.
        New code should use get_virtual_tenants_for_session() instead.
        """
        logger.warning(
            "Using deprecated fallback method - demo_session_id not available",
            demo_account_type=demo_account_type,
            user_id=current_user_id
        )

        try:
            async with self.database_manager.get_session() as db_session:
                await self._init_repositories(db_session)

                if demo_account_type.lower() == 'professional_bakery':
                    # Individual bakery demo user should have access to professional demo tenant
                    # Return demo tenants with business_model='professional_bakery' that are demo tenants
                    tenants = await self.tenant_repo.get_multi(
                        filters={
                            "business_model": "professional_bakery",
                            "is_demo": True,
                            "is_active": True
                        }
                    )
                elif demo_account_type.lower() in ['enterprise_chain', 'enterprise_parent']:
                    # Enterprise demo session should have access to parent tenant and its children
                    # Return demo tenants with tenant_type in ['parent', 'child'] that are demo tenants
                    parent_tenants = await self.tenant_repo.get_multi(
                        filters={
                            "tenant_type": "parent",
                            "is_demo": True,
                            "is_active": True
                        }
                    )
                    child_tenants = await self.tenant_repo.get_multi(
                        filters={
                            "tenant_type": "child",
                            "is_demo": True,
                            "is_active": True
                        }
                    )
                    tenants = parent_tenants + child_tenants
                elif demo_account_type.lower() == 'enterprise_child':
                    # For child enterprise sessions, return only child demo tenants
                    tenants = await self.tenant_repo.get_multi(
                        filters={
                            "tenant_type": "child",
                            "is_demo": True,
                            "is_active": True
                        }
                    )
                else:
                    # Default case - return the user's actual owned tenants
                    tenants = await self.tenant_repo.get_tenants_by_owner(current_user_id)

                return [TenantResponse.from_orm(tenant) for tenant in tenants]

        except Exception as e:
            logger.error("Error getting demo tenants by session type",
                        demo_account_type=demo_account_type,
                        user_id=current_user_id,
                        error=str(e))
            # Fallback: return empty list
            return []

    async def get_active_tenants(self, skip: int = 0, limit: int = 100) -> List[TenantResponse]:
        """Get all active tenants"""

        try:
            async with self.database_manager.get_session() as db_session:
                await self._init_repositories(db_session)
                tenants = await self.tenant_repo.get_active_tenants(skip=skip, limit=limit)
                return [TenantResponse.from_orm(tenant) for tenant in tenants]

        except Exception as e:
            logger.error("Error getting active tenants",
                        skip=skip,
                        limit=limit,
                        error=str(e))
            return []

    async def search_tenants(
        self,
        search_term: str,
        business_type: str = None,
        city: str = None,
        skip: int = 0,
        limit: int = 50
    ) -> List[TenantResponse]:
        """Search tenants with filters"""

        try:
            async with self.database_manager.get_session() as db_session:
                await self._init_repositories(db_session)
                tenants = await self.tenant_repo.search_tenants(
                    search_term, business_type, city, skip, limit
                )
                return [TenantResponse.from_orm(tenant) for tenant in tenants]

        except Exception as e:
            logger.error("Error searching tenants",
                        search_term=search_term,
                        error=str(e))
            return []

    async def update_tenant(
        self,
        tenant_id: str,
        update_data: TenantUpdate,
        user_id: str,
        session: AsyncSession = None
    ) -> TenantResponse:
        """Update tenant information with permission checks"""

        try:
            # Verify user has admin access
            access = await self.verify_user_access(user_id, tenant_id)
            if not access.has_access or access.role not in ["owner", "admin"]:
                raise HTTPException(
                    status_code=status.HTTP_403_FORBIDDEN,
                    detail="Insufficient permissions to update tenant"
                )

            # Update tenant using repository with proper session management
            update_values = update_data.dict(exclude_unset=True)
            if update_values:
                async with self.database_manager.get_session() as db_session:
                    await self._init_repositories(db_session)
                    updated_tenant = await self.tenant_repo.update(tenant_id, update_values)

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

                    logger.info("Tenant updated successfully",
                               tenant_id=tenant_id,
                               updated_by=user_id,
                               fields=list(update_values.keys()))

                    return TenantResponse.from_orm(updated_tenant)

            # No updates to apply - get current tenant data
            async with self.database_manager.get_session() as db_session:
                await self._init_repositories(db_session)
                tenant = await self.tenant_repo.get_by_id(tenant_id)
                return TenantResponse.from_orm(tenant)

        except HTTPException:
            raise
        except Exception as e:
            logger.error("Error updating tenant",
                        tenant_id=tenant_id,
                        user_id=user_id,
                        error=str(e))
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="Failed to update tenant"
            )

    async def add_team_member(
        self,
        tenant_id: str,
        user_id: str,
        role: str,
        invited_by: str,
        session: AsyncSession = None
    ) -> TenantMemberResponse:
        """Add a team member to tenant with enhanced validation"""

        try:
            # Verify inviter has admin access
            access = await self.verify_user_access(invited_by, tenant_id)
            if not access.has_access or access.role not in ["owner", "admin"]:
                raise HTTPException(
                    status_code=status.HTTP_403_FORBIDDEN,
                    detail="Insufficient permissions to add team members"
                )

            # Create membership using repository
            async with self.database_manager.get_session() as db_session:
                await self._init_repositories(db_session)

                membership_data = {
                    "tenant_id": tenant_id,
                    "user_id": user_id,
                    "role": role,
                    "invited_by": invited_by,
                    "is_active": True
                }

                member = await self.member_repo.create_membership(membership_data)

                # Publish member added event
                if self.event_publisher:
                    try:
                        await self.event_publisher.publish_business_event(
                            event_type="tenant.member.added",
                            tenant_id=tenant_id,
                            data={
                                "tenant_id": tenant_id,
                                "user_id": user_id,
                                "role": role,
                                "invited_by": invited_by,
                                "added_at": datetime.now(timezone.utc).isoformat()
                            }
                        )
                    except Exception as e:
                        logger.warning("Failed to publish member added event", error=str(e))

                logger.info("Team member added successfully",
                           tenant_id=tenant_id,
                           user_id=user_id,
                           role=role,
                           invited_by=invited_by)

                return TenantMemberResponse.from_orm(member)

        except HTTPException:
            raise
        except (ValidationError, DuplicateRecordError) as e:
            logger.error("Validation error adding team member",
                        tenant_id=tenant_id,
                        user_id=user_id,
                        error=str(e))
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=str(e)
            )
        except Exception as e:
            logger.error("Error adding team member",
                        tenant_id=tenant_id,
                        user_id=user_id,
                        error=str(e))
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="Failed to add team member"
            )

    async def get_team_members(
        self,
        tenant_id: str,
        user_id: str,
        active_only: bool = True
    ) -> List[TenantMemberResponse]:
        """Get all team members for a tenant with enriched user information"""

        try:
            async with self.database_manager.get_session() as session:
                # Initialize repositories with session
                await self._init_repositories(session)

                members = await self.member_repo.get_tenant_members(
                    tenant_id, active_only=active_only, include_user_info=True
                )

                return [TenantMemberResponse.from_orm(member) for member in members]

        except HTTPException:
            raise
        except Exception as e:
            logger.error("Error getting team members",
                        tenant_id=tenant_id,
                        user_id=user_id,
                        error=str(e))
            return []

    async def update_member_role(
        self,
        tenant_id: str,
        member_user_id: str,
        new_role: str,
        updated_by: str,
        session: AsyncSession = None
    ) -> TenantMemberResponse:
        """Update team member role"""

        try:
            # Verify updater has admin access
            access = await self.verify_user_access(updated_by, tenant_id)
            if not access.has_access or access.role not in ["owner", "admin"]:
                raise HTTPException(
                    status_code=status.HTTP_403_FORBIDDEN,
                    detail="Insufficient permissions to update member roles"
                )

            updated_member = await self.member_repo.update_member_role(
                tenant_id, member_user_id, new_role, updated_by
            )

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

            return TenantMemberResponse.from_orm(updated_member)

        except HTTPException:
            raise
        except (ValidationError, DuplicateRecordError) as e:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=str(e)
            )
        except Exception as e:
            logger.error("Error updating member role",
                        tenant_id=tenant_id,
                        member_user_id=member_user_id,
                        error=str(e))
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="Failed to update member role"
            )

    async def remove_team_member(
        self,
        tenant_id: str,
        member_user_id: str,
        removed_by: str,
        session: AsyncSession = None
    ) -> bool:
        """Remove team member from tenant"""

        try:
            # Verify remover has admin access
            access = await self.verify_user_access(removed_by, tenant_id)
            if not access.has_access or access.role not in ["owner", "admin"]:
                raise HTTPException(
                    status_code=status.HTTP_403_FORBIDDEN,
                    detail="Insufficient permissions to remove team members"
                )

            removed_member = await self.member_repo.deactivate_membership(
                tenant_id, member_user_id, removed_by
            )

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

            return True

        except HTTPException:
            raise
        except ValidationError as e:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=str(e)
            )
        except Exception as e:
            logger.error("Error removing team member",
                        tenant_id=tenant_id,
                        member_user_id=member_user_id,
                        error=str(e))
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="Failed to remove team member"
            )

    async def get_user_memberships(self, user_id: str) -> List[Dict[str, Any]]:
        """Get all tenant memberships for a user (for authentication service)"""
        try:
            async with self.database_manager.get_session() as db_session:
                await self._init_repositories(db_session)

                # Get all user memberships
                memberships = await self.member_repo.get_user_memberships(user_id, active_only=False)

                # Convert to response format
                result = []
                for membership in memberships:
                    result.append({
                        "id": str(membership.id),
                        "tenant_id": str(membership.tenant_id),
                        "user_id": str(membership.user_id),
                        "role": membership.role,
                        "is_active": membership.is_active,
                        "joined_at": membership.joined_at.isoformat() if membership.joined_at else None,
                        "invited_by": str(membership.invited_by) if membership.invited_by else None
                    })

                logger.info("Retrieved user memberships",
                           user_id=user_id,
                           membership_count=len(result))

                return result

        except Exception as e:
            logger.error("Failed to get user memberships",
                        user_id=user_id,
                        error=str(e))
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="Failed to get user memberships"
            )

    async def update_model_status(
        self,
        tenant_id: str,
        ml_model_trained: bool,
        user_id: str,
        last_training_date: datetime = None
    ) -> TenantResponse:
        """Update tenant model training status"""

        try:
            # Verify user has access
            access = await self.verify_user_access(user_id, tenant_id)
            if not access.has_access:
                raise HTTPException(
                    status_code=status.HTTP_403_FORBIDDEN,
                    detail="Access denied to tenant"
                )

            async with self.database_manager.get_session() as db_session:
                await self._init_repositories(db_session)
                updated_tenant = await self.tenant_repo.update_tenant_model_status(
                    tenant_id, ml_model_trained, last_training_date
                )

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

                return TenantResponse.from_orm(updated_tenant)

        except HTTPException:
            raise
        except Exception as e:
            logger.error("Error updating model status",
                        tenant_id=tenant_id,
                        error=str(e))
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="Failed to update model status"
            )

    async def get_tenant_statistics(self) -> Dict[str, Any]:
        """Get comprehensive tenant statistics"""

        try:
            async with self.database_manager.get_session() as db_session:
                await self._init_repositories(db_session)
                # Get tenant statistics
                tenant_stats = await self.tenant_repo.get_tenant_statistics()

                # Get subscription statistics
                subscription_stats = await self.subscription_repo.get_subscription_statistics()

                return {
                    "tenants": tenant_stats,
                    "subscriptions": subscription_stats
                }

        except Exception as e:
            logger.error(f"Error getting tenant statistics: {str(e)}")
            return {
                "tenants": {},
                "subscriptions": {}
            }

    async def get_tenants_near_location(
        self,
        latitude: float,
        longitude: float,
        radius_km: float = 10.0,
        limit: int = 50
    ) -> List[TenantResponse]:
        """Get tenants near a geographic location"""

        try:
            async with self.database_manager.get_session() as db_session:
                await self._init_repositories(db_session)
                tenants = await self.tenant_repo.get_tenants_by_location(
                    latitude, longitude, radius_km, limit
                )

                return [TenantResponse.from_orm(tenant) for tenant in tenants]

        except Exception as e:
            logger.error("Error getting tenants by location",
                        latitude=latitude,
                        longitude=longitude,
                        error=str(e))
            return []

    async def deactivate_tenant(
        self,
        tenant_id: str,
        user_id: str,
        session: AsyncSession = None
    ) -> bool:
        """Deactivate a tenant (admin only)"""

        try:
            # Verify user is owner
            access = await self.verify_user_access(user_id, tenant_id)
            if not access.has_access or access.role != "owner":
                raise HTTPException(
                    status_code=status.HTTP_403_FORBIDDEN,
                    detail="Only tenant owner can deactivate tenant"
                )

            deactivated_tenant = await self.tenant_repo.deactivate_tenant(tenant_id)

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

            # Also suspend subscription
            subscription = await self.subscription_repo.get_active_subscription(tenant_id)
            if subscription:
                await self.subscription_repo.suspend_subscription(
                    str(subscription.id),
                    "Tenant deactivated"
                )

            logger.info("Tenant deactivated",
                       tenant_id=tenant_id,
                       deactivated_by=user_id)

            return True

        except HTTPException:
            raise
        except Exception as e:
            logger.error("Error deactivating tenant",
                        tenant_id=tenant_id,
                        error=str(e))
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="Failed to deactivate tenant"
            )

    async def activate_tenant(
        self,
        tenant_id: str,
        user_id: str,
        session: AsyncSession = None
    ) -> bool:
        """Activate a previously deactivated tenant (admin only)"""

        try:
            # Verify user is owner
            access = await self.verify_user_access(user_id, tenant_id)
            if not access.has_access or access.role != "owner":
                raise HTTPException(
                    status_code=status.HTTP_403_FORBIDDEN,
                    detail="Only tenant owner can activate tenant"
                )

            activated_tenant = await self.tenant_repo.activate_tenant(tenant_id)

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

            # Also reactivate subscription if exists
            subscription = await self.subscription_repo.get_subscription_by_tenant(tenant_id)
            if subscription and subscription.status == "suspended":
                await self.subscription_repo.reactivate_subscription(str(subscription.id))

            logger.info("Tenant activated",
                       tenant_id=tenant_id,
                       activated_by=user_id)

            return True

        except HTTPException:
            raise
        except Exception as e:
            logger.error("Error activating tenant",
                        tenant_id=tenant_id,
                        error=str(e))
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="Failed to activate tenant"
            )

    async def delete_tenant(
        self,
        tenant_id: str,
        requesting_user_id: str = None,
        skip_admin_check: bool = False
    ) -> Dict[str, Any]:
        """
        Permanently delete a tenant and all its associated data

        Args:
            tenant_id: The tenant to delete
            requesting_user_id: The user requesting deletion (for permission check)
            skip_admin_check: Skip the admin check (for internal service calls)

        Returns:
            Dict with deletion summary
        """

        try:
            async with self.database_manager.get_session() as db_session:
                await self._init_repositories(db_session)

                # Get tenant first to verify it exists
                tenant = await self.tenant_repo.get_by_id(tenant_id)
                if not tenant:
                    raise HTTPException(
                        status_code=status.HTTP_404_NOT_FOUND,
                        detail="Tenant not found"
                    )

                # Permission check (unless internal service call)
                if not skip_admin_check:
                    if not requesting_user_id:
                        raise HTTPException(
                            status_code=status.HTTP_400_BAD_REQUEST,
                            detail="User ID required for deletion authorization"
                        )

                    access = await self.verify_user_access(requesting_user_id, tenant_id)
                    if not access.has_access or access.role not in ["owner", "admin"]:
                        raise HTTPException(
                            status_code=status.HTTP_403_FORBIDDEN,
                            detail="Only tenant owner or admin can delete tenant"
                        )

                # Check if there are other admins (protection against accidental deletion)
                admin_members = await self.member_repo.get_tenant_members(
                    tenant_id,
                    active_only=True,
                    role=None  # Get all roles, we'll filter
                )

                admin_count = sum(1 for m in admin_members if m.role in ["owner", "admin"])

                # Build deletion summary
                deletion_summary = {
                    "tenant_id": tenant_id,
                    "tenant_name": tenant.name,
                    "admin_count": admin_count,
                    "total_members": len(admin_members),
                    "deleted_items": {},
                    "errors": []
                }

                # Cancel active subscriptions first
                try:
                    subscription = await self.subscription_repo.get_active_subscription(tenant_id)
                    if subscription:
                        await self.subscription_repo.cancel_subscription(
                            str(subscription.id),
                            reason="Tenant deleted"
                        )
                        deletion_summary["deleted_items"]["subscriptions"] = 1
                except Exception as e:
                    logger.warning("Failed to cancel subscription during tenant deletion",
                                 tenant_id=tenant_id,
                                 error=str(e))
                    deletion_summary["errors"].append(f"Subscription cancellation: {str(e)}")

                # Delete all tenant memberships (CASCADE will handle this, but we do it explicitly)
                try:
                    deleted_members = 0
                    for member in admin_members:
                        try:
                            await self.member_repo.delete(str(member.id))
                            deleted_members += 1
                        except Exception as e:
                            logger.warning("Failed to delete membership",
                                         membership_id=member.id,
                                         error=str(e))

                    deletion_summary["deleted_items"]["memberships"] = deleted_members
                except Exception as e:
                    logger.warning("Failed to delete memberships during tenant deletion",
                                 tenant_id=tenant_id,
                                 error=str(e))
                    deletion_summary["errors"].append(f"Membership deletion: {str(e)}")

                # Finally, delete the tenant itself (CASCADE should handle related records)
                try:
                    await self.tenant_repo.delete(tenant_id)
                    deletion_summary["deleted_items"]["tenant"] = 1
                except Exception as e:
                    logger.error("Failed to delete tenant record",
                               tenant_id=tenant_id,
                               error=str(e))
                    raise HTTPException(
                        status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                        detail=f"Failed to delete tenant: {str(e)}"
                    )

                # Publish deletion event for other services using unified messaging
                try:
                    from shared.messaging import initialize_service_publisher, EVENT_TYPES
                    from app.core.config import settings

                    # Create a temporary publisher to send the event using the unified helper
                    temp_rabbitmq_client, temp_publisher = await initialize_service_publisher("tenant-service", settings.RABBITMQ_URL)
                    if temp_publisher:
                        try:
                            await temp_publisher.publish_business_event(
                                event_type=EVENT_TYPES.TENANT.TENANT_DELETED,
                                tenant_id=tenant_id,
                                data={
                                    "tenant_id": tenant_id,
                                    "tenant_name": tenant.name,
                                    "deleted_at": datetime.now(timezone.utc).isoformat()
                                }
                            )
                        finally:
                            if temp_rabbitmq_client:
                                await temp_rabbitmq_client.disconnect()
                    else:
                        logger.warning("Could not connect to RabbitMQ to publish tenant deletion event")
                except Exception as e:
                    logger.warning("Failed to publish tenant deletion event",
                                 tenant_id=tenant_id,
                                 error=str(e))
                    deletion_summary["errors"].append(f"Event publishing: {str(e)}")

                logger.info("Tenant deleted successfully",
                           tenant_id=tenant_id,
                           tenant_name=tenant.name,
                           deleted_by=requesting_user_id,
                           summary=deletion_summary)

                return deletion_summary

        except HTTPException:
            raise
        except Exception as e:
            logger.error("Error deleting tenant",
                        tenant_id=tenant_id,
                        error=str(e))
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail=f"Failed to delete tenant: {str(e)}"
            )

    async def delete_user_memberships(
        self,
        user_id: str
    ) -> Dict[str, Any]:
        """
        Delete all tenant memberships for a user
        Used when deleting a user from the auth service

        Args:
            user_id: The user whose memberships should be deleted

        Returns:
            Dict with deletion summary
        """

        try:
            async with self.database_manager.get_session() as db_session:
                await self._init_repositories(db_session)

                # Get all user memberships
                memberships = await self.member_repo.get_user_memberships(user_id, active_only=False)

                deleted_count = 0
                errors = []

                for membership in memberships:
                    try:
                        # Delete the membership
                        await self.member_repo.delete(str(membership.id))
                        deleted_count += 1
                    except Exception as e:
                        logger.warning("Failed to delete membership",
                                     membership_id=membership.id,
                                     user_id=user_id,
                                     tenant_id=membership.tenant_id,
                                     error=str(e))
                        errors.append(f"Membership {membership.id}: {str(e)}")

                logger.info("User memberships deleted",
                           user_id=user_id,
                           total_memberships=len(memberships),
                           deleted_count=deleted_count,
                           errors=len(errors))

                return {
                    "user_id": user_id,
                    "total_memberships": len(memberships),
                    "deleted_count": deleted_count,
                    "errors": errors
                }

        except Exception as e:
            logger.error("Error deleting user memberships",
                        user_id=user_id,
                        error=str(e))
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail=f"Failed to delete user memberships: {str(e)}"
            )

    async def transfer_tenant_ownership(
        self,
        tenant_id: str,
        current_owner_id: str,
        new_owner_id: str,
        requesting_user_id: str = None
    ) -> TenantResponse:
        """
        Transfer tenant ownership to another admin

        Args:
            tenant_id: The tenant whose ownership to transfer
            current_owner_id: Current owner (for verification)
            new_owner_id: New owner (must be an existing admin)
            requesting_user_id: User requesting the transfer (for permission check)

        Returns:
            Updated tenant
        """

        try:
            async with self.database_manager.get_session() as db_session:
                async with UnitOfWork(db_session) as uow:
                    # Register repositories
                    tenant_repo = uow.register_repository("tenants", TenantRepository, Tenant)
                    member_repo = uow.register_repository("members", TenantMemberRepository, TenantMember)

                    # Get tenant
                    tenant = await tenant_repo.get_by_id(tenant_id)
                    if not tenant:
                        raise HTTPException(
                            status_code=status.HTTP_404_NOT_FOUND,
                            detail="Tenant not found"
                        )

                    # Verify current ownership
                    if str(tenant.owner_id) != current_owner_id:
                        raise HTTPException(
                            status_code=status.HTTP_400_BAD_REQUEST,
                            detail="Current owner ID does not match"
                        )

                    # Permission check (must be current owner or system)
                    if requesting_user_id and requesting_user_id != current_owner_id:
                        access = await self.verify_user_access(requesting_user_id, tenant_id)
                        if not access.has_access or access.role != "owner":
                            raise HTTPException(
                                status_code=status.HTTP_403_FORBIDDEN,
                                detail="Only current owner can transfer ownership"
                            )

                    # Verify new owner is an admin
                    new_owner_membership = await member_repo.get_membership(tenant_id, new_owner_id)
                    if not new_owner_membership or not new_owner_membership.is_active:
                        raise HTTPException(
                            status_code=status.HTTP_400_BAD_REQUEST,
                            detail="New owner must be an active member of the tenant"
                        )

                    if new_owner_membership.role not in ["admin", "owner"]:
                        raise HTTPException(
                            status_code=status.HTTP_400_BAD_REQUEST,
                            detail="New owner must be an admin"
                        )

                    # Update tenant owner
                    updated_tenant = await tenant_repo.update(tenant_id, {
                        "owner_id": new_owner_id
                    })

                    # Update memberships: current owner -> admin, new owner -> owner
                    current_owner_membership = await member_repo.get_membership(tenant_id, current_owner_id)
                    if current_owner_membership:
                        await member_repo.update_member_role(tenant_id, current_owner_id, "admin")

                    await member_repo.update_member_role(tenant_id, new_owner_id, "owner")

                    # Commit transaction
                    await uow.commit()

                    logger.info("Tenant ownership transferred",
                               tenant_id=tenant_id,
                               from_owner=current_owner_id,
                               to_owner=new_owner_id,
                               requested_by=requesting_user_id)

                    return TenantResponse.from_orm(updated_tenant)

        except HTTPException:
            raise
        except Exception as e:
            logger.error("Error transferring tenant ownership",
                        tenant_id=tenant_id,
                        error=str(e))
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail=f"Failed to transfer ownership: {str(e)}"
            )

    async def get_tenant_admins(
        self,
        tenant_id: str
    ) -> List[TenantMemberResponse]:
        """
        Get all admins (owner + admins) for a tenant
        Used by auth service to check for other admins before tenant deletion

        Args:
            tenant_id: The tenant to query

        Returns:
            List of admin members
        """

        try:
            async with self.database_manager.get_session() as db_session:
                await self._init_repositories(db_session)

                # Get all active members
                all_members = await self.member_repo.get_tenant_members(
                    tenant_id,
                    active_only=True,
                    include_user_info=True
                )

                # Filter to just admins and owner
                admin_members = [m for m in all_members if m.role in ["owner", "admin"]]

                return [TenantMemberResponse.from_orm(m) for m in admin_members]

        except Exception as e:
            logger.error("Error getting tenant admins",
                        tenant_id=tenant_id,
                        error=str(e))
            return []


    # ========================================================================
    # TENANT-INDEPENDENT SUBSCRIPTION METHODS (New Architecture)
    # ========================================================================

    async def link_subscription_to_tenant(
        self,
        tenant_id: str,
        subscription_id: str,
        user_id: str
    ) -> Dict[str, Any]:
        """
        Link a pending subscription to a tenant

        This completes the registration flow by associating the subscription
        created during registration with the tenant created during onboarding

        Args:
            tenant_id: Tenant ID to link to
            subscription_id: Subscription ID to link
            user_id: User ID performing the linking (for validation)

        Returns:
            Dictionary with linking results
        """
        try:
            async with self.database_manager.get_session() as db_session:
                async with UnitOfWork(db_session) as uow:
                    # Register repositories
                    subscription_repo = uow.register_repository(
                        "subscriptions", SubscriptionRepository, Subscription
                    )
                    tenant_repo = uow.register_repository(
                        "tenants", TenantRepository, Tenant
                    )

                    # Get the subscription
                    subscription = await subscription_repo.get_by_id(subscription_id)

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

                    # Verify subscription is in pending_tenant_linking state
                    if subscription.tenant_linking_status != "pending":
                        raise HTTPException(
                            status_code=status.HTTP_400_BAD_REQUEST,
                            detail="Subscription is not in pending tenant linking state"
                        )

                    # Verify subscription belongs to this user
                    if subscription.user_id != user_id:
                        raise HTTPException(
                            status_code=status.HTTP_403_FORBIDDEN,
                            detail="Subscription does not belong to this user"
                        )

                    # Update subscription with tenant_id
                    update_data = {
                        "tenant_id": tenant_id,
                        "is_tenant_linked": True,
                        "tenant_linking_status": "completed",
                        "linked_at": datetime.now(timezone.utc)
                    }

                    await subscription_repo.update(subscription_id, update_data)

                    # Update tenant with subscription information including 3DS flags
                    tenant_update = {
                        "customer_id": subscription.customer_id,
                        "subscription_status": subscription.status,
                        "subscription_plan": subscription.plan,
                        "subscription_tier": subscription.plan,
                        "billing_cycle": subscription.billing_cycle,
                        "trial_period_days": subscription.trial_period_days,
                        "threeds_authentication_required": getattr(subscription, 'threeds_authentication_required', False),
                        "threeds_authentication_required_at": getattr(subscription, 'threeds_authentication_required_at', None),
                        "threeds_authentication_completed": getattr(subscription, 'threeds_authentication_completed', False),
                        "threeds_authentication_completed_at": getattr(subscription, 'threeds_authentication_completed_at', None),
                        "last_threeds_setup_intent_id": getattr(subscription, 'last_threeds_setup_intent_id', None),
                        "threeds_action_type": getattr(subscription, 'threeds_action_type', None)
                    }

                    await tenant_repo.update(tenant_id, tenant_update)

                    # Commit transaction
                    await uow.commit()

                    logger.info("Subscription successfully linked to tenant",
                               tenant_id=tenant_id,
                               subscription_id=subscription_id,
                               user_id=user_id)
                    return {
                        "success": True,
                        "tenant_id": tenant_id,
                        "subscription_id": subscription_id,
                        "status": "linked"
                    }

        except Exception as e:
            logger.error("Failed to link subscription to tenant",
                        error=str(e),
                        tenant_id=tenant_id,
                        subscription_id=subscription_id,
                        user_id=user_id)
            raise

    async def update_tenant_subscription_info(
        self,
        tenant_id: str,
        update_data: Dict[str, Any]
    ) -> TenantResponse:
        """
        Update tenant subscription-related information (plan, status, 3DS flags)

        Args:
            tenant_id: Tenant ID
            update_data: Dictionary with fields to update

        Returns:
            Updated Tenant object
        """
        try:
            async with self.database_manager.get_session() as db_session:
                await self._init_repositories(db_session)

                # Filter allowed fields to prevent accidental overwrites of core tenant data
                allowed_fields = {
                    'subscription_plan', 'subscription_status', 'subscription_tier',
                    'billing_cycle', 'trial_period_days', 'customer_id',
                    'threeds_authentication_required', 'threeds_authentication_required_at',
                    'threeds_authentication_completed', 'threeds_authentication_completed_at',
                    'last_threeds_setup_intent_id', 'threeds_action_type'
                }

                filtered_data = {k: v for k, v in update_data.items() if k in allowed_fields}

                if not filtered_data:
                    logger.warning("No valid subscription info fields provided for update",
                                 tenant_id=tenant_id)
                    tenant = await self.tenant_repo.get_by_id(tenant_id)
                    return TenantResponse.from_orm(tenant)

                updated_tenant = await self.tenant_repo.update(tenant_id, filtered_data)

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

                logger.info("Tenant subscription info updated",
                           tenant_id=tenant_id,
                           updated_fields=list(filtered_data.keys()))

                return TenantResponse.from_orm(updated_tenant)

        except HTTPException:
            raise
        except Exception as e:
            logger.error("Failed to update tenant subscription info",
                        tenant_id=tenant_id,
                        error=str(e))
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail=f"Failed to update tenant subscription info: {str(e)}"
            )

    async def get_tenant_by_customer_id(self, customer_id: str) -> Optional[Tenant]:
        """
        Get tenant by Stripe customer ID
        
        Args:
            customer_id: Stripe customer ID
            
        Returns:
            Tenant object if found, None otherwise
        """
        try:
            async with self.database_manager.get_session() as db_session:
                await self._init_repositories(db_session)
                
                # Search for tenant with matching customer_id
                tenant = await self.tenant_repo.get_by_customer_id(customer_id)
                
                if tenant:
                    logger.info("Found tenant by customer_id",
                               customer_id=customer_id,
                               tenant_id=str(tenant.id))
                    return tenant
                else:
                    logger.info("No tenant found for customer_id",
                               customer_id=customer_id)
                    return None
                    
        except Exception as e:
            logger.error("Error getting tenant by customer_id",
                        customer_id=customer_id,
                        error=str(e))
            return None

    async def get_subscriptions_by_customer_id(self, customer_id: str) -> List[Subscription]:
        """
        Get subscriptions by Stripe customer ID
        
        Args:
            customer_id: Stripe customer ID
            
        Returns:
            List of Subscription objects
        """
        try:
            async with self.database_manager.get_session() as db_session:
                await self._init_repositories(db_session)
                
                # Search for subscriptions with matching customer_id
                subscriptions = await self.subscription_repo.get_by_customer_id(customer_id)
                
                logger.info("Found subscriptions by customer_id",
                           customer_id=customer_id,
                           count=len(subscriptions))
                return subscriptions
                
        except Exception as e:
            logger.error("Error getting subscriptions by customer_id",
                        customer_id=customer_id,
                        error=str(e))
            return []

# Legacy compatibility alias
TenantService = EnhancedTenantService
