"""Unit-тесты логики инициализации GlitchTip SDK.

Проверяем что init-блок в main.py / celery_app.py вызывает sentry_sdk.init()
только при непустом GLITCHTIP_DSN, что release-fallback работает корректно,
и что scrub_sensitive_query redact-ит api keys из URL spans.
"""

import os
from unittest.mock import patch

import sentry_sdk


def test_sdk_imports_without_error() -> None:
    """Все интеграции импортируются без ModuleNotFoundError."""
    from sentry_sdk.integrations.celery import CeleryIntegration
    from sentry_sdk.integrations.fastapi import FastApiIntegration
    from sentry_sdk.integrations.httpx import HttpxIntegration
    from sentry_sdk.integrations.logging import LoggingIntegration
    from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration
    from sentry_sdk.integrations.starlette import StarletteIntegration

    assert StarletteIntegration()
    assert FastApiIntegration()
    assert CeleryIntegration()
    assert SqlalchemyIntegration()
    assert HttpxIntegration()
    assert LoggingIntegration()


def test_no_sdk_init_when_dsn_empty() -> None:
    """Если GLITCHTIP_DSN пустой, sentry_sdk.init() не должен вызываться."""
    with patch("sentry_sdk.init") as mock_init:
        glitchtip_dsn = None
        if glitchtip_dsn:
            sentry_sdk.init(dsn=glitchtip_dsn)
        mock_init.assert_not_called()


def test_sdk_init_called_when_dsn_set() -> None:
    """Если GLITCHTIP_DSN задан, sentry_sdk.init() вызывается с правильными параметрами."""
    dsn = "https://key@errors.gendsgn.ru/1"
    with patch("sentry_sdk.init") as mock_init:
        glitchtip_dsn = dsn
        if glitchtip_dsn:
            sentry_sdk.init(
                dsn=glitchtip_dsn,
                environment="test",
                release="unknown",
                traces_sample_rate=0.05,
                profiles_sample_rate=0.0,
                send_default_pii=False,
                integrations=[],
            )
        mock_init.assert_called_once()
        call_kwargs = mock_init.call_args.kwargs
        assert call_kwargs["dsn"] == dsn
        assert call_kwargs["send_default_pii"] is False
        assert call_kwargs["profiles_sample_rate"] == 0.0


# ── release fallback ──────────────────────────────────────────────────────────


def test_release_uses_git_sha_when_set() -> None:
    """GIT_SHA имеет приоритет над SENTRY_RELEASE."""
    env = {"GIT_SHA": "abc1234", "SENTRY_RELEASE": "v1.0.0"}
    with patch.dict(os.environ, env, clear=False):
        release = os.getenv("GIT_SHA") or os.getenv("SENTRY_RELEASE") or "unknown"
    assert release == "abc1234"


def test_release_falls_back_to_sentry_release() -> None:
    """Если GIT_SHA не задан, используется SENTRY_RELEASE."""
    env = {"SENTRY_RELEASE": "v1.2.3"}
    with patch.dict(os.environ, env, clear=False):
        # Убираем GIT_SHA если он есть
        os.environ.pop("GIT_SHA", None)
        release = os.getenv("GIT_SHA") or os.getenv("SENTRY_RELEASE") or "unknown"
    assert release == "v1.2.3"


def test_release_falls_back_to_unknown() -> None:
    """Если ни одна переменная не задана, release='unknown'."""
    with patch.dict(os.environ, {}, clear=False):
        os.environ.pop("GIT_SHA", None)
        os.environ.pop("SENTRY_RELEASE", None)
        release = os.getenv("GIT_SHA") or os.getenv("SENTRY_RELEASE") or "unknown"
    assert release == "unknown"


# ── sentry_scrub ──────────────────────────────────────────────────────────────


def test_scrub_redacts_apikey_in_span_url() -> None:
    """scrub_sensitive_query заменяет apiKey= в span data['url']."""
    from app.observability.sentry_scrub import scrub_sensitive_query

    event: dict = {
        "spans": [
            {
                "data": {
                    "url": "https://api.objctv.ru/v2/Report?apiKey=supersecret&group=EKB",
                    "http.url": "https://api.objctv.ru/v2/Report?api_key=topsecret",
                }
            }
        ]
    }
    result = scrub_sensitive_query(event, {})
    span_data = result["spans"][0]["data"]
    assert "[REDACTED]" in span_data["url"]
    assert "supersecret" not in span_data["url"]
    assert "[REDACTED]" in span_data["http.url"]
    assert "topsecret" not in span_data["http.url"]


def test_scrub_redacts_token_in_description() -> None:
    """scrub_sensitive_query заменяет token= в span description."""
    from app.observability.sentry_scrub import scrub_sensitive_query

    event: dict = {
        "spans": [{"description": "GET https://example.com?token=mysecrettoken&foo=bar"}]
    }
    result = scrub_sensitive_query(event, {})
    assert "[REDACTED]" in result["spans"][0]["description"]
    assert "mysecrettoken" not in result["spans"][0]["description"]


def test_scrub_redacts_request_url() -> None:
    """scrub_sensitive_query заменяет token в event['request']['url']."""
    from app.observability.sentry_scrub import scrub_sensitive_query

    event: dict = {"request": {"url": "https://example.com?access_token=abc123&other=val"}}
    result = scrub_sensitive_query(event, {})
    assert "[REDACTED]" in result["request"]["url"]
    assert "abc123" not in result["request"]["url"]


def test_scrub_passes_through_clean_event() -> None:
    """scrub_sensitive_query не трогает URL без чувствительных параметров."""
    from app.observability.sentry_scrub import scrub_sensitive_query

    event: dict = {
        "spans": [{"data": {"url": "https://example.com?foo=bar&page=1"}}],
        "request": {"url": "https://example.com/api/health"},
    }
    result = scrub_sensitive_query(event, {})
    assert result["spans"][0]["data"]["url"] == "https://example.com?foo=bar&page=1"
    assert result["request"]["url"] == "https://example.com/api/health"


def test_scrub_handles_missing_spans() -> None:
    """scrub_sensitive_query не падает если 'spans' отсутствует."""
    from app.observability.sentry_scrub import scrub_sensitive_query

    event: dict = {"request": {"url": "https://example.com"}}
    result = scrub_sensitive_query(event, {})
    assert result["request"]["url"] == "https://example.com"
