from datetime import datetime, timezone
from typing import Any

import pytest
from pydantic import BaseModel, ValidationError

from pydantic_extra_types.ulid import ULID

try:
    from ulid import ULID as _ULID
except ModuleNotFoundError:  # pragma: no cover
    raise RuntimeError(
        'The `ulid` module requires "python-ulid" to be installed. You can install it with "pip install python-ulid".'
    )


class Something(BaseModel):
    ulid: ULID


@pytest.mark.parametrize(
    'ulid, result, valid',
    [
        # Valid ULID for str format
        ('01BTGNYV6HRNK8K8VKZASZCFPE', '01BTGNYV6HRNK8K8VKZASZCFPE', True),
        ('01BTGNYV6HRNK8K8VKZASZCFPF', '01BTGNYV6HRNK8K8VKZASZCFPF', True),
        # Invalid ULID for str format
        ('01BTGNYV6HRNK8K8VKZASZCFP', None, False),  # Invalid ULID (short length)
        ('01BTGNYV6HRNK8K8VKZASZCFPEA', None, False),  # Invalid ULID (long length)
        # Valid ULID for _ULID format
        (_ULID.from_str('01BTGNYV6HRNK8K8VKZASZCFPE'), '01BTGNYV6HRNK8K8VKZASZCFPE', True),
        (_ULID.from_str('01BTGNYV6HRNK8K8VKZASZCFPF'), '01BTGNYV6HRNK8K8VKZASZCFPF', True),
        # Invalid _ULID for bytes format
        (b'\x01\xBA\x1E\xB2\x8A\x9F\xFAy\x10\xD5\xA5k\xC8', None, False),  # Invalid ULID (short length)
        (b'\x01\xBA\x1E\xB2\x8A\x9F\xFAy\x10\xD5\xA5k\xC8\xB6\x00', None, False),  # Invalid ULID (long length)
        # Valid ULID for int format
        (109667145845879622871206540411193812282, '2JG4FVY7N8XS4GFVHPXGJZ8S9T', True),
        (109667145845879622871206540411193812283, '2JG4FVY7N8XS4GFVHPXGJZ8S9V', True),
        (109667145845879622871206540411193812284, '2JG4FVY7N8XS4GFVHPXGJZ8S9W', True),
    ],
)
def test_format_for_ulid(ulid: Any, result: Any, valid: bool):
    if valid:
        assert str(Something(ulid=ulid).ulid) == result
    else:
        with pytest.raises(ValidationError, match='format'):
            Something(ulid=ulid)


def test_property_for_ulid():
    ulid = Something(ulid='01BTGNYV6HRNK8K8VKZASZCFPE').ulid
    assert ulid.hex == '015ea15f6cd1c56689a373fab3f63ece'
    assert ulid == '01BTGNYV6HRNK8K8VKZASZCFPE'
    assert ulid.datetime == datetime(2017, 9, 20, 22, 18, 59, 153000, tzinfo=timezone.utc)
    assert ulid.timestamp == 1505945939.153


def test_json_schema():
    assert Something.model_json_schema(mode='validation') == {
        'properties': {
            'ulid': {
                'anyOf': [{'type': 'integer'}, {'format': 'binary', 'type': 'string'}, {'type': 'string'}],
                'title': 'Ulid',
            }
        },
        'required': ['ulid'],
        'title': 'Something',
        'type': 'object',
    }
    assert Something.model_json_schema(mode='serialization') == {
        'properties': {
            'ulid': {
                'anyOf': [{'type': 'integer'}, {'format': 'binary', 'type': 'string'}, {'type': 'string'}],
                'title': 'Ulid',
            }
        },
        'required': ['ulid'],
        'title': 'Something',
        'type': 'object',
    }
