from sqlalchemy.orm import Session

from app.core.config import settings as env_settings
from app.core.hashing import get_password_hash
from app.core.logging import get_logger
from app.models.settings import Settings as SettingsModel
from app.schemas.settings import Settings as SettingsSchema
from app.schemas.settings import SettingsCreate

logger = get_logger(__name__)


def create_initial_settings(db: Session):
    """Create initial settings in the database if they don't exist."""
    logger.debug("Checking for initial settings.")
    db_settings = db.query(SettingsModel).first()
    if not db_settings:
        logger.info(
            "No settings found in the database, creating new default settings from environment variables."
        )

        # Get env settings, but only for fields that exist in the DB model
        model_fields = {c.name for c in SettingsModel.__table__.columns}
        env_data_for_db = {
            k: v for k, v in env_settings.model_dump().items() if k in model_fields
        }

        if env_settings.auth_password:
            env_data_for_db["auth_password_hash"] = get_password_hash(
                env_settings.auth_password
            )
        if "auth_password" in env_data_for_db:
            del env_data_for_db["auth_password"]

        db_settings = SettingsModel(**env_data_for_db)
        db.add(db_settings)
        db.commit()
        db.refresh(db_settings)
        logger.info("Default settings created from environment variables.")


def get_settings(db: Session, with_password: bool = False) -> SettingsSchema:
    """Retrieve application settings, prioritizing environment variables over database."""
    logger.debug("Querying for settings")
    db_settings = db.query(SettingsModel).first()

    if not db_settings:
        # This should not happen if create_initial_settings is called at startup.
        raise RuntimeError("Settings not initialized.")

    # Build dictionary from DB model attributes, handling possible None values
    db_data = {
        "id": db_settings.id,
        "imap_server": db_settings.imap_server or "",
        "imap_username": db_settings.imap_username or "",
        "search_folder": db_settings.search_folder,
        "move_to_folder": db_settings.move_to_folder,
        "mark_as_read": db_settings.mark_as_read,
        "email_check_interval": db_settings.email_check_interval,
        "auto_add_new_senders": db_settings.auto_add_new_senders,
        "auth_username": db_settings.auth_username,
    }

    # Get all environment settings that were explicitly set.
    env_data = env_settings.model_dump(exclude_unset=True)

    # Merge them. env_data takes precedence.
    merged_data = {**db_data, **env_data}

    # The fields that came from env are the "locked" ones.
    locked_fields = list(env_data.keys())
    logger.debug(f"Locked fields from environment variables: {locked_fields}")

    # Handle password separately for security
    if with_password:
        logger.debug("Including IMAP password in settings data")
        if "imap_password" in locked_fields:
            merged_data["imap_password"] = env_settings.imap_password
        else:
            merged_data["imap_password"] = db_settings.imap_password
    elif "imap_password" in merged_data:
        # Ensure password is not in the data if not requested
        del merged_data["imap_password"]

    # Create the final schema object
    settings_schema = SettingsSchema.model_validate(merged_data)
    settings_schema.locked_fields = locked_fields

    return settings_schema


def create_or_update_settings(db: Session, settings: SettingsCreate):
    """Create or update application settings."""
    logger.info("Creating or updating settings")
    db_settings = db.query(SettingsModel).first()
    if not db_settings:
        logger.info("No existing settings found, creating new ones.")
        db_settings = SettingsModel()
        db.add(db_settings)

    update_data = settings.model_dump(exclude_unset=True)

    # Do not update fields that are set by environment variables
    locked_fields = list(env_settings.model_dump(exclude_unset=True).keys())
    logger.debug(f"Fields locked by environment variables: {locked_fields}")

    for key, value in update_data.items():
        if key in locked_fields:
            continue

        if key == "auth_password":
            if value:
                db_settings.auth_password_hash = get_password_hash(value)
            else:
                db_settings.auth_password_hash = None
        elif hasattr(db_settings, key):
            setattr(db_settings, key, value)

    db.commit()
    db.refresh(db_settings)
    logger.info("Successfully updated settings.")

    # Return the updated settings including locked fields for a complete view
    return get_settings(db)
