"""Provider, router, failover, SMS platform tests."""
from __future__ import annotations

import uuid

import pytest
from httpx import AsyncClient

from app.providers import reset_provider_registry_for_tests
from app.tests.conftest import TENANT_A, tenant_headers


async def _create_provider(client, headers, *, name, priority, force_fail=False):
    return await client.post(
        "/api/v1/providers",
        headers=headers,
        json={
            "name": name,
            "provider_kind": "mock",
            "channel": "sms",
            "priority": priority,
            "credentials": {"force_fail": True} if force_fail else {},
            "max_retries": 1,
        },
    )


@pytest.mark.asyncio
async def test_provider_crud_and_status(client: AsyncClient):
    h = tenant_headers(TENANT_A)
    created = await _create_provider(client, h, name="Primary", priority=1)
    assert created.status_code == 201
    pid = created.json()["id"]

    listed = await client.get("/api/v1/providers", headers=h)
    assert listed.status_code == 200
    assert any(p["id"] == pid for p in listed.json())

    status = await client.get("/api/v1/providers/status", headers=h)
    assert status.status_code == 200
    assert any(p["id"] == pid for p in status.json())

    balance = await client.get(f"/api/v1/providers/{pid}/balance", headers=h)
    assert balance.status_code == 200

    health = await client.post(f"/api/v1/providers/{pid}/health", headers=h)
    assert health.status_code == 200
    assert health.json()["healthy"] is True


@pytest.mark.asyncio
async def test_sender_numbers(client: AsyncClient):
    h = tenant_headers(TENANT_A)
    res = await client.post(
        "/api/v1/providers/senders",
        headers=h,
        json={"channel": "sms", "value": "1000", "is_default": True},
    )
    assert res.status_code == 201
    listed = await client.get("/api/v1/providers/senders/list", headers=h)
    assert listed.status_code == 200
    assert any(s["value"] == "1000" for s in listed.json())


@pytest.mark.asyncio
async def test_sms_send_and_delivery_timeline(client: AsyncClient):
    h = tenant_headers(TENANT_A)
    await _create_provider(client, h, name="SMS", priority=1)
    send = await client.post(
        "/api/v1/messages/send",
        headers=h,
        json={
            "channel": "sms",
            "to_address": "+989121234567",
            "body": "hello",
            "process_immediately": True,
        },
    )
    assert send.status_code == 201
    messages = send.json()
    assert len(messages) == 1
    assert messages[0]["status"] == "sent"
    mid = messages[0]["id"]

    timeline = await client.get(f"/api/v1/messages/{mid}/timeline", headers=h)
    assert timeline.status_code == 200
    statuses = [e["status"] for e in timeline.json()]
    assert "sent" in statuses


@pytest.mark.asyncio
async def test_provider_failover(client: AsyncClient):
    mock = reset_provider_registry_for_tests()
    h = tenant_headers(uuid.uuid4())
    # Lower priority number = higher priority
    await _create_provider(client, h, name="Failing", priority=1, force_fail=True)
    await _create_provider(client, h, name="Backup", priority=2, force_fail=False)

    send = await client.post(
        "/api/v1/messages/send",
        headers=h,
        json={
            "channel": "sms",
            "to_address": "+989121111111",
            "body": "failover",
            "process_immediately": True,
        },
    )
    assert send.status_code == 201
    assert send.json()[0]["status"] == "sent"
    assert len(mock.sent) >= 1


@pytest.mark.asyncio
async def test_queue_and_dead_letter(client: AsyncClient):
    h = tenant_headers(uuid.uuid4())
    # Only failing provider — eventually dead letter after retries
    await _create_provider(client, h, name="AlwaysFail", priority=1, force_fail=True)
    send = await client.post(
        "/api/v1/messages/send",
        headers=h,
        json={
            "channel": "sms",
            "to_address": "+989122222222",
            "body": "will fail",
            "process_immediately": True,
        },
    )
    assert send.status_code == 201
    mid = send.json()[0]["id"]
    # First attempt fails then re-queues for retry (status may be queued or failed)
    assert send.json()[0]["status"] in {"failed", "queued"}

    # Force more queue processing for DLQ path (bypass backoff by reclaiming)
    for _ in range(8):
        await client.post("/api/v1/messages/queue/process?limit=20", headers=h)

    dlq = await client.get("/api/v1/messages/queue/dead-letters", headers=h)
    assert dlq.status_code == 200
    assert len(dlq.json()) >= 1

    msg = await client.get(f"/api/v1/messages/{mid}", headers=h)
    assert msg.json()["status"] == "failed"
