import pathlib
import uuid

import pytest
from pydantic import ValidationError

from fidere import Identifier
from fidere.models import DataModel


class TestIdentifier:
    # Test case for Identifier-related functionality
    def test_from_ascii(self):
        ident = Identifier.from_ascii()
        assert ident.isascii()
        ident = Identifier.from_ascii(length=10)
        # check if optional argument is used correctly
        assert len(ident) == 10

    def test_from_uuid(self):
        ident = Identifier.from_uuid()
        # check if it is valid
        uuid.UUID(ident)
        # check an invalid UUID
        with pytest.raises(ValueError):
            uuid.UUID(ident[:-1])
        # check version of UUID
        assert uuid.UUID(ident).version == 4

    def test_from_arbitrary_type(self):
        for i in ("ident", 15, (1, 2, "abcd"), {"abcd": 1234}, {1234, "abcd"}):
            ident = Identifier(i)
            assert ident == str(i)


class TestDataModelIdentifierField:
    # Test case for DataModel-related functionality
    def test_extract_name(self, path_schema):
        model = DataModel.with_id_from_schema(pathlib.Path(path_schema))
        # check if the identifier is correctly read from the type field in the json schema
        assert model.identifier == "ffc68435-eac0-41c8-84f5-e5e67ef12312"

    def test_predefined_id(self, path_schema):
        model = DataModel(
            schema_location=path_schema, identifier=Identifier("abcde")
        )
        assert model.identifier == "abcde"

    def test_identifier_is_arbitrary_type(self, path_schema):
        for i in ("abcde", 12, ["shjb", 25.0]):
            model = DataModel(schema_location=path_schema, identifier=i)
            assert model.identifier == Identifier(i)

    def test_if_id_overwrite_is_blocked(self, path_schema):
        model = DataModel(
            schema_location=path_schema, identifier=Identifier("abcde")
        )

        # check for "TypeError: "identifier" has allow_mutation set to False and cannot be assigned"
        def assign_value(m: DataModel):
            m.identifier = "bcdef"  # just a small temporary function

        # check if the according TypeError is raised upon assignment
        with pytest.raises(ValidationError):
            assign_value(model)
