"""Upload dispatch over FastAPI multipart — files bind into Upload fields and
the declarative `File(...)` constraints are enforced."""

from __future__ import annotations

import json
from typing import Annotated

import pytest
from fastapi import FastAPI
from fastapi.testclient import TestClient
from pydantic import BaseModel

from mizan_core.client.function import client
from mizan_core.registry import clear_registry, register
from mizan_fastapi import File, MizanError, Upload, mizan_exception_handler, router as mizan_router


class AvatarOut(BaseModel):
    ok: bool
    size: int
    name: str | None = None


@pytest.fixture
def app():
    clear_registry()

    @client
    def set_avatar(
        request,
        user_id: int,
        avatar: Annotated[Upload, File(max_size="1MB", content_types=["image/png"])],
    ) -> AvatarOut:
        return AvatarOut(ok=True, size=avatar.size, name=avatar.filename)

    register(set_avatar, "set_avatar")

    fastapi_app = FastAPI()
    fastapi_app.include_router(mizan_router, prefix="/api/mizan")
    fastapi_app.add_exception_handler(MizanError, mizan_exception_handler)
    return fastapi_app


def _post(test_client: TestClient, args: dict, file_tuple: tuple):
    return test_client.post(
        "/api/mizan/call/",
        data={"fn": "set_avatar", "args": json.dumps(args)},
        files={"avatar": file_tuple},
    )


def test_upload_binds_and_executes(app):
    resp = _post(TestClient(app), {"user_id": 5}, ("a.png", b"\x89PNG" + b"x" * 100, "image/png"))
    assert resp.status_code == 200, resp.text
    result = resp.json()["result"]
    assert result["ok"] is True
    assert result["name"] == "a.png"
    assert result["size"] == 104


def test_max_size_rejected(app):
    resp = _post(TestClient(app), {"user_id": 5}, ("b.png", b"x" * (2 * 1024 * 1024), "image/png"))
    assert resp.status_code == 400
    assert "max size" in resp.text


def test_content_type_rejected(app):
    resp = _post(TestClient(app), {"user_id": 5}, ("c.gif", b"GIF89a", "image/gif"))
    assert resp.status_code == 400
    assert "content-type" in resp.text
