"""Тесты сценариев CryptoBot в PaymentService."""

import sys
from datetime import UTC, datetime
from pathlib import Path
from typing import Any

import pytest


ROOT_DIR = Path(__file__).resolve().parents[2]
if str(ROOT_DIR) not in sys.path:
    sys.path.insert(0, str(ROOT_DIR))

from app.config import settings
from app.database.crud import cryptobot as cryptobot_crud
from app.services.payment_service import PaymentService


@pytest.fixture
def anyio_backend() -> str:
    return 'asyncio'


class DummySession:
    def __init__(self) -> None:
        self.added_objects: list[Any] = []

    async def commit(self) -> None:  # pragma: no cover
        return None

    def add(self, obj: Any) -> None:  # pragma: no cover
        self.added_objects.append(obj)

    async def flush(self) -> None:  # pragma: no cover
        return None


class DummyLocalPayment:
    def __init__(self, payment_id: int = 888) -> None:
        self.id = payment_id
        self.created_at = datetime(2024, 3, 1, 9, 0, 0, tzinfo=UTC)


class StubCryptoBotService:
    def __init__(self, response: dict[str, Any] | None) -> None:
        self.response = response
        self.calls: list[dict[str, Any]] = []

    async def create_invoice(self, **kwargs: Any) -> dict[str, Any] | None:
        self.calls.append(kwargs)
        return self.response


def _make_service(stub: StubCryptoBotService | None) -> PaymentService:
    service = PaymentService.__new__(PaymentService)  # type: ignore[call-arg]
    service.bot = None
    service.cryptobot_service = stub
    service.mulenpay_service = None
    service.pal24_service = None
    service.yookassa_service = None
    service.stars_service = None
    return service


@pytest.mark.anyio('asyncio')
async def test_create_cryptobot_payment_success(monkeypatch: pytest.MonkeyPatch) -> None:
    response = {
        'invoice_id': 12345,
        'bot_invoice_url': 'https://t.me/invoice',
        'mini_app_invoice_url': 'https://mini.app/invoice',
        'web_app_invoice_url': 'https://web.app/invoice',
    }
    stub = StubCryptoBotService(response)
    service = _make_service(stub)
    db = DummySession()

    captured_args: dict[str, Any] = {}

    async def fake_create_cryptobot_payment(**kwargs: Any) -> DummyLocalPayment:
        captured_args.update(kwargs)
        return DummyLocalPayment(payment_id=555)

    monkeypatch.setattr(
        cryptobot_crud,
        'create_cryptobot_payment',
        fake_create_cryptobot_payment,
        raising=False,
    )
    monkeypatch.setattr(
        type(settings),
        'get_cryptobot_invoice_expires_seconds',
        lambda self: 600,
        raising=False,
    )

    result = await service.create_cryptobot_payment(
        db=db,
        user_id=9,
        amount_usd=12.5,
        asset='USDT',
        description='Пополнение',
        payload='custom',
    )

    assert result is not None
    assert result['local_payment_id'] == 555
    assert result['invoice_id'] == '12345'
    assert result['bot_invoice_url'] == 'https://t.me/invoice'
    assert stub.calls and stub.calls[0]['expires_in'] == 600
    assert captured_args['invoice_id'] == '12345'
    assert captured_args['amount'] == '12.50'


@pytest.mark.anyio('asyncio')
async def test_create_cryptobot_payment_returns_none_when_service_missing() -> None:
    service = _make_service(None)
    db = DummySession()
    result = await service.create_cryptobot_payment(
        db=db,
        user_id=1,
        amount_usd=10,
    )
    assert result is None


@pytest.mark.anyio('asyncio')
async def test_create_cryptobot_payment_handles_empty_response(monkeypatch: pytest.MonkeyPatch) -> None:
    stub = StubCryptoBotService(response=None)
    service = _make_service(stub)
    db = DummySession()

    called = False

    async def fake_create_cryptobot_payment(**kwargs: Any) -> DummyLocalPayment:
        nonlocal called
        called = True
        return DummyLocalPayment()

    monkeypatch.setattr(
        cryptobot_crud,
        'create_cryptobot_payment',
        fake_create_cryptobot_payment,
        raising=False,
    )

    result = await service.create_cryptobot_payment(
        db=db,
        user_id=1,
        amount_usd=5,
    )
    assert result is None
    assert called is False
