from enum import IntEnum
import configparser
import os
import sys
from unittest.mock import patch, MagicMock


import pytest
import sys
import builtins

import DisplayCAL.config as config


def test_default_values_1():
    """Test default values of module level variables."""
    from DisplayCAL import config

    config.initcfg()

    assert config.configparser.DEFAULTSECT == "Default"
    assert config.EXE == sys.executable  # venv/bin/python
    assert config.EXEDIR == os.path.dirname(sys.executable)  # venv/bin
    assert config.EXENAME == os.path.basename(sys.executable)  # python
    assert config.ISEXE is False
    # $HOME/.local/bin/pycharm-{PYCHARMVERSION}/plugins/python/helpers/pycharm/_jb_pytest_runner.py
    assert config.PYFILE != ""
    # $HOME/.local/bin/pycharm-{PYCHARMVERSION}/plugins/python/helpers/pycharm/_jb_pytest_runner.py
    assert config.PYPATH != ""
    assert config.ISAPP is False  #
    assert config.PYNAME != ""  # _jb_pytest_runner
    # assert config.pyext != ""  # .py  This is not valid when pytest runß directly
    # $HOME/Documents/development/displaycal/DisplayCAL
    assert config.PYDIR != ""

    if sys.platform == "linux":
        assert config.XDG_CONFIG_DIR_DEFAULT == "/etc/xdg"
        assert config.XDG_CONFIG_HOME == os.path.expanduser("~/.config")
        assert config.XDG_DATA_HOME == os.path.expanduser("~/.local/share")
        assert config.XDG_DATA_HOME_DEFAULT == os.path.expanduser("~/.local/share")

    # skip the rest of the test for now
    return

    assert config.XDG_DATA_DIRS == [
        "/usr/share/pop",
        os.path.expanduser("~/.local/share/flatpak/exports/share"),
        "/var/lib/flatpak/exports/share",
        "/usr/local/share",
        "/usr/share",
        "/var/lib",
    ]

    from DisplayCAL.meta import VERSION_STRING

    expected_data_dirs = [
        os.path.expanduser("~/.local/share/DisplayCAL"),
        os.path.expanduser("~/.local/share/doc/DisplayCAL"),
        os.path.expanduser(f"~/.local/share/doc/DisplayCAL-{VERSION_STRING}"),
        os.path.expanduser("~/.local/share/doc/displaycal"),
        os.path.expanduser("~/.local/share/doc/packages/DisplayCAL"),
        os.path.expanduser("~/.local/share/flatpak/exports/share/DisplayCAL"),
        os.path.expanduser("~/.local/share/flatpak/exports/share/doc/DisplayCAL"),
        os.path.expanduser(
            f"~/.local/share/flatpak/exports/share/doc/DisplayCAL-{VERSION_STRING}"
        ),
        os.path.expanduser("~/.local/share/flatpak/exports/share/doc/displaycal"),
        os.path.expanduser(
            "~/.local/share/flatpak/exports/share/doc/packages/DisplayCAL"
        ),
        os.path.expanduser("~/.local/share/flatpak/exports/share/icons/hicolor"),
        os.path.expanduser("~/.local/share/icons/hicolor"),
        config.PYDIR,
        os.path.expanduser(
            "~/PycharmProjects/DisplayCAL/venv/lib/python3.9/site-packages/DisplayCAL-3.8.9.3-py3.9-linux-x86_64.egg/DisplayCAL"
        ),
        "/usr/local/share/DisplayCAL",
        "/usr/local/share/doc/DisplayCAL",
        f"/usr/local/share/doc/DisplayCAL-{VERSION_STRING}",
        "/usr/local/share/doc/displaycal",
        "/usr/local/share/doc/packages/DisplayCAL",
        "/usr/local/share/icons/hicolor",
        "/usr/share/DisplayCAL",
        "/usr/share/doc/DisplayCAL",
        f"/usr/share/doc/DisplayCAL-{VERSION_STRING}",
        "/usr/share/doc/displaycal",
        "/usr/share/doc/packages/DisplayCAL",
        "/usr/share/icons/hicolor",
        "/usr/share/pop/DisplayCAL",
        "/usr/share/pop/doc/DisplayCAL",
        f"/usr/share/pop/doc/DisplayCAL-{VERSION_STRING}",
        "/usr/share/pop/doc/displaycal",
        "/usr/share/pop/doc/packages/DisplayCAL",
        "/usr/share/pop/icons/hicolor",
        "/var/lib/DisplayCAL",
        "/var/lib/doc/DisplayCAL",
        f"/var/lib/doc/DisplayCAL-{VERSION_STRING}",
        "/var/lib/doc/displaycal",
        "/var/lib/doc/packages/DisplayCAL",
        "/var/lib/flatpak/exports/share/DisplayCAL",
        "/var/lib/flatpak/exports/share/doc/DisplayCAL",
        f"/var/lib/flatpak/exports/share/doc/DisplayCAL-{VERSION_STRING}",
        "/var/lib/flatpak/exports/share/doc/displaycal",
        "/var/lib/flatpak/exports/share/doc/packages/DisplayCAL",
        "/var/lib/flatpak/exports/share/icons/hicolor",
        "/var/lib/icons/hicolor",
    ]
    assert sorted(config.DATA_DIRS) == sorted(expected_data_dirs)


# get_hidpi_scaling_factor
@pytest.mark.parametrize(
    "platform,expected", [
        ("darwin", 1.0),
        ("win32", 1.0),
    ]
)
def test_get_hidpi_scaling_factor_mac_win(monkeypatch, platform, expected):
    monkeypatch.setattr(sys, "platform", platform)
    assert config.get_hidpi_scaling_factor() == expected


def test_get_hidpi_scaling_factor_xrdb(monkeypatch):
    # Simulate Linux, xrdb present, Xft.dpi found
    monkeypatch.setattr(sys, "platform", "linux")
    # Patch which to return True for "xrdb"
    monkeypatch.setattr("DisplayCAL.util_os.which", lambda name: name == "xrdb")
    # Patch get_default_dpi to return 96
    monkeypatch.setattr(config, "get_default_dpi", lambda: 96)
    # Patch subprocess.Popen to simulate xrdb output
    class DummyPopen:
        def __init__(self, *a, **k): pass
        def communicate(self):
            return (b"Xft.dpi:        192\n", b"")
    monkeypatch.setattr("subprocess.Popen", DummyPopen)
    assert config.get_hidpi_scaling_factor() == 2.0


def test_get_hidpi_scaling_factor_xrdb_invalid(monkeypatch):
    # Simulate Linux, xrdb present, Xft.dpi invalid
    monkeypatch.setattr(sys, "platform", "linux")
    monkeypatch.setattr("DisplayCAL.util_os.which", lambda name: name == "xrdb")
    monkeypatch.setattr(config, "get_default_dpi", lambda: 96)
    class DummyPopen:
        def __init__(self, *a, **k): pass
        def communicate(self):
            return (b"Xft.dpi:        notanumber\n", b"")
    monkeypatch.setattr("subprocess.Popen", DummyPopen)
    # Should fall through and return None
    assert config.get_hidpi_scaling_factor() is None


def test_get_hidpi_scaling_factor_kde(monkeypatch):
    # Simulate Linux, no xrdb, KDE desktop, QT_SCREEN_SCALE_FACTORS set
    monkeypatch.setattr(sys, "platform", "linux")
    monkeypatch.setattr("DisplayCAL.util_os.which", lambda name: False)
    monkeypatch.setenv("XDG_CURRENT_DESKTOP", "KDE")
    monkeypatch.setenv("QT_SCREEN_SCALE_FACTORS", "1.5;2.0")
    # Patch wx and real_display_size_mm to avoid wx dependency
    class DummyWx:
        @staticmethod
        def GetApp():
            return None
    monkeypatch.setitem(sys.modules, "DisplayCAL.wx_addons", type("mod", (), {"wx": DummyWx}))
    # Should use first factor
    assert config.get_hidpi_scaling_factor() == 1.5


def test_get_hidpi_scaling_factor_gnome(monkeypatch):
    # Simulate Linux, no xrdb, not KDE, gsettings present
    monkeypatch.setattr(sys, "platform", "linux")
    monkeypatch.setattr("DisplayCAL.util_os.which", lambda name: name == "gsettings")
    monkeypatch.setenv("XDG_CURRENT_DESKTOP", "GNOME")
    # Patch subprocess.Popen to simulate gsettings output
    class DummyPopen:
        def __init__(self, *a, **k): pass
        def communicate(self):
            return (b"uint32 2", b"")
    monkeypatch.setattr("subprocess.Popen", DummyPopen)
    assert config.get_hidpi_scaling_factor() == 2.0


def test_get_hidpi_scaling_factor_none(monkeypatch):
    # Simulate Linux, no xrdb, not KDE, no gsettings
    monkeypatch.setattr(sys, "platform", "linux")
    monkeypatch.setattr("DisplayCAL.util_os.which", lambda name: False)
    monkeypatch.setenv("XDG_CURRENT_DESKTOP", "XFCE")
    assert config.get_hidpi_scaling_factor() is None


# getcfg debug_print fix (#698)

def test_getcfg_no_debug_print_for_none_default(monkeypatch):
    """debug_print must not fire when the fallback default value is None."""
    monkeypatch.setattr(config, "DEBUG", True)
    mock_debug_print = MagicMock()
    monkeypatch.setattr(config, "debug_print", mock_debug_print)

    fresh_cfg = config.CaseSensitiveConfigParser()
    # "argyll.dir" has DEFAULTS["argyll.dir"] = None
    config.getcfg("argyll.dir", fallback=True, cfg=fresh_cfg)

    mock_debug_print.assert_not_called()


def test_getcfg_debug_print_called_for_non_none_default(monkeypatch):
    """debug_print must fire when the fallback default value is not None."""
    monkeypatch.setattr(config, "DEBUG", True)
    mock_debug_print = MagicMock()
    monkeypatch.setattr(config, "debug_print", mock_debug_print)

    fresh_cfg = config.CaseSensitiveConfigParser()
    # "calibration.ambient_viewcond_adjust" has DEFAULTS value of 0 (not None)
    config.getcfg("calibration.ambient_viewcond_adjust", fallback=True, cfg=fresh_cfg)

    mock_debug_print.assert_called_once()


# initcfg combined "if not module" block (#698)

def _make_ini(tmp_path: os.PathLike) -> None:
    """Write a minimal DisplayCAL.ini so fetch_config_files finds it."""
    ini = tmp_path / "DisplayCAL.ini"
    ini.write_text("[Default]\n")


def test_initcfg_sets_lang_default(monkeypatch, tmp_path):
    """initcfg() without a module sets lang when absent from config."""
    _make_ini(tmp_path)
    monkeypatch.setattr(config, "CONFIG_HOME", str(tmp_path))
    monkeypatch.setattr(config, "CONFIG_SYS", str(tmp_path))

    fresh_cfg = config.CaseSensitiveConfigParser()
    config.initcfg(cfg=fresh_cfg)

    assert fresh_cfg.get(configparser.DEFAULTSECT, "lang", fallback=None) is not None


def test_initcfg_sets_calibration_ambient_default(monkeypatch, tmp_path):
    """initcfg() without a module sets calibration.ambient_viewcond_adjust when absent."""
    _make_ini(tmp_path)
    monkeypatch.setattr(config, "CONFIG_HOME", str(tmp_path))
    monkeypatch.setattr(config, "CONFIG_SYS", str(tmp_path))

    fresh_cfg = config.CaseSensitiveConfigParser()
    config.initcfg(cfg=fresh_cfg)

    value = fresh_cfg.get(
        configparser.DEFAULTSECT, "calibration.ambient_viewcond_adjust", fallback=None
    )
    assert value == config.DEFAULTS["calibration.ambient_viewcond_adjust"]


def test_initcfg_sets_profile_save_path_default(monkeypatch, tmp_path):
    """initcfg() without a module sets profile.save_path to STORAGE when absent."""
    _make_ini(tmp_path)
    monkeypatch.setattr(config, "CONFIG_HOME", str(tmp_path))
    monkeypatch.setattr(config, "CONFIG_SYS", str(tmp_path))

    fresh_cfg = config.CaseSensitiveConfigParser()
    config.initcfg(cfg=fresh_cfg)

    value = fresh_cfg.get(configparser.DEFAULTSECT, "profile.save_path", fallback=None)
    assert value == config.STORAGE


def test_initcfg_module_skips_defaults(monkeypatch, tmp_path):
    """initcfg() with a module arg must not touch the module-only defaults."""
    ini = tmp_path / "DisplayCAL-apply-profiles.ini"
    ini.write_text("[Default]\n")
    monkeypatch.setattr(config, "CONFIG_HOME", str(tmp_path))

    fresh_cfg = config.CaseSensitiveConfigParser()
    mock_setcfg = MagicMock()
    monkeypatch.setattr(config, "setcfg", mock_setcfg)

    config.initcfg(module="apply-profiles", cfg=fresh_cfg)

    set_keys = {call.args[0] for call in mock_setcfg.call_args_list}
    assert "lang" not in set_keys
    assert "calibration.ambient_viewcond_adjust" not in set_keys
    assert "profile.save_path" not in set_keys

