"""Tests para metabase_bulk_add_users_to_group (mocks only — sin instancia real)."""

import sys
import os
import unittest
from unittest.mock import MagicMock, patch

sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))

from pipelines.metabase_bulk_add_users_to_group import metabase_bulk_add_users_to_group


def _make_client():
    """Crea un MetabaseClient mock con base_url."""
    client = MagicMock()
    client.base_url = "https://metabase.example.com"
    return client


class TestBulkAddUsersToGroup(unittest.TestCase):

    def test_one_new_one_existing_already_member(self):
        """1 usuario a crear + 1 existente que ya es miembro -> created+added / existing+already_member."""
        client = _make_client()

        targets = [
            {"first_name": "Alice", "last_name": "Smith", "email": "alice@example.com"},
            {"first_name": "Bob",   "last_name": "Jones", "email": "bob@example.com"},
        ]

        # bob ya existe con id=10, alice no existe
        users_data = [{"id": 10, "email": "bob@example.com"}]
        # El grupo tiene id=5 y bob (id=10) ya es miembro
        group_info = {"id": 5, "name": "Admins", "members": [{"user_id": 10}]}
        # alice se crea con id=20
        created_alice = {"id": 20, "email": "alice@example.com"}

        with patch(
            "pipelines.metabase_bulk_add_users_to_group.metabase_list_groups",
            return_value=[{"id": 5, "name": "Admins"}],
        ), patch(
            "pipelines.metabase_bulk_add_users_to_group.metabase_list_users",
            return_value={"data": users_data},
        ), patch(
            "pipelines.metabase_bulk_add_users_to_group.metabase_get_group",
            return_value=group_info,
        ), patch(
            "pipelines.metabase_bulk_add_users_to_group.metabase_create_user",
            return_value=created_alice,
        ) as mock_create, patch(
            "pipelines.metabase_bulk_add_users_to_group.metabase_add_membership",
            return_value=[],
        ) as mock_add:

            results = metabase_bulk_add_users_to_group(client, "Admins", targets)

        self.assertEqual(len(results), 2)

        alice_r = results[0]
        self.assertEqual(alice_r["email"], "alice@example.com")
        self.assertEqual(alice_r["status"], "created")
        self.assertEqual(alice_r["membership"], "added")
        self.assertIsNone(alice_r["error"])

        bob_r = results[1]
        self.assertEqual(bob_r["email"], "bob@example.com")
        self.assertEqual(bob_r["status"], "existing")
        self.assertEqual(bob_r["membership"], "already_member")
        self.assertIsNone(bob_r["error"])

        mock_create.assert_called_once_with(client, "Alice", "Smith", "alice@example.com")
        mock_add.assert_called_once_with(client, 20, 5)

    def test_one_new_one_existing_not_yet_member(self):
        """1 existente que NO es miembro todavia -> existing + added."""
        client = _make_client()

        targets = [
            {"first_name": "Carol", "last_name": "White", "email": "carol@example.com"},
        ]

        users_data = [{"id": 30, "email": "carol@example.com"}]
        # carol (id=30) NO esta en el grupo
        group_info = {"id": 7, "name": "Jefes", "members": []}

        with patch(
            "pipelines.metabase_bulk_add_users_to_group.metabase_list_groups",
            return_value=[{"id": 7, "name": "Jefes"}],
        ), patch(
            "pipelines.metabase_bulk_add_users_to_group.metabase_list_users",
            return_value={"data": users_data},
        ), patch(
            "pipelines.metabase_bulk_add_users_to_group.metabase_get_group",
            return_value=group_info,
        ), patch(
            "pipelines.metabase_bulk_add_users_to_group.metabase_add_membership",
            return_value=[],
        ) as mock_add:

            results = metabase_bulk_add_users_to_group(client, 7, targets)

        self.assertEqual(len(results), 1)
        r = results[0]
        self.assertEqual(r["status"], "existing")
        self.assertEqual(r["membership"], "added")
        mock_add.assert_called_once_with(client, 30, 7)

    def test_group_name_no_match_raises(self):
        """group por nombre que no matchea ningun grupo -> ValueError."""
        client = _make_client()

        with patch(
            "pipelines.metabase_bulk_add_users_to_group.metabase_list_groups",
            return_value=[{"id": 1, "name": "Reportes"}, {"id": 2, "name": "Finanzas"}],
        ):
            with self.assertRaises(ValueError) as ctx:
                metabase_bulk_add_users_to_group(client, "Inexistente", [])

        self.assertIn("No group found", str(ctx.exception))

    def test_group_name_multiple_matches_raises(self):
        """group por nombre que matchea >1 grupo -> ValueError con mensaje claro."""
        client = _make_client()

        with patch(
            "pipelines.metabase_bulk_add_users_to_group.metabase_list_groups",
            return_value=[
                {"id": 10, "name": "Jefes de Centro"},
                {"id": 11, "name": "Jefes de Zona"},
            ],
        ):
            with self.assertRaises(ValueError) as ctx:
                metabase_bulk_add_users_to_group(client, "Jefes", [])

        msg = str(ctx.exception)
        self.assertIn("Ambiguous", msg)
        self.assertIn("10:Jefes de Centro", msg)
        self.assertIn("11:Jefes de Zona", msg)

    def test_create_failure_marks_skipped(self):
        """Si create_user falla, status=create_failed y membership=skipped."""
        client = _make_client()

        targets = [
            {"first_name": "Dave", "last_name": "Error", "email": "dave@example.com"},
        ]

        with patch(
            "pipelines.metabase_bulk_add_users_to_group.metabase_list_groups",
            return_value=[{"id": 3, "name": "TestGroup"}],
        ), patch(
            "pipelines.metabase_bulk_add_users_to_group.metabase_list_users",
            return_value={"data": []},
        ), patch(
            "pipelines.metabase_bulk_add_users_to_group.metabase_get_group",
            return_value={"id": 3, "name": "TestGroup", "members": []},
        ), patch(
            "pipelines.metabase_bulk_add_users_to_group.metabase_create_user",
            side_effect=Exception("409 Conflict"),
        ), patch(
            "pipelines.metabase_bulk_add_users_to_group.metabase_add_membership"
        ) as mock_add:

            results = metabase_bulk_add_users_to_group(client, "TestGroup", targets)

        r = results[0]
        self.assertEqual(r["status"], "create_failed")
        self.assertEqual(r["membership"], "skipped")
        self.assertIsNotNone(r["error"])
        mock_add.assert_not_called()

    def test_group_by_int_skips_list_groups(self):
        """group=int usa el id directamente sin llamar a metabase_list_groups."""
        client = _make_client()

        targets = [{"first_name": "Eve", "last_name": "Test", "email": "eve@example.com"}]

        with patch(
            "pipelines.metabase_bulk_add_users_to_group.metabase_list_groups"
        ) as mock_list_groups, patch(
            "pipelines.metabase_bulk_add_users_to_group.metabase_list_users",
            return_value={"data": []},
        ), patch(
            "pipelines.metabase_bulk_add_users_to_group.metabase_get_group",
            return_value={"id": 99, "name": "Direct", "members": []},
        ), patch(
            "pipelines.metabase_bulk_add_users_to_group.metabase_create_user",
            return_value={"id": 50, "email": "eve@example.com"},
        ), patch(
            "pipelines.metabase_bulk_add_users_to_group.metabase_add_membership",
            return_value=[],
        ):

            results = metabase_bulk_add_users_to_group(client, 99, targets)

        mock_list_groups.assert_not_called()
        self.assertEqual(results[0]["status"], "created")
        self.assertEqual(results[0]["membership"], "added")


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