"""
Session management for encrypted database connections.
Handles session creation, validation, and cleanup.
"""

import datetime
import secrets
import threading
from datetime import UTC
from typing import Dict, Optional, Set

from loguru import logger

from ...security import get_security_default


class SessionManager:
    """Manages user sessions and database connection lifecycle."""

    def __init__(self):
        self.sessions: Dict[str, dict] = {}
        self._lock = threading.Lock()
        # Load session timeouts from security settings
        session_hours = get_security_default(
            "security.session_timeout_hours", 2
        )
        remember_days = get_security_default(
            "security.session_remember_me_days", 30
        )
        self.session_timeout = datetime.timedelta(hours=session_hours)
        self.remember_me_timeout = datetime.timedelta(days=remember_days)

    def create_session(self, username: str, remember_me: bool = False) -> str:
        """Create a new session for a user.

        Args:
            username: The username to create a session for.
            remember_me: If True, use extended session timeout.

        Returns:
            The session ID as a URL-safe string.
        """
        session_id = secrets.token_urlsafe(32)

        with self._lock:
            self.sessions[session_id] = {
                "username": username,
                "created_at": datetime.datetime.now(UTC),
                "last_access": datetime.datetime.now(UTC),
                "remember_me": remember_me,
            }

        logger.debug(f"Created session {session_id[:8]}... for user {username}")
        return session_id

    def validate_session(self, session_id: str) -> Optional[str]:
        """
        Validate a session and return username if valid.
        Updates last access time.
        """
        with self._lock:
            if session_id not in self.sessions:
                return None

            session_data = self.sessions[session_id]
            now = datetime.datetime.now(UTC)

            # Check timeout
            timeout = (
                self.remember_me_timeout
                if session_data["remember_me"]
                else self.session_timeout
            )
            if now - session_data["last_access"] > timeout:
                # Session expired — remove inline (already under lock)
                username = session_data["username"]
                del self.sessions[session_id]
                logger.debug(
                    f"Session {session_id[:8]}... expired for {username}"
                )
                return None

            # Update last access
            session_data["last_access"] = now
            return str(session_data["username"])

    def destroy_session(self, session_id: str):
        """Destroy a session and clean up."""
        with self._lock:
            if session_id in self.sessions:
                username = self.sessions[session_id]["username"]
                del self.sessions[session_id]
                logger.debug(
                    f"Destroyed session {session_id[:8]}... for user {username}"
                )

    def destroy_all_user_sessions(self, username: str) -> int:
        """Destroy all sessions for a given user. Returns count destroyed."""
        with self._lock:
            to_delete = [
                sid
                for sid, data in self.sessions.items()
                if data["username"] == username
            ]
            for sid in to_delete:
                del self.sessions[sid]
        if to_delete:
            logger.debug(
                f"Destroyed {len(to_delete)} session(s) for user {username}"
            )
        return len(to_delete)

    def cleanup_expired_sessions(self):
        """Remove all expired sessions."""
        now = datetime.datetime.now(UTC)
        expired = []

        with self._lock:
            for session_id, data in self.sessions.items():
                timeout = (
                    self.remember_me_timeout
                    if data["remember_me"]
                    else self.session_timeout
                )
                if now - data["last_access"] > timeout:
                    expired.append(session_id)

            for session_id in expired:
                del self.sessions[session_id]

        if expired:
            logger.info(f"Cleaned up {len(expired)} expired sessions")

    def get_active_sessions_count(self) -> int:
        """Get count of active sessions."""
        self.cleanup_expired_sessions()
        with self._lock:
            return len(self.sessions)

    def get_user_sessions(self, username: str) -> list:
        """Get all active sessions for a user."""
        user_sessions = []
        with self._lock:
            for session_id, data in self.sessions.items():
                if data["username"] == username:
                    user_sessions.append(
                        {
                            "session_id": session_id[:8] + "...",
                            "created_at": data["created_at"],
                            "last_access": data["last_access"],
                            "remember_me": data["remember_me"],
                        }
                    )
        return user_sessions

    def get_active_usernames(self) -> Set[str]:
        """Return set of usernames with at least one non-expired session."""
        now = datetime.datetime.now(UTC)
        with self._lock:
            return {
                data["username"]
                for data in self.sessions.values()
                if now - data["last_access"]
                <= (
                    self.remember_me_timeout
                    if data["remember_me"]
                    else self.session_timeout
                )
            }

    def has_active_sessions_for(self, username: str) -> bool:
        """Check if a user has any non-expired sessions."""
        now = datetime.datetime.now(UTC)
        with self._lock:
            for data in self.sessions.values():
                if data["username"] != username:
                    continue
                timeout = (
                    self.remember_me_timeout
                    if data["remember_me"]
                    else self.session_timeout
                )
                if now - data["last_access"] <= timeout:
                    return True
            return False


# Module-level singleton
session_manager = SessionManager()
