from types import SimpleNamespace
from unittest.mock import patch

from django.conf import settings

if not settings.configured:
    settings.configure(
        SECRET_KEY="test-key",
        ROOT_URLCONF=__name__,
        INSTALLED_APPS=[
            "django.contrib.auth",
            "django.contrib.contenttypes",
            "rest_framework",
        ],
        DATABASES={"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}},
        MIDDLEWARE=[],
        USE_TZ=True,
        DEFAULT_AUTO_FIELD="django.db.models.AutoField",
    )
else:
    settings.ROOT_URLCONF = __name__

import django

django.setup()

from django.test import SimpleTestCase
from django.urls import include, path, resolve, reverse
from rest_framework.test import APIClient, APIRequestFactory, force_authenticate

from django_workflows.users import views as workflow_views

urlpatterns = [
    path("api/", include("django_workflows.users.urls")),
]


class PackageUserWorkflowViewsTest(SimpleTestCase):
    def setUp(self):
        self.client = APIClient()

    def test_register_missing_fields_returns_400(self):
        response = self.client.post(
            reverse("register"),
            {"firstName": "Pat", "email": "pat@example.com"},
            format="json",
        )
        self.assertEqual(response.status_code, 400)
        self.assertEqual(
            response.json()["detail"],
            "Missing required field(s): lastName, password",
        )

    def test_register_honeypot_returns_400(self):
        response = self.client.post(
            reverse("register"),
            {
                "firstName": "Pat",
                "lastName": "User",
                "email": "pat@example.com",
                "password": "pw",
                "company": "bot-field",
            },
            format="json",
        )

        self.assertEqual(response.status_code, 400)
        self.assertEqual(response.json()["detail"], "Bot detected. Registration blocked.")

    @patch("django_workflows.users.views.register_user_and_send_verification_email")
    def test_register_success_passes_optional_groups(self, mock_register):
        mock_register.return_value = SimpleNamespace(
            success=True,
            message="Registration successful. Please check your email for the verification code.",
        )

        response = self.client.post(
            reverse("register"),
            {
                "firstName": "Pat",
                "lastName": "User",
                "email": "pat@example.com",
                "username": "pat@example.com",
                "password": "pw",
                "groups": [
                    {"name": "Time Entry Admin", "enabled": True},
                    {"name": "Superuser", "enabled": False},
                ],
            },
            format="json",
        )

        self.assertEqual(response.status_code, 201)
        mock_register.assert_called_once_with(
            first_name="Pat",
            last_name="User",
            email="pat@example.com",
            password="pw",
            username="pat@example.com",
            groups=[
                {"name": "Time Entry Admin", "enabled": True},
                {"name": "Superuser", "enabled": False},
            ],
        )

    @patch("django_workflows.users.views.register_user_and_send_verification_email")
    def test_register_conflict_returns_409(self, mock_register):
        mock_register.return_value = SimpleNamespace(
            success=False,
            message="A user with this email already exists.",
        )

        response = self.client.post(
            reverse("register"),
            {
                "firstName": "Pat",
                "lastName": "User",
                "email": "pat@example.com",
                "password": "pw",
            },
            format="json",
        )

        self.assertEqual(response.status_code, 409)

    @patch("django_workflows.users.views.register_user_and_send_verification_email")
    def test_register_validation_failure_returns_400(self, mock_register):
        mock_register.return_value = SimpleNamespace(
            success=False,
            message="An unexpected error occurred during registration.",
        )

        response = self.client.post(
            reverse("register"),
            {
                "firstName": "Pat",
                "lastName": "User",
                "email": "pat@example.com",
                "password": "pw",
            },
            format="json",
        )

        self.assertEqual(response.status_code, 400)

    @patch("django_workflows.users.views.send_forgot_password_code")
    def test_forgot_password_success(self, mock_send):
        mock_send.return_value = SimpleNamespace(message="Email sent")

        response = self.client.post(
            reverse("forgot-password"),
            {"email": "pat@example.com"},
            format="json",
        )

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.json()["detail"], "Email sent")

    def test_forgot_password_missing_email_returns_400(self):
        response = self.client.post(reverse("forgot-password"), {}, format="json")

        self.assertEqual(response.status_code, 400)
        self.assertEqual(response.json()["detail"], "Email is required.")

    def test_forgot_password_honeypot_returns_400(self):
        response = self.client.post(
            reverse("forgot-password"),
            {"email": "pat@example.com", "company": "bot-field"},
            format="json",
        )

        self.assertEqual(response.status_code, 400)
        self.assertEqual(response.json()["detail"], "Password reset blocked.")

    @patch("django_workflows.users.views.verify_reset_code")
    def test_verify_code_success_returns_workflow_payload(self, mock_verify):
        mock_verify.return_value = SimpleNamespace(
            payload={"detail": "Code verified"},
            status_code=202,
        )

        response = self.client.post(
            reverse("verify-code"),
            {"email": "user@example.com", "code": "123456"},
            format="json",
        )

        self.assertEqual(response.status_code, 202)
        self.assertEqual(response.json()["detail"], "Code verified")

    def test_verify_code_missing_input_returns_400(self):
        response = self.client.post(
            reverse("verify-code"),
            {"email": "user@example.com"},
            format="json",
        )

        self.assertEqual(response.status_code, 400)
        self.assertEqual(response.json()["detail"], "Email and code are required.")

    def test_verify_code_honeypot_returns_400(self):
        response = self.client.post(
            reverse("verify-code"),
            {"email": "user@example.com", "code": "123456", "company": "bot-field"},
            format="json",
        )

        self.assertEqual(response.status_code, 400)
        self.assertEqual(response.json()["detail"], "Verification blocked.")

    @patch("django_workflows.users.views.change_password")
    def test_change_password_failure_returns_400(self, mock_change_password):
        mock_change_password.return_value = SimpleNamespace(success=False, message="Mismatch")

        response = self.client.post(
            reverse("change-password"),
            {
                "email": "user@example.com",
                "password": "pw",
                "passwordVerify": "bad",
            },
            format="json",
        )

        self.assertEqual(response.status_code, 400)
        self.assertEqual(response.json()["detail"], "Mismatch")

    @patch("django_workflows.users.views.change_password")
    def test_change_password_success_returns_200(self, mock_change_password):
        mock_change_password.return_value = SimpleNamespace(
            success=True,
            message="Password changed successfully.",
        )

        response = self.client.post(
            reverse("change-password"),
            {
                "email": "user@example.com",
                "password": "pw",
                "passwordVerify": "pw",
            },
            format="json",
        )

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.json()["detail"], "Password changed successfully.")

    def test_change_password_honeypot_returns_400(self):
        response = self.client.post(
            reverse("change-password"),
            {
                "email": "user@example.com",
                "password": "pw",
                "passwordVerify": "pw",
                "company": "bot-field",
            },
            format="json",
        )

        self.assertEqual(response.status_code, 400)
        self.assertEqual(response.json()["detail"], "Bot detected. Change password blocked.")


class PackageUserDetailViewTest(SimpleTestCase):
    def _make_user(self):
        groups = [SimpleNamespace(name="EXPENSE MANAGER")]

        class FakeUser:
            def __init__(self):
                self.id = 7
                self.username = "member@example.com"
                self.email = "member@example.com"
                self.first_name = "Member"
                self.last_name = "User"
                self.is_active = True
                self.is_authenticated = True
                self.groups = SimpleNamespace(all=lambda: groups)

        return FakeUser()

    def test_package_user_detail_route_resolves_to_package_view(self):
        match = resolve("/api/user/")
        self.assertEqual(match.view_name, "user-detail")
        self.assertIs(match.func.view_class, workflow_views.UserDetailView)

    def test_package_user_list_route_resolves_to_package_view(self):
        match = resolve("/api/users/")
        self.assertEqual(match.view_name, "user-list")
        self.assertIs(match.func.view_class, workflow_views.UserListView)

    def test_package_admin_register_route_resolves_to_package_view(self):
        match = resolve("/api/admin-register/")
        self.assertEqual(match.view_name, "admin-register")
        self.assertIs(match.func.view_class, workflow_views.AdminUserCreateView)

    @patch("django_workflows.users.views.get_user_details")
    def test_user_detail_get_delegates_to_service(self, mock_get_user_details):
        mock_get_user_details.return_value = {
            "id": 7,
            "username": "member@example.com",
            "email": "member@example.com",
            "first_name": "Member",
            "last_name": "User",
            "is_active": True,
            "force_password_reset": True,
            "groups": ["EXPENSE MANAGER"],
        }

        user = self._make_user()
        request = SimpleNamespace(user=user)
        response = workflow_views.UserDetailView().get(request)

        self.assertEqual(response.status_code, 200)
        self.assertTrue(response.data["is_active"])
        self.assertTrue(response.data["force_password_reset"])
        self.assertEqual(response.data["groups"], ["EXPENSE MANAGER"])
        mock_get_user_details.assert_called_once_with(user)

    @patch("django_workflows.users.views.update_user")
    def test_user_detail_patch_success_returns_200(self, mock_update_user):
        mock_update_user.return_value = SimpleNamespace(
            success=True,
            message="User updated successfully.",
        )

        user = self._make_user()
        request = SimpleNamespace(
            user=user,
            data={
                "isActive": 0,
                "groups": [
                    {"name": "Superuser", "enabled": False},
                    {"name": "Time Entry User", "enabled": True},
                ],
            },
        )
        response = workflow_views.UserDetailView().patch(request)

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data["detail"], "User updated successfully.")
        mock_update_user.assert_called_once_with(
            user,
            {
                "isActive": 0,
                "groups": [
                    {"name": "Superuser", "enabled": False},
                    {"name": "Time Entry User", "enabled": True},
                ],
            },
        )

    @patch("django_workflows.users.views.update_user")
    def test_user_detail_patch_validation_error_returns_400(self, mock_update_user):
        mock_update_user.return_value = SimpleNamespace(
            success=False,
            message="isActive must be a boolean value.",
        )

        user = self._make_user()
        request = SimpleNamespace(user=user, data={"isActive": "not-a-bool"})
        response = workflow_views.UserDetailView().patch(request)

        self.assertEqual(response.status_code, 400)
        self.assertEqual(response.data["detail"], "isActive must be a boolean value.")

    @patch("django_workflows.users.views.list_users")
    def test_user_list_get_passes_exclude_group_filter(self, mock_list_users):
        mock_list_users.return_value = [
            {
                "id": 9,
                "username": "member@example.com",
                "email": "member@example.com",
                "first_name": "Member",
                "last_name": "User",
                "is_active": True,
                "force_password_reset": False,
                "groups": ["EXPENSE MANAGER"],
            }
        ]

        request = SimpleNamespace(query_params={"exclude_group": "Superuser,Time Entry Admin"})
        response = workflow_views.UserListView().get(request)

        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(response.data), 1)
        self.assertTrue(response.data[0]["is_active"])
        mock_list_users.assert_called_once_with(
            exclude_groups="Superuser,Time Entry Admin"
        )


class PackageAdminUserWorkflowViewsTest(SimpleTestCase):
    def setUp(self):
        self.factory = APIRequestFactory()

    def _make_authenticated_user(self, group_names):
        groups = [SimpleNamespace(name=name) for name in group_names]
        return SimpleNamespace(
            id=99,
            email="admin@example.com",
            username="admin@example.com",
            is_authenticated=True,
            groups=SimpleNamespace(all=lambda: groups),
        )

    @patch("django_workflows.users.views.admin_register_user_and_send_setup_email")
    def test_admin_register_success_requires_time_entry_admin_and_passes_groups(self, mock_admin_register):
        mock_admin_register.return_value = SimpleNamespace(
            success=True,
            message="User created successfully. The user can use the Forgot Password flow to establish a password and login.",
        )

        request = self.factory.post(
            reverse("admin-register"),
            {
                "firstName": "Pat",
                "lastName": "User",
                "email": "pat@example.com",
                "username": "pat@example.com",
                "groups": [
                    {"name": "Time Entry User", "enabled": True},
                    {"name": "Can Log Work", "enabled": True},
                ],
            },
            format="json",
        )
        force_authenticate(request, user=self._make_authenticated_user(["Time Entry Admin"]))

        response = workflow_views.AdminUserCreateView.as_view()(request)

        self.assertEqual(response.status_code, 201)
        mock_admin_register.assert_called_once_with(
            first_name="Pat",
            last_name="User",
            email="pat@example.com",
            username="pat@example.com",
            groups=[
                {"name": "Time Entry User", "enabled": True},
                {"name": "Can Log Work", "enabled": True},
            ],
        )

    def test_admin_register_forbidden_without_time_entry_admin_group(self):
        request = self.factory.post(
            reverse("admin-register"),
            {
                "firstName": "Pat",
                "lastName": "User",
                "email": "pat@example.com",
            },
            format="json",
        )
        force_authenticate(request, user=self._make_authenticated_user(["Time Entry User"]))

        response = workflow_views.AdminUserCreateView.as_view()(request)

        self.assertEqual(response.status_code, 403)

    def test_admin_register_honeypot_returns_400(self):
        request = self.factory.post(
            reverse("admin-register"),
            {
                "firstName": "Pat",
                "lastName": "User",
                "email": "pat@example.com",
                "company": "bot-field",
            },
            format="json",
        )
        force_authenticate(request, user=self._make_authenticated_user(["Time Entry Admin"]))

        response = workflow_views.AdminUserCreateView.as_view()(request)

        self.assertEqual(response.status_code, 400)
        self.assertEqual(response.data["detail"], "Bot detected. Registration blocked.")

    def test_admin_register_missing_fields_returns_400(self):
        request = self.factory.post(
            reverse("admin-register"),
            {
                "firstName": "Pat",
                "email": "pat@example.com",
            },
            format="json",
        )
        force_authenticate(request, user=self._make_authenticated_user(["Time Entry Admin"]))

        response = workflow_views.AdminUserCreateView.as_view()(request)

        self.assertEqual(response.status_code, 400)
        self.assertEqual(response.data["detail"], "Missing required field(s): lastName")

    @patch("django_workflows.users.views.admin_register_user_and_send_setup_email")
    def test_admin_register_conflict_returns_409(self, mock_admin_register):
        mock_admin_register.return_value = SimpleNamespace(
            success=False,
            message="A user with this email already exists.",
        )

        request = self.factory.post(
            reverse("admin-register"),
            {
                "firstName": "Pat",
                "lastName": "User",
                "email": "pat@example.com",
                "groups": [],
            },
            format="json",
        )
        force_authenticate(request, user=self._make_authenticated_user(["Time Entry Admin"]))

        response = workflow_views.AdminUserCreateView.as_view()(request)

        self.assertEqual(response.status_code, 409)

    @patch("django_workflows.users.views.admin_register_user_and_send_setup_email")
    def test_admin_register_validation_failure_returns_400(self, mock_admin_register):
        mock_admin_register.return_value = SimpleNamespace(
            success=False,
            message="groups must be a list of objects with name and enabled.",
        )

        request = self.factory.post(
            reverse("admin-register"),
            {
                "firstName": "Pat",
                "lastName": "User",
                "email": "pat@example.com",
                "groups": "Time Entry User",
            },
            format="json",
        )
        force_authenticate(request, user=self._make_authenticated_user(["Time Entry Admin"]))

        response = workflow_views.AdminUserCreateView.as_view()(request)

        self.assertEqual(response.status_code, 400)
        self.assertEqual(response.data["detail"], "groups must be a list of objects with name and enabled.")

    def test_user_has_group_handles_unauthenticated_and_missing_group_manager(self):
        unauthenticated_user = SimpleNamespace(is_authenticated=False)
        missing_group_manager_user = SimpleNamespace(is_authenticated=True, groups=None)

        self.assertFalse(workflow_views._user_has_group(unauthenticated_user, "Time Entry Admin"))
        self.assertFalse(workflow_views._user_has_group(missing_group_manager_user, "Time Entry Admin"))

    def test_user_has_group_uses_filter_exists_when_available(self):
        filtered_groups = SimpleNamespace(exists=lambda: True)
        user = SimpleNamespace(
            is_authenticated=True,
            groups=SimpleNamespace(filter=lambda **kwargs: filtered_groups),
        )

        self.assertTrue(workflow_views._user_has_group(user, "Time Entry Admin"))

    def test_user_has_group_returns_false_without_filter_or_all(self):
        user = SimpleNamespace(
            is_authenticated=True,
            groups=SimpleNamespace(),
        )

        self.assertFalse(workflow_views._user_has_group(user, "Time Entry Admin"))