"""
TimezoneManager — timezone detection, validation, and weekday helpers.

Manages local timezone detection (system, registry, or user-entity-derived),
timezone mismatch checking, and weekday name formatting for the Workstatus
desktop agent.

Thread-safety:
    All methods are synchronous.  ``set_local_timezone()`` mutates
    ``self.local_timezone`` and should only be called from the monitoring loop
    at session start.  Read-only methods (``get_today_weekday()``,
    ``get_weekday()``, ``timezone_wrong()``) are thread-safe.
"""
# ws_package/managers/timezone_manager.py
import datetime
import platform
import zoneinfo
from tzlocal import get_localzone
from typing import Optional

from ws_package.log.writer import LogWriter
from ws_package.exceptions.custom_exceptions import ValidationError, ActivityMonitoringError

logger = LogWriter.file(__name__)

class TimezoneManager:
    """Manages timezone operations and validation for activity monitoring."""
    
    def __init__(self):
        self.local_timezone = None
        self.ws_timezone = None
        self.window_timezone = None
        self.tzmain = None
        
    def set_local_timezone(self, user_entity) -> None:
        """Set ``self.local_timezone`` from the user entity or fall back to the system timezone.

        If ``user_entity`` has a non-empty ``timezone`` attribute, that value is used.
        Otherwise, ``tzlocal.get_localzone()`` is called.

        Args:
            user_entity: User entity that may carry a ``timezone`` string, or ``None``.
        """
        try:
            if user_entity and hasattr(user_entity, 'timezone') and user_entity.timezone:
                self.local_timezone = user_entity.timezone
                logger.info("Set timezone from user entity: %s", self.local_timezone)
            else:
                self.local_timezone = get_localzone()
                logger.info("Set timezone from system: %s", self.local_timezone)
        except Exception as e:
            logger.error("Error setting local timezone: %s", e, exc_info=True)
            self.local_timezone = get_localzone()
    
    def get_system_timezone(self) -> str:
        """Return the system timezone as a string.

        On Windows delegates to :meth:`current_registry_timezone`; on all other
        platforms calls ``str(get_localzone())``.  Falls back to ``"UTC"`` on error.

        Returns:
            IANA timezone name string (e.g. ``"America/New_York"``).
        """
        try:
            if platform.system() == "Windows":
                return self.current_registry_timezone()
            else:
                return str(get_localzone())
        except Exception as e:
            logger.error("Error getting system timezone: %s", e, exc_info=True)
            return "UTC"
    
    def current_registry_timezone(self) -> str:
        """Read the current timezone name from the Windows registry.

        On non-Windows systems returns ``str(get_localzone())``.  Falls back to
        ``"UTC"`` if the registry key is unavailable.

        Returns:
            IANA-compatible timezone name string, or ``"UTC"`` on failure.
        """
        try:
            if platform.system() != "Windows":
                return str(get_localzone())
            
            import winreg
            with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, 
                              r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation") as key:
                timezone_name = winreg.QueryValueEx(key, "TimeZoneKeyName")[0]
                return timezone_name
        except Exception as e:
            logger.error("Error reading registry timezone: %s", e, exc_info=True)
            return "UTC"
    
    def check_timezone_info(self) -> dict:
        """Compare the current system timezone against the cached local timezone.

        Returns:
            A dict with keys ``system_timezone`` (str), ``local_timezone`` (str), and
            ``timezone_match`` (bool).

        Raises:
            ActivityMonitoringError: If timezone information cannot be retrieved.
        """
        try:
            current_tz = self.get_system_timezone()
            local_tz = str(self.local_timezone) if self.local_timezone else "Unknown"
            
            logger.info("Current system timezone: %s", current_tz)
            logger.info("Local timezone: %s", local_tz)
            
            return {
                "system_timezone": current_tz,
                "local_timezone": local_tz,
                "timezone_match": current_tz == local_tz
            }
        except Exception as e:
            logger.error("Error checking timezone info: %s", e, exc_info=True)
            raise ActivityMonitoringError(f"Failed to check timezone info: {e}")
    
    def timezone_wrong(self) -> bool:
        """Check whether the current system timezone differs from the cached local timezone.

        Returns:
            ``True`` if the timezones do not match or if timezone data is unavailable,
            ``False`` if they match.
        """
        try:
            if not self.local_timezone:
                return True
            
            current_tz = self.get_system_timezone()
            local_tz = str(self.local_timezone)
            
            return current_tz != local_tz
        except Exception as e:
            logger.error("Error checking if timezone is wrong: %s", e, exc_info=True)
            return True
    
    def get_today_weekday(self) -> str:
        """Return the abbreviated weekday name for today (e.g. ``'Mon'``).

        Returns:
            Three-letter weekday abbreviation from ``strftime('%a')``.
        """
        return datetime.datetime.now().strftime('%a')

    def get_weekday(self, date_str: str) -> str:
        """Return the abbreviated weekday name for the given date string.

        Accepts both ``%Y-%m-%d %H:%M:%S.%f`` and ``%Y-%m-%d %H:%M:%S`` formats.

        Args:
            date_str: Date/datetime string to parse.

        Returns:
            Three-letter weekday abbreviation, or ``"Unknown"`` if parsing fails.
        """
        try:
            # Try parsing with microseconds first
            try:
                dt = datetime.datetime.strptime(date_str, "%Y-%m-%d %H:%M:%S.%f")
                return dt.strftime('%a')
            except ValueError:
                # Fallback to parsing without microseconds
                dt = datetime.datetime.strptime(date_str, "%Y-%m-%d %H:%M:%S")
                return dt.strftime('%a')
        except ValueError as e:
            logger.error("Error parsing date string %s: %s", date_str, e, exc_info=True)
            return "Unknown"
