from dataclasses import dataclass
from datetime import datetime

import attrs
import pytest

from msgspec import Struct
from pydantic import BaseModel
from pydantic.dataclasses import dataclass as pydataclass

from sanic_ext import openapi

from .utils import get_spec


@dataclass
class AlertDataclass:
    hit: dict[str, int]
    last_updated: datetime


@dataclass
class AlertResponseDataclass:
    alert: AlertDataclass
    rule_id: str


class AlertPydanticBaseModel(BaseModel):
    hit: dict[str, int]
    last_updated: datetime


class AlertResponsePydanticBaseModel(BaseModel):
    alert: AlertPydanticBaseModel
    rule_id: str


class AlertMsgspecBaseModel(Struct):
    hit: dict[str, int]
    last_updated: datetime


class AlertResponseMsgspecBaseModel(Struct):
    alert: AlertMsgspecBaseModel
    rule_id: str


@pydataclass
class AlertPydanticDataclass:
    hit: dict[str, int]
    last_updated: datetime


@pydataclass
class AlertResponsePydanticDataclass:
    alert: AlertPydanticDataclass
    rule_id: str


@attrs.define
class AlertAttrs:
    hit: dict[str, int]
    last_updated: datetime


@attrs.define
class AlertResponseAttrs:
    alert: AlertAttrs
    rule_id: str


@pytest.mark.parametrize(
    "AlertResponse,check_alert",
    (
        (AlertResponseDataclass, False),
        (AlertResponseAttrs, False),
        (AlertResponseMsgspecBaseModel, True),
        (AlertResponsePydanticBaseModel, True),
        (AlertResponsePydanticDataclass, True),
    ),
)
def test_pydantic_base_model(app, AlertResponse, check_alert):
    @app.get("/")
    @openapi.definition(
        body={"application/json": openapi.Component(AlertResponse)}
    )
    async def handler(_): ...

    spec = get_spec(app)
    alert_response_name = AlertResponse.__name__
    alert_name = "Alert" + alert_response_name[13:]

    assert spec["paths"]["/"]["get"]["requestBody"] == {
        "content": {
            "application/json": {
                "schema": {
                    "$ref": f"#/components/schemas/{alert_response_name}"
                }
            }
        }
    }
    assert alert_response_name in spec["components"]["schemas"]

    if check_alert:
        assert alert_name in spec["components"]["schemas"]
        assert spec["components"]["schemas"][alert_response_name][
            "properties"
        ]["alert"] == {"$ref": f"#/components/schemas/{alert_name}"}
