import pytest
from django.contrib.auth import get_user_model
from django.utils import timezone
from apps.notifications.models import Notification, NotificationTemplate, NotificationPreference
from apps.notifications.handler import NotificationHandler


@pytest.fixture
def user(db):
    User = get_user_model()
    return User.objects.create_user(email="notify@example.com", password="pass123")


@pytest.fixture
def user2(db):
    User = get_user_model()
    return User.objects.create_user(email="notify2@example.com", password="pass123")


@pytest.mark.django_db
class TestNotificationModel:
    def test_create_notification(self, user):
        notification = Notification.objects.create(
            user=user,
            title="Test Notification",
            message="This is a test",
            notification_type="info",
            priority="medium"
        )
        assert notification.title == "Test Notification"
        assert notification.is_read is False
        assert notification.notification_type == "info"

    def test_notification_mark_as_read(self, user):
        notification = Notification.objects.create(
            user=user,
            title="Test",
            message="Test message"
        )
        notification.mark_as_read()
        notification.refresh_from_db()
        assert notification.is_read is True
        assert notification.read_at is not None

    def test_notification_soft_delete(self, user):
        notification = Notification.objects.create(
            user=user,
            title="Test",
            message="Test message"
        )
        notification.mark_as_deleted()
        notification.refresh_from_db()
        assert notification.is_deleted is True

    def test_notification_types(self, user):
        types = ["info", "success", "warning", "error", "task", "event", "chat"]
        for ntype in types:
            Notification.objects.create(
                user=user,
                title=f"{ntype} notification",
                message="Test",
                notification_type=ntype
            )
        assert Notification.objects.filter(notification_type="task").count() == 1
        assert Notification.objects.filter(notification_type="chat").count() == 1

    def test_notification_metadata(self, user):
        metadata = {"task_id": 123, "assigned_by": "admin"}
        notification = Notification.objects.create(
            user=user,
            title="New Task",
            message="You have a new task",
            metadata=metadata
        )
        assert notification.metadata == {"task_id": 123, "assigned_by": "admin"}

    def test_notification_priority(self, user):
        notification = Notification.objects.create(
            user=user,
            title="Urgent",
            message="This is urgent",
            priority="urgent"
        )
        assert notification.priority == "urgent"

    def test_notification_with_link(self, user):
        notification = Notification.objects.create(
            user=user,
            title="New Message",
            message="You have a new message",
            link="/chat/conversation/123"
        )
        assert notification.link == "/chat/conversation/123"


@pytest.mark.django_db
class TestNotificationTemplateModel:
    def test_create_template(self):
        template = NotificationTemplate.objects.create(
            name="task_assigned",
            title_template="New Task: {task_title}",
            message_template="You have been assigned to task #{task_id}",
            notification_type="task",
            icon="task"
        )
        assert template.name == "task_assigned"
        assert "{task_title}" in template.title_template

    def test_template_str(self):
        template = NotificationTemplate.objects.create(
            name="test_template",
            title_template="Test",
            message_template="Test message",
            notification_type="info"
        )
        assert str(template) == "test_template"


@pytest.mark.django_db
class TestNotificationPreferenceModel:
    def test_create_preference(self, user):
        prefs = NotificationPreference.objects.create(user=user)
        assert prefs.email_enabled is True
        assert prefs.push_enabled is True

    def test_type_enabled(self, user):
        prefs = NotificationPreference.objects.create(user=user)
        assert prefs.is_type_enabled("task") is True
        prefs.set_type_enabled("task", False)
        prefs.refresh_from_db()
        assert prefs.is_type_enabled("task") is False

    def test_quiet_hours(self, user):
        from datetime import time
        prefs = NotificationPreference.objects.create(
            user=user,
            quiet_hours_start=time(22, 0),
            quiet_hours_end=time(8, 0)
        )
        assert prefs.quiet_hours_start.hour == 22


@pytest.mark.django_db
class TestNotificationHandler:
    def test_send_notification(self, user):
        notification = NotificationHandler.send(
            user=user,
            title="Handler Test",
            message="Testing the handler",
            notification_type="info"
        )
        assert notification.user == user
        assert notification.title == "Handler Test"

    def test_broadcast_notifications(self, user, user2):
        notifications = NotificationHandler.broadcast(
            users=[user, user2],
            title="Broadcast Test",
            message="This is a broadcast",
            notification_type="info"
        )
        assert len(notifications) == 2

    def test_send_to_role(self, user):
        from django.contrib.auth.models import Group
        group = Group.objects.create(name="testers")
        user.groups.add(group)

        notifications = NotificationHandler.broadcast(
            users=[user],
            title="Role Test",
            message="Testing role-based notification",
            notification_type="system"
        )
        assert len(notifications) == 1

    def test_mark_as_read(self, user):
        notification = Notification.objects.create(
            user=user,
            title="To Mark Read",
            message="Will be marked read"
        )
        success = NotificationHandler.mark_as_read(str(notification.id), user)
        assert success is True
        notification.refresh_from_db()
        assert notification.is_read is True

    def test_mark_all_as_read(self, user):
        Notification.objects.create(user=user, title="N1", message="M1")
        Notification.objects.create(user=user, title="N2", message="M2")
        count = NotificationHandler.mark_all_as_read(user)
        assert count == 2

    def test_get_unread_count(self, user):
        Notification.objects.create(user=user, title="N1", message="M1")
        Notification.objects.create(user=user, title="N2", message="M2")
        count = NotificationHandler.get_unread_count(user)
        assert count == 2

    def test_get_unread(self, user):
        Notification.objects.create(user=user, title="N1", message="M1")
        Notification.objects.create(user=user, title="N2", message="M2")
        unread = NotificationHandler.get_unread(user, limit=10)
        assert len(unread) == 2


@pytest.mark.django_db
class TestFCMDevice:
    def test_register_device(self, user, client):
        client.force_login(user)
        from apps.notifications.models import FCMDevice
        FCMDevice.objects.create(user=user, token="tok-abc", platform="web")
        assert FCMDevice.objects.filter(user=user, is_active=True).count() == 1

    def test_send_skips_when_fcm_unconfigured(self, user, settings):
        # No credentials file -> push is a no-op, never raises.
        settings.FIREBASE_CREDENTIALS_FILE = ""
        from apps.notifications import fcm
        from apps.notifications.models import FCMDevice
        FCMDevice.objects.create(user=user, token="tok-xyz", platform="web")
        assert fcm.send_to_user(user, title="Hi", body="There") == 0

    def test_handler_push_respects_disabled_pref(self, user):
        # push_enabled=False must not raise even with a registered device.
        prefs = NotificationPreference.objects.create(user=user, push_enabled=False)
        from apps.notifications.models import FCMDevice
        FCMDevice.objects.create(user=user, token="tok-1", platform="web")
        n = NotificationHandler.send(user=user, title="T", message="M")
        assert n.pk is not None
