# ws_packagae/repositories/user_repo.py

import asyncio
from typing import Optional
from sqlalchemy import select
from ws_package.models.base import UserDetail, OrgConfigurations
from ws_package.models.base import async_session
from ws_package.entities.user_entity import UserEntity
from ws_package.entities.org_entity import OrgEntity
from ws_package.log.writer import LogWriter
from ws_package.utils.db_lock_manager import db_lock_manager
from ws_package.utils.db_write_queue import db_write_queue, WriteOperationType


logger = LogWriter.file(__name__)

# Database operation timeout (5 seconds)
DB_OPERATION_TIMEOUT = 5.0


class UserManager:
    # Removed individual db_write_lock - now using centralized db_lock_manager
    async def save_user(self, user: UserEntity):
        """
        Save user to in-memory queue.
        The database writer thread will process this asynchronously.
        
        Returns:
            user entity for backward compatibility
        """
        try:
            # Enqueue to in-memory queue (non-blocking, no SQLite operation)
            success = db_write_queue.enqueue(
                operation_type=WriteOperationType.USER,
                table_name="user_details",  # Updated to match new table name
                data=user.model_dump()
            )
            
            if success:
                logger.debug("Enqueued User: %s", user.username)
                # Return entity for backward compatibility
                return user
            else:
                logger.error("Failed to enqueue User - queue may be full")
                return False
                
        except Exception as e:
            logger.error("Error enqueueing User: %s", e, exc_info=True)
            return False
    
    async def get_user(self, user_id: Optional[int] = None) -> UserEntity:
        """
        Get user by ID or latest user.
        
        CRITICAL FIX: Extracts all attributes while session is still open and returns
        UserEntity instead of SQLAlchemy ORM object. This prevents lazy-loading
        issues after the session closes and ensures the read lock releases immediately.
        """
        selectors = select(UserDetail).order_by(UserDetail.id.desc())
        if user_id is not None:
            selectors = selectors.where(UserDetail.user_id == user_id)
        # Use centralized read lock for database read operations
        try:
            with db_lock_manager.read_lock():
                async with async_session() as session:
                    result = await asyncio.wait_for(
                        session.scalars(selectors),
                        timeout=DB_OPERATION_TIMEOUT
                    )
                    record = result.first()
                    
                    # CRITICAL FIX: Extract all attributes while session is still open
                    # This prevents lazy-loading issues after the session closes
                    if record is None:
                        return None
                    
                    # Create UserEntity with all values loaded
                    # This ensures no lazy loading happens after session closes
                    return UserEntity(
                        username=record.username,
                        name=record.name,
                        timezone=record.timezone,
                        token=record.token,
                        sd_token=str(record.sd_token),  # Convert int to str
                        org_id=record.org_id,
                        user_id=record.user_id,
                        bucket=record.bucket,
                        timezone_preference=getattr(record, "timezone_preference", 1),
                    )
        except asyncio.TimeoutError:
            logger.error("Database read operation timed out after %ss while getting user", DB_OPERATION_TIMEOUT)
            return None
        except Exception as e:
            logger.error("Error getting user: %s", e)
            return None

    async def save_user_org(self, org_entity: OrgEntity):
        """
        Save organization to in-memory queue.
        The database writer thread will process this asynchronously.
        
        Returns:
            org_entity for backward compatibility
        """
        try:
            # Enqueue to in-memory queue (non-blocking, no SQLite operation)
            success = db_write_queue.enqueue(
                operation_type=WriteOperationType.ORG,
                table_name="org_configuration",  # Updated to match new table name
                data=org_entity.model_dump()
            )
            
            if success:
                logger.debug("Enqueued Org: %s", org_entity.org_id)
                # Return entity for backward compatibility
                return org_entity
            else:
                logger.error("Failed to enqueue Org - queue may be full")
                return False
                
        except Exception as e:
            logger.error("Error enqueueing Org: %s", e, exc_info=True)
            return False
    
    async def get_user_org(self, user_id: int):
        """
        Get user organization configuration.
        
        CRITICAL FIX: Extracts all attributes while session is still open and returns
        OrgEntity instead of SQLAlchemy ORM object. This prevents lazy-loading
        issues after the session closes and ensures the read lock releases immediately.
        """
        # Use centralized read lock for database read operations
        try:
            with db_lock_manager.read_lock():
                async with async_session() as session:
                    result = await asyncio.wait_for(
                        session.scalars(
                            select(OrgConfigurations).where(OrgConfigurations.user_id == user_id)
                        ),
                        timeout=DB_OPERATION_TIMEOUT
                    )
                    record = result.first()
                    
                    # CRITICAL FIX: Extract all attributes while session is still open
                    # This prevents lazy-loading issues after the session closes
                    if record is None:
                        return None
                    
                    # Create OrgEntity with all values loaded
                    # This ensures no lazy loading happens after session closes
                    return OrgEntity(
                        org_id=record.org_id,
                        user_id=record.user_id,
                        screenshot_interval=record.screenshot_interval,
                        date=record.date,
                        appandurlstatus=record.appandurlstatus,
                        ssfrequencystatus=record.ssfrequencystatus,
                        screenshotblurstatus=record.screenshotblurstatus,
                        livescreenshotstatus=record.livescreenshotstatus,
                        idlenotes_status=record.idlenotes_status,
                        break_limit=record.break_limit,
                        break_status=record.break_status,
                        notes_char_limit=record.notes_char_limit,
                        idle_limit=record.idle_limit,
                        can_delete_scrnshots=record.can_delete_scrnshots,
                        restrict_timer_to_jobsite=record.restrict_timer_to_jobsite,
                        todos_files_upload_limit=record.todos_files_upload_limit,
                        org_timezone=record.org_timezone,
                        user_timezone=record.user_timezone,
                        user_timezone_gmt=record.user_timezone_gmt,
                        user_timezone_gmt_dst=record.user_timezone_gmt_dst,
                        user_timezone_gmt_shortcode=record.user_timezone_gmt_shortcode,
                        bulkInvite=record.bulkInvite,
                        timeInterval=record.timeInterval,
                        blur_intensity=record.blur_intensity,
                        img_quality=record.img_quality,
                        notification_status=record.notification_status,
                        weekly_limit=getattr(record, 'weekly_limit', None),
                        org_week_start_day=getattr(record, 'org_week_start_day', None),
                        worked_total_hours_week=getattr(record, 'worked_total_hours_week', None),
                        worked_total_hours_week_leaving_today=getattr(record, 'worked_total_hours_week_leaving_today', None),
                        daily_limit=getattr(record, 'daily_limit', None),
                        weekly_limit_hours=getattr(record, 'weekly_limit_hours', None),
                        worked_total_hours_today=getattr(record, 'worked_total_hours_today', None),
                        limit_action=getattr(record, 'limit_action', None),
                        timezone_preference=getattr(record, 'timezone_preference', 1),
                    )
        except asyncio.TimeoutError:
            logger.error("Database read operation timed out after %ss while getting user org", DB_OPERATION_TIMEOUT)
            return None
        except Exception as e:
            logger.error("Error getting user org: %s", e)
            return None

user_manager = UserManager()