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

from django.test import SimpleTestCase

from ocyan.plugin.coyote import utils


class DummyResponse:
    def __init__(self, payload):
        self._payload = payload

    def raise_for_status(self):
        return None

    def json(self):
        return self._payload


class GetGoogleFontsTests(SimpleTestCase):
    def setUp(self):
        super().setUp()
        self.tmpdir = tempfile.TemporaryDirectory()
        self.cache_path = os.path.join(self.tmpdir.name, "google-fonts.json")

    def tearDown(self):
        self.tmpdir.cleanup()
        super().tearDown()

    def _write_cache(self, payload, age_seconds):
        with open(self.cache_path, "w", encoding="utf-8") as handle:
            json.dump(payload, handle)
        stale_time = time.time() - age_seconds
        os.utime(self.cache_path, (stale_time, stale_time))

    def test_uses_fresh_cache_without_network(self):
        payload = [{"family": "Inter"}]
        self._write_cache(payload, age_seconds=30)

        with self.settings(
            COYOTE_GOOGLE_FONTS_CACHE_PATH=self.cache_path,
            COYOTE_GOOGLE_FONTS_CACHE_TTL_SECONDS=300,
            GOOGLE_FONTS_API_KEY="dummy",
        ):
            with mock.patch("ocyan.plugin.coyote.utils.requests.get") as mocked_get:
                result = utils.get_google_fonts()

        self.assertEqual(result, payload)
        mocked_get.assert_not_called()

    def test_refreshes_expired_cache_from_api(self):
        self._write_cache([{"family": "Old"}], age_seconds=3600)
        api_payload = [{"family": "Roboto"}]

        with self.settings(
            COYOTE_GOOGLE_FONTS_CACHE_PATH=self.cache_path,
            COYOTE_GOOGLE_FONTS_CACHE_TTL_SECONDS=10,
            GOOGLE_FONTS_API_KEY="dummy",
        ):
            with mock.patch(
                "ocyan.plugin.coyote.utils.requests.get",
                return_value=DummyResponse({"items": api_payload}),
            ) as mocked_get:
                result = utils.get_google_fonts()

        self.assertEqual(result, api_payload)
        mocked_get.assert_called_once()
        with open(self.cache_path, "r", encoding="utf-8") as handle:
            self.assertEqual(json.load(handle), api_payload)

    def test_uses_stale_cache_when_api_fails(self):
        stale_payload = [{"family": "Fallback"}]
        self._write_cache(stale_payload, age_seconds=7200)

        with self.settings(
            COYOTE_GOOGLE_FONTS_CACHE_PATH=self.cache_path,
            COYOTE_GOOGLE_FONTS_CACHE_TTL_SECONDS=30,
            GOOGLE_FONTS_API_KEY="dummy",
        ):
            with mock.patch(
                "ocyan.plugin.coyote.utils.requests.get",
                side_effect=utils.requests.RequestException("boom"),
            ):
                result = utils.get_google_fonts()

        self.assertEqual(result, stale_payload)

    def test_returns_empty_when_api_key_is_missing(self):
        with self.settings(
            COYOTE_GOOGLE_FONTS_CACHE_PATH=self.cache_path,
            COYOTE_GOOGLE_FONTS_CACHE_TTL_SECONDS=30,
            GOOGLE_FONTS_API_KEY="",
        ):
            with mock.patch("ocyan.plugin.coyote.utils.requests.get") as mocked_get:
                result = utils.get_google_fonts()

        self.assertEqual(result, [])
        mocked_get.assert_not_called()
