import inspect
from pathlib import Path
from types import NoneType

from config_inspector.file_content import FileContentService
from config_inspector.file_content import FileMetadataService
from config_inspector.file_content.application.ports import DateTimeFormatter
from config_inspector.file_content.application.ports import SizeFormatter
from config_inspector.file_content.domain.entities import FileMeta
from config_inspector.registry import FileRegistry
from config_inspector.updaters import ConfigUpdater
from config_inspector.updaters import PlainTextUpdater
from config_inspector.updaters import updater_factory

NO_DEFAULT = object()


def _assert_signature(
    func,
    expected_params: list[tuple[str, object, object]],
    expected_return: object = inspect.Signature.empty,
) -> None:
    signature = inspect.signature(func)
    assert list(signature.parameters) == [name for name, _, _ in expected_params]

    for name, annotation, default in expected_params:
        param = signature.parameters[name]
        assert param.annotation == annotation
        if default is NO_DEFAULT:
            assert param.default is inspect.Parameter.empty
        else:
            assert param.default == default

    assert signature.return_annotation == expected_return


def test_updater_factory_signature_contract():
    signature = inspect.signature(updater_factory)
    assert list(signature.parameters) == ["file_extension"]


def test_signature_lock_file_registry():
    _assert_signature(
        FileRegistry.__init__,
        [
            ("self", inspect.Parameter.empty, NO_DEFAULT),
            ("metadata_service", FileMetadataService, NO_DEFAULT),
        ],
    )
    _assert_signature(
        FileRegistry.register,
        [
            ("self", inspect.Parameter.empty, NO_DEFAULT),
            ("filepath", Path, NO_DEFAULT),
            ("label", str | NoneType, None),
        ],
        expected_return=None,
    )
    _assert_signature(
        FileRegistry.get_all_metadata,
        [("self", inspect.Parameter.empty, NO_DEFAULT)],
        expected_return=list[FileMeta],
    )
    _assert_signature(
        FileRegistry.is_registered,
        [("self", inspect.Parameter.empty, NO_DEFAULT), ("filepath", Path, NO_DEFAULT)],
        expected_return=bool,
    )
    _assert_signature(
        FileRegistry.clear,
        [("self", inspect.Parameter.empty, NO_DEFAULT)],
        expected_return=None,
    )
    _assert_signature(
        FileRegistry.list_files,
        [("self", inspect.Parameter.empty, NO_DEFAULT)],
        expected_return=list[tuple[Path, str | NoneType]],
    )


def test_signature_lock_file_services():
    _assert_signature(
        FileMetadataService.__init__,
        [
            ("self", inspect.Parameter.empty, NO_DEFAULT),
            ("size_formatter", SizeFormatter, NO_DEFAULT),
            ("datetime_formatter", DateTimeFormatter, NO_DEFAULT),
            ("lang_mapper", dict[str, str] | NoneType, None),
        ],
    )
    _assert_signature(
        FileMetadataService.get_lang,
        [("self", inspect.Parameter.empty, NO_DEFAULT), ("filepath", Path, NO_DEFAULT)],
        expected_return=str,
    )
    _assert_signature(
        FileMetadataService.get_metadata,
        [
            ("self", inspect.Parameter.empty, NO_DEFAULT),
            ("filepath", Path, NO_DEFAULT),
            ("label", str | NoneType, None),
        ],
        expected_return=FileMeta,
    )
    _assert_signature(
        FileContentService.__init__,
        [
            ("self", inspect.Parameter.empty, NO_DEFAULT),
            ("metadata_service", FileMetadataService, NO_DEFAULT),
            ("max_size_bytes", int, 268_435_455),
        ],
    )
    _assert_signature(
        FileContentService.is_valid_size,
        [("self", inspect.Parameter.empty, NO_DEFAULT), ("filepath", Path, NO_DEFAULT)],
        expected_return=bool,
    )
    _assert_signature(
        FileContentService.get_payload,
        [
            ("self", inspect.Parameter.empty, NO_DEFAULT),
            ("filepath", Path, NO_DEFAULT),
            ("key", str, NO_DEFAULT),
            ("label", str | NoneType, None),
        ],
        expected_return=dict,
    )


def test_signature_lock_updater_symbols():
    _assert_signature(
        PlainTextUpdater.update,
        [
            ("self", inspect.Parameter.empty, NO_DEFAULT),
            ("filepath", Path, NO_DEFAULT),
            ("content", str, NO_DEFAULT),
        ],
        expected_return=None,
    )
    _assert_signature(
        updater_factory,
        [("file_extension", str, NO_DEFAULT)],
        expected_return=ConfigUpdater,
    )
