"""Mock VictoriaMetrics server for testing."""

from __future__ import annotations

import anyio
import os
import tempfile
from contextlib import asynccontextmanager

import httpx

from asyncvictoria import connect
from asyncvictoria import get_data as _get_data
from asyncvictoria import get_max_ts as _get_max_ts

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from anyio import Process

    from collections.abc import AsyncGenerator


class VictoriaTester:
    """Test harness for VictoriaMetrics."""

    _client = None
    _server = None
    _session = None

    def __init__(self, tcp_port: int | None = None, http_port: int | None = None) -> None:
        if tcp_port is None:
            tcp_port = (os.getpid() + 23) % 10000 + 40000
        if http_port is None:
            http_port = (os.getpid() + 24) % 10000 + 40000
        self.TCP_PORT = tcp_port
        self.HTTP_PORT = http_port
        self.URL = f"http://localhost:{http_port}/api/v1/query"

    @asynccontextmanager
    async def _daemon(
        self, http: int | None = None, tcp: int | None = None
    ) -> AsyncGenerator[Process, None]:
        """Start VictoriaMetrics daemon for testing."""
        if http is None:
            http = self.HTTP_PORT
        if tcp is None:
            tcp = self.TCP_PORT

        async with anyio.create_task_group():
            with tempfile.TemporaryDirectory() as d:
                print(f"VictoriaMetrics data dir: {d}")

                # Start VictoriaMetrics with OpenTSDB and HTTP ports
                async with await anyio.open_process([
                    "victoria-metrics",
                    f"-storageDataPath={d}",
                    f"-httpListenAddr=:{http}",
                    f"-opentsdbListenAddr=:{tcp}",
                    "-retentionPeriod=99y",
                ]) as proc:
                    try:
                        # Wait for VictoriaMetrics to start
                        with anyio.fail_after(10):
                            while True:
                                try:
                                    # Check if OpenTSDB port is ready
                                    async with await anyio.connect_tcp("127.0.0.1", tcp):
                                        pass
                                    # Check if HTTP port is ready
                                    async with httpx.AsyncClient() as client:
                                        await client.get(f"http://127.0.0.1:{http}/health")
                                    break
                                except (OSError, httpx.HTTPError):
                                    await anyio.sleep(0.1)
                        yield proc
                    finally:
                        proc.terminate()
                        with anyio.move_on_after(2, shield=True):
                            await proc.wait()
                        if proc.returncode is None:
                            proc.kill()

    @asynccontextmanager
    async def run(self) -> AsyncGenerator[VictoriaTester, None]:
        """Start VictoriaMetrics and create test client."""
        limits = httpx.Limits(max_keepalive_connections=1, max_connections=3)
        async with (
            self._daemon() as _server,
            httpx.AsyncClient(timeout=600, limits=limits) as session,
            connect(port=self.TCP_PORT) as client,
        ):
            self._server = _server
            self._session = session
            self._client = client
            yield self

    @property
    def port(self):  # type: ignore[no-untyped-def]
        """Get the OpenTSDB client."""
        return self._client

    def get_data(self, *a, **k):  # type: ignore[no-untyped-def]
        """Get data from VictoriaMetrics."""
        k.setdefault("url", self.URL)
        return _get_data(self._session, *a, **k)

    def get_max_ts(self, *a, **k):  # type: ignore[no-untyped-def]
        """Get maximum timestamp for a series."""
        k.setdefault("url", self.URL)
        return _get_max_ts(self._session, *a, **k)

    def preload(self, *a, **k):  # type: ignore[no-untyped-def]
        """Preload series metadata."""
        return self._client.preload(*a, **k)

    def flush_dict(self, *a, **k):  # type: ignore[no-untyped-def]
        """Flush preloaded metadata."""
        return self._client.flush_dict(*a, **k)

    def put(self, *a, **k):  # type: ignore[no-untyped-def]
        """Put data into VictoriaMetrics."""
        return self._client.put(*a, **k)

    def flush(self, *a, **k):  # type: ignore[no-untyped-def]
        """Flush buffered data."""
        return self._client.flush(*a, **k)

    async def force_flush(self) -> None:
        """Force VictoriaMetrics to flush all pending data to disk."""
        async with httpx.AsyncClient() as client:
            url = f"http://localhost:{self.HTTP_PORT}/internal/force_flush"
            response = await client.get(url)
            response.raise_for_status()
