"""Tests for installation event handler."""

from __future__ import annotations

from unittest.mock import AsyncMock, MagicMock, patch

from specwright.github.handlers.on_installation import on_installation


def _make_payload(action: str, installation_id: int = 12345, org: str = "test-org") -> dict:
    return {
        "action": action,
        "installation": {
            "id": installation_id,
            "app_id": 2813462,
            "account": {
                "login": org,
                "id": 111,
            },
        },
    }


def _make_state(**overrides):
    state = MagicMock()
    state.registry = overrides.get("registry", AsyncMock())
    state.indexer = overrides.get("indexer", MagicMock())
    state.search_index = overrides.get("search_index", MagicMock())
    state.embed_client = overrides.get("embed_client", MagicMock())
    return state


class TestOnInstallationCreated:
    async def test_upserts_and_schedules_indexing(self):
        state = _make_state()
        client = AsyncMock()

        await on_installation(client, _make_payload("created"), _app_state=state)

        state.registry.upsert_installation.assert_awaited_once()
        state.indexer.schedule_org_index.assert_called_once()

    async def test_skips_indexing_without_search_index(self):
        state = _make_state(search_index=None)
        client = AsyncMock()

        await on_installation(client, _make_payload("created"), _app_state=state)

        state.registry.upsert_installation.assert_awaited_once()
        state.indexer.schedule_org_index.assert_not_called()


class TestOnInstallationDeleted:
    async def test_marks_removed(self):
        state = _make_state()
        client = AsyncMock()

        await on_installation(client, _make_payload("deleted"), _app_state=state)
        state.registry.mark_removed.assert_awaited_once_with(12345)


class TestOnInstallationSuspend:
    async def test_marks_suspended(self):
        state = _make_state()
        client = AsyncMock()

        await on_installation(client, _make_payload("suspend"), _app_state=state)
        state.registry.mark_suspended.assert_awaited_once_with(12345)


class TestOnInstallationUnsuspend:
    async def test_marks_unsuspended(self):
        state = _make_state()
        client = AsyncMock()

        await on_installation(client, _make_payload("unsuspend"), _app_state=state)
        state.registry.mark_unsuspended.assert_awaited_once_with(12345)


class TestNoRegistry:
    async def test_skips_when_no_registry(self):
        state = _make_state(registry=None)
        client = AsyncMock()

        # Should not raise
        await on_installation(client, _make_payload("created"), _app_state=state)

    async def test_skips_when_no_state(self):
        client = AsyncMock()

        # Should not raise — no state at all
        await on_installation(client, _make_payload("created"), _app_state=None)


class TestOnInstallationOnboarding:
    async def test_fires_onboarding_for_created_with_repos(self):
        """Verify fire_and_forget is called with onboard_repos on 'created'."""
        state = _make_state()
        client = AsyncMock()
        repos = [{"full_name": "test-org/api"}, {"full_name": "test-org/web"}]
        payload = _make_payload("created")
        payload["repositories"] = repos

        with patch("specwright.github.handlers.on_installation.fire_and_forget") as mock_ff:
            await on_installation(client, payload, _app_state=state)

        mock_ff.assert_called_once()

    async def test_no_onboarding_without_repos(self):
        """No fire_and_forget call when payload has no repositories."""
        state = _make_state()
        client = AsyncMock()
        payload = _make_payload("created")
        # No "repositories" key in payload

        with patch("specwright.github.handlers.on_installation.fire_and_forget") as mock_ff:
            await on_installation(client, payload, _app_state=state)

        mock_ff.assert_not_called()
