"""Tests for myburn.auth — config management, login/logout flows."""
from __future__ import annotations

import json
import tempfile
import unittest
from pathlib import Path
from unittest.mock import MagicMock, patch

import myburn.auth as auth


class TestConfigHelpers(unittest.TestCase):
    """Test load_config / save_config / get_token / get_username."""

    def setUp(self):
        self._tmpdir = tempfile.mkdtemp()
        self._orig_dir = auth.CONFIG_DIR
        self._orig_path = auth.CONFIG_PATH
        auth.CONFIG_DIR = Path(self._tmpdir)
        auth.CONFIG_PATH = Path(self._tmpdir) / "config.json"

    def tearDown(self):
        auth.CONFIG_DIR = self._orig_dir
        auth.CONFIG_PATH = self._orig_path

    def test_load_missing(self):
        """Missing config returns empty dict."""
        self.assertEqual(auth.load_config(), {})

    def test_save_and_load(self):
        cfg = {"token": "abc123", "username": "testuser"}
        auth.save_config(cfg)
        loaded = auth.load_config()
        self.assertEqual(loaded["token"], "abc123")
        self.assertEqual(loaded["username"], "testuser")

    def test_save_creates_dir(self):
        auth.CONFIG_DIR = Path(self._tmpdir) / "sub" / "dir"
        auth.CONFIG_PATH = auth.CONFIG_DIR / "config.json"
        auth.save_config({"token": "x"})
        self.assertTrue(auth.CONFIG_PATH.exists())

    def test_load_corrupt_json(self):
        auth.CONFIG_PATH.write_text("not json{{{")
        self.assertEqual(auth.load_config(), {})

    def test_get_token(self):
        auth.save_config({"token": "mytoken"})
        self.assertEqual(auth.get_token(), "mytoken")

    def test_get_token_missing(self):
        self.assertIsNone(auth.get_token())

    def test_get_username(self):
        auth.save_config({"username": "gekko"})
        self.assertEqual(auth.get_username(), "gekko")

    def test_get_username_missing(self):
        self.assertIsNone(auth.get_username())

    def test_overwrite_config(self):
        auth.save_config({"token": "old"})
        auth.save_config({"token": "new"})
        self.assertEqual(auth.load_config()["token"], "new")


class TestLogout(unittest.TestCase):
    """Test logout flow."""

    def setUp(self):
        self._tmpdir = tempfile.mkdtemp()
        self._orig_dir = auth.CONFIG_DIR
        self._orig_path = auth.CONFIG_PATH
        auth.CONFIG_DIR = Path(self._tmpdir)
        auth.CONFIG_PATH = Path(self._tmpdir) / "config.json"

    def tearDown(self):
        auth.CONFIG_DIR = self._orig_dir
        auth.CONFIG_PATH = self._orig_path

    def test_logout_clears_token(self):
        auth.save_config({
            "token": "abc",
            "username": "testuser",
            "avatar_url": "http://example.com/avatar.png",
            "logged_in_at": "2026-02-25T00:00:00Z",
        })
        auth.logout()
        cfg = auth.load_config()
        self.assertNotIn("token", cfg)
        self.assertNotIn("username", cfg)
        self.assertNotIn("avatar_url", cfg)
        self.assertNotIn("logged_in_at", cfg)

    def test_logout_when_not_logged_in(self):
        # Should not raise
        auth.logout()


class TestLogin(unittest.TestCase):
    """Test login flow with mocked HTTP."""

    def setUp(self):
        self._tmpdir = tempfile.mkdtemp()
        self._orig_dir = auth.CONFIG_DIR
        self._orig_path = auth.CONFIG_PATH
        auth.CONFIG_DIR = Path(self._tmpdir)
        auth.CONFIG_PATH = Path(self._tmpdir) / "config.json"

    def tearDown(self):
        auth.CONFIG_DIR = self._orig_dir
        auth.CONFIG_PATH = self._orig_path

    def test_login_already_logged_in(self):
        """If already logged in, should print message and return."""
        auth.save_config({"token": "existing", "username": "already"})
        # Should not raise, not try any HTTP
        auth.login()
        # Config should be unchanged
        self.assertEqual(auth.get_token(), "existing")

    @patch("myburn.auth._device_flow")
    @patch("myburn.auth._post_json")
    def test_login_success(self, mock_post_json, mock_device_flow):
        """Successful login saves config."""
        mock_device_flow.return_value = "gh_token_abc"
        mock_post_json.return_value = {
            "token": "myburn_token_xyz",
            "username": "testdev",
            "avatar_url": "https://github.com/avatars/123",
        }

        auth.login()

        cfg = auth.load_config()
        self.assertEqual(cfg["token"], "myburn_token_xyz")
        self.assertEqual(cfg["username"], "testdev")
        self.assertEqual(cfg["avatar_url"], "https://github.com/avatars/123")
        self.assertIn("logged_in_at", cfg)


class TestPostHelpers(unittest.TestCase):
    """Test HTTP helper edge cases."""

    @patch("urllib.request.urlopen")
    def test_post_json(self, mock_urlopen):
        mock_resp = MagicMock()
        mock_resp.read.return_value = b'{"ok": true}'
        mock_resp.__enter__ = MagicMock(return_value=mock_resp)
        mock_resp.__exit__ = MagicMock(return_value=False)
        mock_urlopen.return_value = mock_resp

        result = auth._post_json("https://example.com/api", {"key": "val"})
        self.assertEqual(result, {"ok": True})

    @patch("urllib.request.urlopen")
    def test_post_form(self, mock_urlopen):
        mock_resp = MagicMock()
        mock_resp.read.return_value = b'{"device_code": "abc"}'
        mock_resp.__enter__ = MagicMock(return_value=mock_resp)
        mock_resp.__exit__ = MagicMock(return_value=False)
        mock_urlopen.return_value = mock_resp

        result = auth._post_form("https://example.com/form", {"a": "1"})
        self.assertEqual(result, {"device_code": "abc"})


if __name__ == "__main__":
    unittest.main()
