# Copyright © The Debusine Developers
# See the AUTHORS file at the top-level directory of this distribution
#
# This file is part of Debusine. It is subject to the license terms
# in the LICENSE file found in the top-level directory of this
# distribution. No part of Debusine, including this file, may be copied,
# modified, propagated, or distributed except according to the terms
# contained in the LICENSE file.

"""Tests for the open metrics view."""

from collections.abc import Callable, Iterable
from typing import assert_never, cast

from django.test import SimpleTestCase
from django.urls import reverse
from prometheus_client import CONTENT_TYPE_LATEST as PROMETHEUS_CONTENT_TYPE
from prometheus_client import Metric
from prometheus_client.openmetrics.exposition import (
    CONTENT_TYPE_LATEST as OPENMETRICS_CONTENT_TYPE,
)
from prometheus_client.openmetrics.parser import (
    text_string_to_metric_families as openmetrics_parser,
)
from prometheus_client.parser import (
    text_string_to_metric_families as prometheus_parser,
)

from debusine.server.views.open_metrics import (
    RendererFormats,
    extract_media_type,
)
from debusine.test.django import TestCase


class HelperTests(SimpleTestCase):
    """Tests for simple helper methods."""

    def test_extract_media_type(self) -> None:
        self.assertEqual(
            extract_media_type("text/plain; version=1; charset=utf-8"),
            "text/plain",
        )


class OpenMetricsViewTests(TestCase):
    """Tests for OpenMetricsView."""

    def get_metrics(
        self, renderer_format: RendererFormats = RendererFormats.PROMETHEUS
    ) -> dict[str, Metric]:
        headers: dict[str, str]
        expected_content_type: str
        parser: Callable[[str], Iterable[Metric]]

        match renderer_format:
            case RendererFormats.PROMETHEUS:
                headers = {}
                expected_content_type = PROMETHEUS_CONTENT_TYPE
                parser = prometheus_parser
            case RendererFormats.OPENMETRICS:
                headers = {"Accept": "application/openmetrics-text"}
                expected_content_type = OPENMETRICS_CONTENT_TYPE
                parser = cast(
                    Callable[[str], Iterable[Metric]], openmetrics_parser
                )
            case _ as unreachable:
                assert_never(unreachable)

        response = self.client.get(reverse("api:open-metrics"), headers=headers)
        self.assertEqual(response.status_code, 200)
        self.assertEqual(
            response.headers["Content-Type"], expected_content_type
        )
        return {
            metric.name: metric for metric in parser(response.content.decode())
        }

    def test_output_headers_prometheus(self) -> None:
        self.get_metrics(RendererFormats.PROMETHEUS)

    def test_output_headers_openmetrics(self) -> None:
        self.get_metrics(RendererFormats.OPENMETRICS)

    def test_django_prometheus_metrics_registered(self) -> None:
        self.client.get(reverse("homepage:homepage"))

        metrics = self.get_metrics()
        responses = {
            (
                sample.labels["status"],
                sample.labels["view"],
                sample.labels["method"],
            ): sample.value
            for sample in metrics[
                "django_http_responses_total_by_status_view_method"
            ].samples
        }
        self.assertGreaterEqual(
            responses[("200", "homepage:homepage", "GET")], 1.0
        )
