import json
import pytest

from psi_grafana.grafana_client import GrafanaClient
from psi_utils.unittest_mock import MockApi


class TestGrafanaClient_UnitTests:
    @pytest.mark.unit
    def test_init(self):
        with pytest.raises(AssertionError) as e_info:
            GrafanaClient(api_key="test-key", base_url="")
        assert str(e_info.value) == "must include a base URL when initializing GrafanaClient"

    @pytest.mark.unit
    def test_get_dashboard(self):
        # Set expected values
        expected_api_key = "test-key"
        expected_uid = "test-uid"
        expected_base_url = "https://test.grafana.net"
        expected_url = f"{expected_base_url}/api/dashboards/uid/{expected_uid}"
        expected_dashboard = {
            "id": 1,
            "uid": expected_uid,
            "title": "mock-dashboard",
            "tags": ["mock"],
            "timezone": "browser",
            "schemaVersion": 16,
            "version": 0,
        }
        expected_response = {
            "dashboard": expected_dashboard,
            "meta": {
                "isStarred": False,
                "url": f"/d/{expected_uid}/mock-dashboard",
                "folderId": 2,
                "folderUid": "mockFolderUID",
                "slug": "mock-dashboard",
            },
        }

        # Set up mocks
        mock_resp = MockApi()
        mock_resp.on("json").return_val(expected_response)
        mock_resp.on("raise_for_status").return_val(None)
        mock_session = MockApi()
        setattr(mock_session, "headers", {})
        mock_session.on(
            "request", "get", expected_url, params=None, headers=None, data=json.dumps(None)
        ).return_val(mock_resp)

        # Test get_dashboard()
        grafana = GrafanaClient(
            api_key=expected_api_key, base_url=expected_base_url, http_session=mock_session
        )
        dashboard = grafana.get_dashboard(expected_uid)
        assert dashboard == expected_dashboard

        # Test get_dashboard() with metadata
        response = grafana.get_dashboard(expected_uid, include_metadata=True)
        assert response == expected_response
        mock_session.assert_expectations()

    @pytest.mark.unit
    def test_create_dashboard(self):
        # Set expected values
        expected_api_key = "test-key"
        expected_uid = "test-uid"
        expected_folder_uid = "mock-folder-uid"
        expected_base_url = "https://test.grafana.net"
        expected_url = f"{expected_base_url}/api/dashboards/db"
        expected_response = {
            "id": 1,
            "uid": expected_uid,
            "url": f"/d/{expected_uid}/mock-dashboard",
            "status": "success",
            "version": 6,
            "slug": "mock-dashboard",
        }
        expected_dashboard = {
            "annotations": {"list": []},
            "editable": True,
            "fiscalYearStartMonth": 0,
            "graphTooltip": 0,
            "links": [],
            "liveNow": False,
            "panels": [],
            "schemaVersion": 37,
            "style": "dark",
            "tags": [],
            "templating": {"list": []},
            "time": {"from": "now-6h", "to": "now"},
            "timepicker": {},
            "timezone": "",
            "title": "mock-dashboard",
            "version": 3,
            "weekStart": "",
        }
        expected_payload = {
            "dashboard": expected_dashboard,
            "folderUid": expected_folder_uid,
            "message": "Created by POE Python Grafana client",
            "overwrite": False,
        }
        initial_dashboard = expected_dashboard.copy()
        initial_dashboard["id"] = 123
        initial_dashboard["uid"] = "thisWillBeRemoved"
        # Set up mocks
        mock_resp = MockApi()
        mock_resp.on("json").return_val(expected_response)
        mock_resp.on("raise_for_status").return_val(None)
        mock_session = MockApi()
        setattr(mock_session, "headers", {})
        mock_session.on(
            "request",
            "post",
            expected_url,
            params=None,
            headers=None,
            data=json.dumps(expected_payload),
        ).return_val(mock_resp)

        # Test update_dashboard()
        grafana = GrafanaClient(
            api_key=expected_api_key, base_url=expected_base_url, http_session=mock_session
        )
        response = grafana.create_dashboard(initial_dashboard, expected_folder_uid)
        assert response == expected_response
        mock_session.assert_expectations()

    @pytest.mark.unit
    def test_update_dashboard(self):
        # Set expected values
        expected_api_key = "test-key"
        expected_uid = "test-uid"
        expected_folder_uid = "mock-folder-uid"
        expected_base_url = "https://test.grafana.net"
        expected_url = f"{expected_base_url}/api/dashboards/db"
        expected_response = {
            "id": 1,
            "uid": expected_uid,
            "url": f"/d/{expected_uid}/mock-dashboard",
            "status": "success",
            "version": 6,
            "slug": "mock-dashboard",
        }
        expected_dashboard = {
            "annotations": {"list": []},
            "editable": True,
            "fiscalYearStartMonth": 0,
            "graphTooltip": 0,
            "id": 12345,
            "links": [],
            "liveNow": False,
            "panels": [],
            "schemaVersion": 37,
            "style": "dark",
            "tags": [],
            "templating": {"list": []},
            "time": {"from": "now-6h", "to": "now"},
            "timepicker": {},
            "timezone": "",
            "title": "mock-dashboard",
            "uid": expected_uid,
            "version": 3,
            "weekStart": "",
        }
        expected_payload = {
            "dashboard": expected_dashboard,
            "folderUid": expected_folder_uid,
            "message": "Updated by POE Python Grafana client",
            "overwrite": True,
        }
        # Set up mocks
        mock_resp = MockApi()
        mock_resp.on("json").return_val(expected_response)
        mock_resp.on("raise_for_status").return_val(None)
        mock_session = MockApi()
        setattr(mock_session, "headers", {})
        mock_session.on(
            "request",
            "post",
            expected_url,
            params=None,
            headers=None,
            data=json.dumps(expected_payload),
        ).return_val(mock_resp)

        # Test update_dashboard()
        grafana = GrafanaClient(
            api_key=expected_api_key, base_url=expected_base_url, http_session=mock_session
        )
        response = grafana.update_dashboard(expected_dashboard, expected_folder_uid)
        assert response == expected_response

        # Test get_dashboard() with missing id/uid
        expected_dashboard["id"] = None
        expected_dashboard["uid"] = None
        with pytest.raises(AssertionError) as e_info:
            grafana.update_dashboard(expected_dashboard, expected_folder_uid)
        assert str(e_info.value) == "dashboard must include an id or uid to update"
        mock_session.assert_expectations()

    @pytest.mark.unit
    def test_get_dashboard_file(self):
        expected_file_path = "test/mock_dashboard.json"
        assert isinstance(GrafanaClient.get_dashboard_file(expected_file_path), dict)

    @pytest.mark.unit
    def test__make_request(self):
        grafana = GrafanaClient(api_key="test-key")

        with pytest.raises(AssertionError) as e_info:
            grafana._make_request("invalid method", "test/url/")
        assert str(e_info.value) == "method must be a valid HTTP method"
