import uuid
from typing import Optional

import pytest
from bson import Binary, ObjectId as BsonObjectId
from pydantic import BaseModel, ValidationError

from keble_helpers.typings.id import ObjectId, Uuid, is_optional, to_binary_uuid


def test_is_optional():
    assert is_optional(Optional[int]) is True
    assert is_optional(int) is False


def test_object_id_validation_and_serialization():
    class Example(BaseModel):
        oid: ObjectId

    oid = BsonObjectId()
    assert Example(oid=oid).oid == oid
    assert Example(oid=str(oid)).oid == oid
    assert Example(oid=oid).model_dump()["oid"] == oid
    assert Example(oid=oid).model_dump(mode="json")["oid"] == str(oid)

    with pytest.raises(ValidationError):
        Example(oid="not-a-valid-object-id")


def test_uuid_type_serializes_to_bson_binary():
    class Example(BaseModel):
        uid: Uuid

    uid = uuid.uuid4()
    model = Example(uid=str(uid))
    assert model.uid == uid

    dumped = model.model_dump()
    assert isinstance(dumped["uid"], Binary)
    assert dumped["uid"].as_uuid() == uid


def test_uuid_type_accepts_bson_binary():
    class Example(BaseModel):
        uid: Uuid

    uid = uuid.uuid4()
    model = Example(uid=Binary.from_uuid(uid))
    assert model.uid == uid


def test_to_binary_uuid():
    uid = uuid.uuid4()
    binary_uid = Binary.from_uuid(uid)

    assert to_binary_uuid(uid).as_uuid() == uid
    assert to_binary_uuid(binary_uid) == binary_uid

    binary_list = to_binary_uuid([uid, binary_uid])
    assert [b.as_uuid() for b in binary_list] == [uid, uid]
