import json
import logging
from pathlib import Path

import pytest

from sitedrop.server.config import (
    CURRENT_CONFIG_VERSION,
    ServerConfig,
    _migrate_config,
)


def test_default_config():
    config = ServerConfig()
    assert config.host == "127.0.0.1"
    assert config.port == 8000
    assert config.password_hash == ""
    assert len(config.jwt_secret) == 64  # 32 bytes hex


def test_each_instance_gets_unique_secret():
    a = ServerConfig()
    b = ServerConfig()
    assert a.jwt_secret != b.jwt_secret


def test_save_and_load(tmp_path):
    path = tmp_path / "server.json"
    config = ServerConfig(password_hash="hashed", host="0.0.0.0", port=9000)
    config.save(path)

    loaded = ServerConfig.load(path)
    assert loaded.password_hash == "hashed"
    assert loaded.host == "0.0.0.0"
    assert loaded.port == 9000
    assert loaded.jwt_secret == config.jwt_secret


def test_save_creates_parent_dirs(tmp_path):
    path = tmp_path / "nested" / "dir" / "server.json"
    config = ServerConfig()
    config.save(path)
    assert path.exists()


def test_load_nonexistent_returns_default(tmp_path):
    path = tmp_path / "nope.json"
    config = ServerConfig.load(path)
    assert config.password_hash == ""
    assert config.host == "127.0.0.1"


def test_load_ignores_unknown_fields(tmp_path):
    path = tmp_path / "server.json"
    path.write_text(json.dumps({"password_hash": "x", "unknown_field": "y"}))
    config = ServerConfig.load(path)
    assert config.password_hash == "x"


def test_save_overwrites(tmp_path):
    path = tmp_path / "server.json"
    ServerConfig(password_hash="first").save(path)
    ServerConfig(password_hash="second").save(path)
    loaded = ServerConfig.load(path)
    assert loaded.password_hash == "second"


def test_load_corrupted_json(tmp_path):
    path = tmp_path / "server.json"
    path.write_text("not valid json {{{")
    config = ServerConfig.load(path)
    assert config.password_hash == ""
    assert config.host == "127.0.0.1"


def test_save_permission_error(tmp_path):
    from unittest.mock import patch

    path = tmp_path / "readonly" / "server.json"
    (tmp_path / "readonly").mkdir()
    with patch.object(Path, "write_text", side_effect=OSError("Permission denied")):
        with pytest.raises(OSError, match="Failed to save config"):
            ServerConfig().save(path)


def test_save_sets_chmod_600(tmp_path):
    import os
    import stat

    path = tmp_path / "server.json"
    ServerConfig().save(path)
    mode = stat.S_IMODE(os.stat(path).st_mode)
    assert mode == 0o600


def test_ssl_fields_default_empty():
    config = ServerConfig()
    assert config.ssl_certfile == ""
    assert config.ssl_keyfile == ""


def test_ssl_fields_save_load(tmp_path):
    path = tmp_path / "server.json"
    config = ServerConfig(ssl_certfile="/path/cert.pem", ssl_keyfile="/path/key.pem")
    config.save(path)
    loaded = ServerConfig.load(path)
    assert loaded.ssl_certfile == "/path/cert.pem"
    assert loaded.ssl_keyfile == "/path/key.pem"


def test_env_var_override(tmp_path, monkeypatch):
    custom_path = tmp_path / "custom.json"
    monkeypatch.setenv("SITEDROP_CONFIG", str(custom_path))
    # Re-import to pick up env var
    import importlib
    import sitedrop.server.config as cfg

    importlib.reload(cfg)
    assert cfg.DEFAULT_CONFIG_PATH == custom_path
    # Restore
    monkeypatch.delenv("SITEDROP_CONFIG", raising=False)
    importlib.reload(cfg)


def test_config_version_default():
    config = ServerConfig()
    assert config.config_version == CURRENT_CONFIG_VERSION


def test_save_includes_config_version(tmp_path):
    path = tmp_path / "server.json"
    ServerConfig().save(path)
    data = json.loads(path.read_text())
    assert data["config_version"] == CURRENT_CONFIG_VERSION


def test_load_old_config_gets_migrated(tmp_path):
    path = tmp_path / "server.json"
    path.write_text(json.dumps({"password_hash": "old"}))
    config = ServerConfig.load(path)
    assert config.config_version == CURRENT_CONFIG_VERSION
    assert config.password_hash == "old"


def test_load_current_version_no_migration(tmp_path):
    path = tmp_path / "server.json"
    path.write_text(
        json.dumps({"password_hash": "x", "config_version": CURRENT_CONFIG_VERSION})
    )
    config = ServerConfig.load(path)
    assert config.config_version == CURRENT_CONFIG_VERSION
    assert config.password_hash == "x"


def test_load_future_version_warns(tmp_path, caplog):
    path = tmp_path / "server.json"
    path.write_text(json.dumps({"password_hash": "x", "config_version": 99}))
    with caplog.at_level(logging.WARNING):
        config = ServerConfig.load(path)
    assert config.config_version == 99
    assert "newer than supported" in caplog.text


def test_migrate_config_function():
    data = {"password_hash": "test"}
    result = _migrate_config(data)
    assert result["config_version"] == CURRENT_CONFIG_VERSION
    assert result["password_hash"] == "test"
