# app/core/logging.py
"""Hybrid logging system with file rotation and database storage.

This module provides classes and functions for:
- File-based logging with automatic rotation
- Database logging for critical events (WARNING, ERROR, CRITICAL)
- Dynamic log level configuration from database settings
- Log retention and cleanup policies
"""

import logging
import sys
import traceback
from datetime import UTC, datetime
from logging.handlers import RotatingFileHandler
from pathlib import Path

from app.core.config import settings


class DatabaseLogHandler(logging.Handler):
    """
    Custom logging handler that stores WARNING, ERROR, and CRITICAL logs in database.

    Runs asynchronously to avoid blocking application performance.
    """

    def __init__(self):
        super().__init__()
        self.setLevel(logging.WARNING)  # Only log WARNING and above to database

    def emit(self, record):
        """Emit a log record to the database."""
        try:
            from app.core.database import SessionLocal
            from app.modules.tenancy.models import ApplicationLog

            # Skip if no database session available
            db = SessionLocal()
            if not db:
                return

            try:
                # Extract exception information if present
                exception_type = None
                exception_message = None
                stack_trace = None

                if record.exc_info:
                    exception_type = (
                        record.exc_info[0].__name__ if record.exc_info[0] else None
                    )
                    exception_message = (
                        str(record.exc_info[1]) if record.exc_info[1] else None
                    )
                    stack_trace = "".join(
                        traceback.format_exception(*record.exc_info)
                    )

                # Extract context from record (if middleware added it)
                user_id = getattr(record, "user_id", None)
                store_id = getattr(record, "store_id", None)
                request_id = getattr(record, "request_id", None)
                context = getattr(record, "context", None)

                # Create log entry
                log_entry = ApplicationLog(
                    timestamp=datetime.fromtimestamp(record.created, tz=UTC),
                    level=record.levelname,
                    logger_name=record.name,
                    module=record.module,
                    function_name=record.funcName,
                    line_number=record.lineno,
                    message=record.getMessage(),
                    exception_type=exception_type,
                    exception_message=exception_message,
                    stack_trace=stack_trace,
                    request_id=request_id,
                    user_id=user_id,
                    store_id=store_id,
                    context=context,
                )

                db.add(log_entry)
                db.commit()

            except Exception:
                db.rollback()
                # Silently skip - don't print to stderr to avoid log spam
            finally:
                db.close()

        except Exception:
            # Silently fail - logging should never crash the app
            pass


def get_log_level_from_db():
    """
    Get log level from database settings.
    Falls back to environment variable if not found.
    """
    try:
        from app.core.database import SessionLocal
        from app.modules.core.services.admin_settings_service import (
            admin_settings_service,
        )

        db = SessionLocal()
        if not db:
            return settings.log_level

        try:
            log_level = admin_settings_service.get_setting_value(
                db, "log_level", default=settings.log_level
            )
            return log_level.upper() if log_level else settings.log_level.upper()
        finally:
            db.close()
    except Exception:
        # If database not ready or error, fall back to settings
        return settings.log_level.upper()


def get_rotation_settings_from_db():
    """
    Get log rotation settings from database.
    Returns tuple: (max_bytes, backup_count)
    """
    try:
        from app.core.database import SessionLocal
        from app.modules.core.services.admin_settings_service import (
            admin_settings_service,
        )

        db = SessionLocal()
        if not db:
            return (10 * 1024 * 1024, 5)  # 10MB, 5 backups

        try:
            max_mb = admin_settings_service.get_setting_value(
                db, "log_file_max_size_mb", default=10
            )
            backup_count = admin_settings_service.get_setting_value(
                db, "log_file_backup_count", default=5
            )
            return (int(max_mb) * 1024 * 1024, int(backup_count))
        finally:
            db.close()
    except Exception:
        # Fall back to defaults
        return (10 * 1024 * 1024, 5)


def reload_log_level():
    """
    Reload log level from database without restarting application.
    Useful when log level is changed via admin panel.
    """
    try:
        new_level = get_log_level_from_db()
        logger = logging.getLogger()
        logger.setLevel(getattr(logging, new_level))
        logging.info(f"Log level changed to: {new_level}")
        return new_level
    except Exception as e:
        logging.error(f"Failed to reload log level: {e}")
        return None


def setup_logging():
    """Configure application logging with file rotation and database handlers."""
    # Determine log file path
    log_file_path = settings.log_file
    if log_file_path:
        log_file = Path(log_file_path)
    else:
        # Default to logs/app.log
        log_file = Path("logs") / "app.log"

    # Create logs directory if it doesn't exist
    log_file.parent.mkdir(parents=True, exist_ok=True)

    # Get log level from database (or fall back to env)
    log_level = get_log_level_from_db()

    # Get rotation settings from database (or fall back to defaults)
    max_bytes, backup_count = get_rotation_settings_from_db()

    # Configure root logger
    logger = logging.getLogger()
    logger.setLevel(getattr(logging, log_level))

    # Remove existing handlers
    for handler in logger.handlers[:]:
        logger.removeHandler(handler)

    # Create formatters
    detailed_formatter = logging.Formatter(
        "%(asctime)s - %(name)s - %(levelname)s - [%(module)s:%(funcName)s:%(lineno)d] - %(message)s"
    )
    simple_formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")

    # Console handler (simple format)
    console_handler = logging.StreamHandler(sys.stdout)
    console_handler.setFormatter(simple_formatter)
    logger.addHandler(console_handler)

    # Rotating file handler (detailed format)
    file_handler = RotatingFileHandler(
        log_file, maxBytes=max_bytes, backupCount=backup_count, encoding="utf-8"
    )
    file_handler.setFormatter(detailed_formatter)
    logger.addHandler(file_handler)

    # Database handler for critical events (WARNING and above)
    try:
        db_handler = DatabaseLogHandler()
        db_handler.setFormatter(detailed_formatter)
        logger.addHandler(db_handler)
    except Exception as e:
        # If database handler fails, just use file logging
        print(
            f"Warning: Database logging handler could not be initialized: {e}",
            file=sys.stderr,
        )

    # Configure specific loggers to reduce noise
    logging.getLogger("uvicorn.access").setLevel(logging.WARNING)
    logging.getLogger("sqlalchemy.engine").setLevel(logging.WARNING)

    # Log startup info
    logger.info("=" * 80)
    logger.info("LOGGING SYSTEM INITIALIZED")
    logger.info(f"Log Level: {log_level}")
    logger.info(f"Log File: {log_file}")
    logger.info(f"Max File Size: {max_bytes / (1024 * 1024):.1f} MB")
    logger.info(f"Backup Count: {backup_count}")
    logger.info("Database Logging: Enabled (WARNING and above)")
    logger.info("=" * 80)

    return logging.getLogger(__name__)
