from datetime import UTC

import pytest
from django.http import JsonResponse

from aiservice.middleware.track_usage_middleware import TrackUsageMiddleware


def parse_json(response: JsonResponse):
    import json

    return json.loads(response.content.decode())


class FakeSubscription:
    def __init__(self, status="active", used=0, limit=100, lifetime=0, plan_type="free", current_period_end=None):
        from datetime import datetime, timedelta

        self.subscription_status = status
        self.optimizations_used = used
        self.optimizations_limit = limit
        self.total_lifetime_optimizations = lifetime
        self.plan_type = plan_type
        self.current_period_end = current_period_end or (datetime.now(UTC) + timedelta(days=30))

    async def asave(self, *args, **kwargs):
        return None


@pytest.fixture
def middleware():
    async def get_response(request):
        return JsonResponse({"ok": True})

    return TrackUsageMiddleware(get_response)


@pytest.mark.asyncio
async def test_requires_authentication(middleware, rf):
    request = rf.get("/ai/optimize")
    request.user = None

    response = await middleware(request)
    data = parse_json(response)

    assert response.status_code == 401
    assert "Authentication required" in data["error"]


@pytest.mark.asyncio
async def test_creates_subscription_for_new_user(middleware, rf, monkeypatch):
    request = rf.get("/ai/optimize")
    request.user = type("User", (), {"id": 1})()

    class FakeFilter:
        async def afirst(self):
            return None

    monkeypatch.setattr(
        "aiservice.middleware.track_usage_middleware.Subscriptions.objects.filter", lambda **kwargs: FakeFilter()
    )

    fake_sub = FakeSubscription()

    async def fake_acreate(**kwargs):
        return fake_sub

    monkeypatch.setattr("aiservice.middleware.track_usage_middleware.Subscriptions.objects.acreate", fake_acreate)

    response = await middleware(request)
    data = parse_json(response)

    assert response.status_code == 200
    assert data["ok"] is True


@pytest.mark.asyncio
async def test_blocks_inactive_subscription(middleware, rf, monkeypatch):
    request = rf.get("/ai/optimize")
    request.user = type("User", (), {"id": 1})()

    fake_sub = FakeSubscription(status="inactive")

    class FakeFilter:
        async def afirst(self):
            return fake_sub

    monkeypatch.setattr(
        "aiservice.middleware.track_usage_middleware.Subscriptions.objects.filter", lambda **kwargs: FakeFilter()
    )

    response = await middleware(request)
    data = parse_json(response)

    assert response.status_code == 403
    assert data["status"] == "inactive"


@pytest.mark.asyncio
async def test_usage_limit_exceeded(middleware, rf, monkeypatch):
    request = rf.get("/ai/optimize")
    request.user = type("User", (), {"id": 1})()

    fake_sub = FakeSubscription(used=101, limit=100)

    class FakeFilter:
        async def afirst(self):
            return fake_sub

    monkeypatch.setattr(
        "aiservice.middleware.track_usage_middleware.Subscriptions.objects.filter", lambda **kwargs: FakeFilter()
    )

    response = await middleware(request)
    data = parse_json(response)

    assert response.status_code == 403
    assert "Usage limit exceeded" in data["error"]


@pytest.mark.asyncio
async def test_allows_usage_within_limit(middleware, rf, monkeypatch):
    request = rf.get("/ai/optimize")
    request.user = type("User", (), {"id": 1})()

    fake_sub = FakeSubscription(used=50, limit=100, lifetime=10)
    # After atomic update, the new used value would be 50 + 10 (optimize cost) = 60
    updated_sub = FakeSubscription(used=60, limit=100, lifetime=20)

    # Track whether aupdate has been called to determine which subscription to return
    update_called = {"value": False}

    class FakeFilter:
        async def afirst(self):
            # Return original sub before update, updated sub after update
            if update_called["value"]:
                return updated_sub
            return fake_sub

        async def aupdate(self, **kwargs):
            # Simulate atomic update - mark that update was called
            update_called["value"] = True
            return 1

    monkeypatch.setattr(
        "aiservice.middleware.track_usage_middleware.Subscriptions.objects.filter", lambda **kwargs: FakeFilter()
    )

    response = await middleware(request)
    data = parse_json(response)

    assert response.status_code == 200
    assert data["ok"] is True


@pytest.mark.asyncio
async def test_internal_error_handled(middleware, rf, monkeypatch):
    request = rf.get("/ai/optimize")
    request.user = type("User", (), {"id": 1})()

    class FakeFilter:
        async def afirst(self):
            raise Exception("DB down")

    monkeypatch.setattr(
        "aiservice.middleware.track_usage_middleware.Subscriptions.objects.filter", lambda **kwargs: FakeFilter()
    )

    response = await middleware(request)
    data = parse_json(response)

    assert response.status_code == 500
    assert "Service temporarily unavailable" in data["error"]
