import json
import os
from pathlib import Path
from unittest.mock import PropertyMock

import pytest
from pytest_mock import MockerFixture

from dangerzone.settings import SETTINGS_FILENAME, Settings


def default_settings_0_4_1() -> dict:
    """Get the default settings for the 0.4.1 Dangerzone release."""
    return {
        "save": True,
        "archive": True,
        "ocr": True,
        "ocr_language": "English",
        "open": True,
        "open_app": None,
        "safe_extension": "-safe.pdf",
    }


@pytest.fixture
def settings(tmp_path: Path, mocker: MockerFixture) -> Settings:
    dz_core = mocker.MagicMock()
    type(dz_core).appdata_path = PropertyMock(return_value=tmp_path)
    return Settings(dz_core)


def save_settings(tmp_path: Path, settings: dict) -> None:
    """Mimic the way Settings save a dictionary to a settings.json file."""
    settings_filename = tmp_path / "settings.json"
    with open(settings_filename, "w") as settings_file:
        json.dump(settings, settings_file, indent=4)


def test_no_settings_file_creates_new_one(settings: Settings) -> None:
    """Default settings file is created on first run"""
    assert os.path.isfile(settings.settings_filename)
    new_settings_dict = json.load(open(settings.settings_filename))
    assert sorted(new_settings_dict.items()) == sorted(
        settings.generate_default_settings().items()
    )


def test_corrupt_settings(tmp_path: Path, mocker: MockerFixture) -> None:
    # Set some broken settings file
    corrupt_settings_dict = "{:}"
    with open(tmp_path / SETTINGS_FILENAME, "w") as settings_file:
        settings_file.write(corrupt_settings_dict)

    # Initialize settings
    dz_core = mocker.MagicMock()
    type(dz_core).appdata_path = PropertyMock(return_value=tmp_path)
    settings = Settings(dz_core)
    assert os.path.isfile(settings.settings_filename)

    # Check if settings file was reset to the default
    new_settings_dict = json.load(open(settings.settings_filename))
    assert new_settings_dict != corrupt_settings_dict
    assert sorted(new_settings_dict.items()) == sorted(
        settings.generate_default_settings().items()
    )


def test_new_default_setting(tmp_path: Path, mocker: MockerFixture) -> None:
    # Initialize settings
    dz_core = mocker.MagicMock()
    type(dz_core).appdata_path = PropertyMock(return_value=tmp_path)
    settings = Settings(dz_core)
    settings.save()

    # Ensure new default setting is imported into settings
    mocker.patch(
        "dangerzone.settings.Settings.generate_default_settings",
        return_value={"mock_setting": 1},
    )

    settings2 = Settings(dz_core)
    assert settings2.get("mock_setting") == 1


def test_new_settings_added(tmp_path: Path, mocker: MockerFixture) -> None:
    # Initialize settings
    dz_core = mocker.MagicMock()
    type(dz_core).appdata_path = PropertyMock(return_value=tmp_path)
    settings = Settings(dz_core)

    # Add new setting
    settings.set("new_setting_autosaved", 20, autosave=True)
    settings.set(
        "new_setting", 10
    )  # XXX has to be afterwards; otherwise this will be saved

    # Simulate new app startup (settings recreation)
    settings2 = Settings(dz_core)

    # Check if new setting persisted
    assert 20 == settings2.get("new_setting_autosaved")
    with pytest.raises(KeyError):
        settings2.get("new_setting")
