from __future__ import annotations

from types import SimpleNamespace
from unittest.mock import patch

from fastapi import Request
from starlette.responses import PlainTextResponse, Response

from pygitweb.api.action import Action
from pygitweb.api.subpage import Subpage
from pygitweb.plugin_loader import (
	ACTION_GROUP,
	DISPATCH_ACTIONS,
	PLUGIN_ACTIONS,
	SUBPAGE_GROUP,
	SUBPAGES,
	apply_plugin_reload,
	list_available_plugins,
	load_plugin_actions,
	load_subpages,
)


class _DemoAction(Action):
	def action_name(self) -> str:
		return "demo_action"

	def action(self, project: str, req: Request | None = None) -> Response | None:
		return PlainTextResponse(project)


class _OtherAction(Action):
	def action_name(self) -> str:
		return "other_action"

	def action(self, project: str, req: Request | None = None) -> Response | None:
		return PlainTextResponse(project)


class _DemoSubpage(Subpage):
	def subpage_name(self) -> str:
		return "demo_subpage"


def _ep(name: str, dist_name: str, loaded: object) -> SimpleNamespace:
	return SimpleNamespace(
		name=name,
		dist=SimpleNamespace(name=dist_name),
		load=lambda: loaded,
	)


def _fake_entry_points(group: str) -> list[SimpleNamespace]:
	if group == ACTION_GROUP:
		return [
			_ep("demo", "pygitweb_demo", _DemoAction),
			_ep("other", "pygitweb_other", _OtherAction),
		]
	if group == SUBPAGE_GROUP:
		return [_ep("demo_sp", "pygitweb_demo", _DemoSubpage)]
	return []


def test_list_available_plugins_without_enablement() -> None:
	with patch("pygitweb.plugin_loader._entry_points", side_effect=_fake_entry_points):
		assert list_available_plugins() == ["pygitweb_demo", "pygitweb_other"]


def test_load_skips_disabled_plugins() -> None:
	with patch("pygitweb.plugin_loader._entry_points", side_effect=_fake_entry_points):
		assert load_plugin_actions(frozenset()) == {}
		assert load_subpages(frozenset()) == []
		actions = load_plugin_actions(frozenset({"pygitweb_demo"}))
		assert set(actions) == {"demo_action"}
		subpages = load_subpages(frozenset({"pygitweb_demo"}))
		assert [s.subpage_name() for s in subpages] == ["demo_subpage"]


def test_apply_plugin_reload_updates_registry(monkeypatch) -> None:
	from pygitweb.config import settings

	previous = list(settings.ENABLED_PLUGINS)
	monkeypatch.setattr(settings, "ENABLED_PLUGINS", ["pygitweb_other"])
	with patch("pygitweb.plugin_loader._entry_points", side_effect=_fake_entry_points):
		try:
			apply_plugin_reload()
			assert set(PLUGIN_ACTIONS) == {"other_action"}
			assert SUBPAGES == []
			assert "other_action" in DISPATCH_ACTIONS
			assert "demo_action" not in DISPATCH_ACTIONS

			monkeypatch.setattr(settings, "ENABLED_PLUGINS", ["pygitweb_demo"])
			apply_plugin_reload()
			assert set(PLUGIN_ACTIONS) == {"demo_action"}
			assert [s.subpage_name() for s in SUBPAGES] == ["demo_subpage"]
			assert "demo_action" in DISPATCH_ACTIONS
			assert "other_action" not in DISPATCH_ACTIONS
		finally:
			monkeypatch.setattr(settings, "ENABLED_PLUGINS", previous)
			apply_plugin_reload()