"""Tests for zimba API client."""

import json
import os
import tempfile
import time
from unittest import mock

import pytest

from zimba.api import (
    _save_token,
    _load_token,
    _soap_request,
    load_config,
    save_config,
)


class TestConfig:
    def test_save_and_load(self, tmp_path):
        config_file = tmp_path / "config.json"
        with mock.patch("zimba.api.CONFIG_FILE", str(config_file)):
            save_config("https://mail.example.com", "user@example.com", "pass123")
            config = load_config()
        assert config["server"] == "https://mail.example.com"
        assert config["username"] == "user@example.com"
        assert config["password"] == "pass123"

    def test_load_missing(self, tmp_path):
        with mock.patch("zimba.api.CONFIG_FILE", str(tmp_path / "nope.json")):
            with pytest.raises(RuntimeError, match="No config found"):
                load_config()

    def test_config_permissions(self, tmp_path):
        config_file = tmp_path / "config.json"
        with mock.patch("zimba.api.CONFIG_FILE", str(config_file)):
            with mock.patch("zimba.api.CONFIG_DIR", str(tmp_path)):
                save_config("https://mail.example.com", "user@example.com", "pass")
        assert oct(os.stat(config_file).st_mode)[-3:] == "600"


class TestToken:
    def test_save_and_load(self, tmp_path):
        token_file = tmp_path / "token.json"
        with mock.patch("zimba.api.TOKEN_FILE", str(token_file)):
            with mock.patch("zimba.api.CONFIG_DIR", str(tmp_path)):
                _save_token("test-token-123", 3600000)
                assert _load_token() == "test-token-123"

    def test_expired_token(self, tmp_path):
        token_file = tmp_path / "token.json"
        with mock.patch("zimba.api.TOKEN_FILE", str(token_file)):
            with mock.patch("zimba.api.CONFIG_DIR", str(tmp_path)):
                _save_token("old-token", 1000)  # 1 second lifetime
                # Manually expire it
                with open(token_file, "w") as f:
                    json.dump({"token": "old-token", "expires": time.time() - 10}, f)
                assert _load_token() is None

    def test_missing_token(self, tmp_path):
        with mock.patch("zimba.api.TOKEN_FILE", str(tmp_path / "nope.json")):
            assert _load_token() is None


class TestSoapRequest:
    def test_builds_envelope(self):
        """Test that SOAP envelope is built correctly."""
        with mock.patch("urllib.request.urlopen") as mock_urlopen:
            mock_resp = mock.MagicMock()
            mock_resp.read.return_value = json.dumps({
                "Body": {"AuthResponse": {"authToken": [{"_content": "tok"}]}}
            }).encode()
            mock_resp.__enter__ = lambda s: s
            mock_resp.__exit__ = mock.Mock(return_value=False)
            mock_urlopen.return_value = mock_resp

            result = _soap_request(
                "https://mail.example.com/service/soap",
                "urn:zimbraAccount",
                "AuthRequest",
                {"account": {"_content": "user"}},
                token="test-token",
            )

            # Verify the request body
            call_args = mock_urlopen.call_args
            req = call_args[0][0]
            body = json.loads(req.data)
            assert body["Body"]["AuthRequest"]["_jsns"] == "urn:zimbraAccount"
            assert body["Header"]["context"]["authToken"]["_content"] == "test-token"
