import unittest
from unittest import mock

import gnos.chunking.registry as reg
from gnos.chunking.api import Chunker, ChunkerContext, ManifestChunk, SplitChunk, SplitResult
from gnos.chunking.registry import get_method, list_methods


class _FakeDist:
    def __init__(self, name: str) -> None:
        self.name = name


class _FakeEntryPoint:
    def __init__(self, obj, dist_name: str) -> None:
        self._obj = obj
        self.dist = _FakeDist(dist_name)

    def load(self):
        return self._obj


class _PluginChunker(Chunker):
    name = "plugin-v1"

    def split(self, data: bytes, params, _ctx: ChunkerContext):
        return SplitResult(chunks=[SplitChunk(data=data)], params=params)

    def reconstruct(self, chunks: list[ManifestChunk], _params):
        return b"".join(c.data for c in chunks)


class TestChunkingPlugins(unittest.TestCase):
    def test_entrypoint_chunker_loaded(self) -> None:
        reg._PLUGINS_LOADED = False
        reg._CHUNKERS = dict(reg._BUILTIN_CHUNKERS)
        reg._SOURCES = {name: "core" for name in reg._BUILTIN_CHUNKERS}

        plugin = _PluginChunker()

        with mock.patch("gnos.chunking.registry._iter_entry_points", return_value=[_FakeEntryPoint(plugin, "pkg")]):
            m = get_method("plugin-v1")
            self.assertEqual(m.name, "plugin-v1")
            names = [n for n, _ in list_methods()]
            self.assertIn("plugin-v1", names)

    def test_entrypoint_name_collision_ignored(self) -> None:
        reg._PLUGINS_LOADED = False
        reg._CHUNKERS = dict(reg._BUILTIN_CHUNKERS)
        reg._SOURCES = {name: "core" for name in reg._BUILTIN_CHUNKERS}

        class _CollisionChunker(_PluginChunker):
            name = "identity-v1"

        plugin = _CollisionChunker()
        with mock.patch("gnos.chunking.registry._iter_entry_points", return_value=[_FakeEntryPoint(plugin, "pkg")]):
            m = get_method("identity-v1")
            self.assertEqual(m.name, "identity-v1")


if __name__ == "__main__":
    unittest.main()
