"""
Tests for MailDomainInvitations API endpoint in People's app mailbox_manager.
Focus on "create" action.
"""

from django.core import mail

import pytest
from rest_framework import status
from rest_framework.test import APIClient

from core import factories as core_factories

from mailbox_manager import enums, factories, models

pytestmark = pytest.mark.django_db


def test_api_domain_invitations__create__anonymous():
    """Anonymous users should not be able to create invitations."""
    domain = factories.MailDomainEnabledFactory()
    response = APIClient().post(
        f"/api/v1.0/mail-domains/{domain.slug}/invitations/",
        {
            "email": "some.email@domain.com",
            "role": "admin",
        },
        format="json",
    )
    assert response.status_code == status.HTTP_401_UNAUTHORIZED
    assert response.json() == {
        "detail": "Authentication credentials were not provided."
    }


def test_api_domain_invitations__create__no_access_forbidden_not_found():
    """Users should not be permitted to send domain management invitations
    for a domain they don't manage."""
    user = core_factories.UserFactory()
    domain = factories.MailDomainEnabledFactory()

    client = APIClient()
    client.force_login(user)
    response = client.post(
        f"/api/v1.0/mail-domains/{domain.slug}/invitations/",
        {
            "email": "some.email@domain.com",
            "role": "viewer",
        },
        format="json",
    )
    assert response.status_code == status.HTTP_404_NOT_FOUND


@pytest.mark.parametrize(
    "role",
    ["owner", "administrator"],
)
def test_api_domain_invitations__admin_should_create_invites(role):
    """Owners and administrators should be able to invite new domain managers."""
    user = core_factories.UserFactory(language="fr-fr")
    domain = factories.MailDomainEnabledFactory()
    factories.MailDomainAccessFactory(domain=domain, user=user, role=role)

    client = APIClient()
    client.force_login(user)
    assert len(mail.outbox) == 0
    response = client.post(
        f"/api/v1.0/mail-domains/{domain.slug}/invitations/",
        {
            "email": "some.email@domain.com",
            "role": "administrator",
        },
        format="json",
    )
    assert response.status_code == status.HTTP_201_CREATED
    assert len(mail.outbox) == 1
    email = mail.outbox[0]
    assert email.to == ["some.email@domain.com"]
    assert email.subject == "[La Suite] Vous avez été invité(e) à rejoindre la Régie"


def test_api_domain_invitations__viewers_forbidden():
    """
    Domain viewers should not be able to invite new domain managers.
    """
    user = core_factories.UserFactory()
    domain = factories.MailDomainEnabledFactory()
    factories.MailDomainAccessFactory(
        domain=domain, user=user, role=enums.MailDomainRoleChoices.VIEWER
    )

    client = APIClient()
    client.force_login(user)
    response = client.post(
        f"/api/v1.0/mail-domains/{domain.slug}/invitations/",
        {
            "email": "some.email@domain.com",
            "role": "viewer",
        },
        format="json",
    )
    assert response.status_code == status.HTTP_403_FORBIDDEN
    assert response.json() == {
        "detail": "You do not have permission to perform this action."
    }


def test_api_domain_invitations__should_not_create_duplicate_invitations():
    """An email should not be invited multiple times to the same domain."""
    existing_invitation = factories.MailDomainInvitationFactory(
        role=enums.MailDomainRoleChoices.ADMIN
    )
    domain = existing_invitation.domain

    # Grant privileged role on the domain to the user
    user = core_factories.UserFactory()
    factories.MailDomainAccessFactory(
        domain=domain, user=user, role=enums.MailDomainRoleChoices.OWNER
    )

    # New invitation to the same domain with the exact same email address
    client = APIClient()
    client.force_login(user)
    response = client.post(
        f"/api/v1.0/mail-domains/{domain.slug}/invitations/",
        {
            "email": existing_invitation.email,
            "role": "viewer",
        },
        format="json",
    )
    assert response.status_code == status.HTTP_400_BAD_REQUEST
    assert response.json()["__all__"] == [
        "Mail domain invitation with this Email address and Domain already exists."
    ]
    invit = models.MailDomainInvitation.objects.get()
    assert invit.role == existing_invitation.role


def test_api_domain_invitations__inviting_known_email_should_create_access():
    """Already existing users should not be invited but given access directly."""
    existing_user = core_factories.UserFactory()
    access = factories.MailDomainAccessFactory(role=enums.MailDomainRoleChoices.ADMIN)

    client = APIClient()
    client.force_login(access.user)
    response = client.post(
        f"/api/v1.0/mail-domains/{access.domain.slug}/invitations/",
        {
            "email": existing_user.email,
            "role": enums.MailDomainRoleChoices.ADMIN,
        },
        format="json",
    )
    assert response.status_code == status.HTTP_201_CREATED
    assert (
        response.json()["detail"]
        == "Email already known. Invitation not sent but access created instead."
    )

    assert not models.MailDomainInvitation.objects.exists()
    access = models.MailDomainAccess.objects.get(user=existing_user)
    assert access.role == enums.MailDomainRoleChoices.ADMIN


def test_api_domain_invitations__inviting_known_email_case_insensitive():
    """Email matching should be case-insensitive when creating access for existing users."""
    # Create a user with lowercase email
    existing_user = core_factories.UserFactory(email="john.doe@example.com")
    access = factories.MailDomainAccessFactory(role=enums.MailDomainRoleChoices.OWNER)

    client = APIClient()
    client.force_login(access.user)

    # Try to invite with same email different case - should create access for existing user
    response = client.post(
        f"/api/v1.0/mail-domains/{access.domain.slug}/invitations/",
        {
            "email": "John.Doe@Example.COM",
            "role": "owner",
        },
        format="json",
    )
    assert response.status_code == status.HTTP_201_CREATED
    assert (
        response.json()["detail"]
        == "Email already known. Invitation not sent but access created instead."
    )

    # No invitation should be created
    assert not models.MailDomainInvitation.objects.exists()

    # Access should be created for the existing user (not a new user)
    assert models.MailDomainAccess.objects.filter(user=existing_user).exists()
    assert models.MailDomainAccess.objects.filter(
        user=existing_user, domain=access.domain
    ).exists()

    # Ensure only one user exists (no duplicate user created)
    assert models.User.objects.filter(email__iexact="john.doe@example.com").count() == 1


def test_api_domain_invitations__should_never_invite_above_issuer_role():
    """Should not be able to invite with higher than issuer's."""
    access = factories.MailDomainAccessFactory(role=enums.MailDomainRoleChoices.ADMIN)
    client = APIClient()
    client.force_login(access.user)

    # Doesn't work for normal invitations
    response = client.post(
        f"/api/v1.0/mail-domains/{access.domain.slug}/invitations/",
        {
            "email": "some.email@mail.com",
            "role": enums.MailDomainRoleChoices.OWNER,
        },
        format="json",
    )
    assert response.status_code == status.HTTP_403_FORBIDDEN
    assert response.json() == {"role": "You cannot grant a role higher than your own."}

    # Also doesn't work when user already exists
    existing_user = core_factories.UserFactory()

    response = client.post(
        f"/api/v1.0/mail-domains/{access.domain.slug}/invitations/",
        {
            "email": existing_user.email,
            "role": enums.MailDomainRoleChoices.OWNER,
        },
        format="json",
    )
    assert response.status_code == status.HTTP_403_FORBIDDEN
    assert response.json() == {"role": "You cannot grant a role higher than your own."}
