"""Tests for the notification store layer."""

from __future__ import annotations

from datetime import datetime, timezone
from unittest.mock import AsyncMock, MagicMock
from uuid import uuid4

import pytest

import notifications.config as cfg
from notifications.config import NotificationConfig
from notifications.models import CreateNotification, NotificationType
from notifications.store import (
    delete_old,
    get_by_user,
    get_preferences,
    get_unread_count,
    insert,
    mark_all_read,
    mark_read,
    set_preferences,
)


class TestInsert:
    """Tests for store.insert()."""

    async def test_insert_creates_notification(
        self, initialized_config: NotificationConfig, mock_conn: AsyncMock
    ):
        mock_conn.execute = AsyncMock()

        payload = CreateNotification(
            user_id="user-123",
            type=NotificationType.INFO,
            title="Test notification",
            message="This is a test",
            action_url="https://example.com",
        )

        result = await insert(payload)

        assert result.user_id == "user-123"
        assert result.type == NotificationType.INFO
        assert result.title == "Test notification"
        assert result.message == "This is a test"
        assert result.action_url == "https://example.com"
        assert result.read is False
        assert result.id is not None
        mock_conn.execute.assert_called_once()

    async def test_insert_without_action_url(
        self, initialized_config: NotificationConfig, mock_conn: AsyncMock
    ):
        mock_conn.execute = AsyncMock()

        payload = CreateNotification(
            user_id="user-456",
            type=NotificationType.ERROR,
            title="Error occurred",
            message="Something went wrong",
        )

        result = await insert(payload)

        assert result.action_url is None
        assert result.type == NotificationType.ERROR


class TestGetByUser:
    """Tests for store.get_by_user()."""

    async def test_get_by_user_returns_notifications(
        self, initialized_config: NotificationConfig, mock_conn: AsyncMock
    ):
        now = datetime.now(timezone.utc)
        nid = uuid4()
        mock_conn.fetch = AsyncMock(
            return_value=[
                {
                    "id": nid,
                    "user_id": "user-123",
                    "type": "info",
                    "title": "Test",
                    "message": "Hello",
                    "action_url": None,
                    "read": False,
                    "created_at": now,
                }
            ]
        )

        results = await get_by_user("user-123", limit=10, offset=0)

        assert len(results) == 1
        assert results[0].id == nid
        assert results[0].user_id == "user-123"
        mock_conn.fetch.assert_called_once()

    async def test_get_by_user_unread_only(
        self, initialized_config: NotificationConfig, mock_conn: AsyncMock
    ):
        mock_conn.fetch = AsyncMock(return_value=[])

        results = await get_by_user("user-123", limit=10, offset=0, unread_only=True)

        assert results == []
        # Verify the query included the unread filter
        call_args = mock_conn.fetch.call_args
        query = call_args[0][0]
        assert "read = FALSE" in query


class TestMarkRead:
    """Tests for store.mark_read() and store.mark_all_read()."""

    async def test_mark_read_returns_true_on_success(
        self, initialized_config: NotificationConfig, mock_conn: AsyncMock
    ):
        mock_conn.execute = AsyncMock(return_value="UPDATE 1")
        nid = uuid4()

        result = await mark_read(nid)

        assert result is True

    async def test_mark_read_returns_false_on_miss(
        self, initialized_config: NotificationConfig, mock_conn: AsyncMock
    ):
        mock_conn.execute = AsyncMock(return_value="UPDATE 0")
        nid = uuid4()

        result = await mark_read(nid)

        assert result is False

    async def test_mark_all_read_returns_count(
        self, initialized_config: NotificationConfig, mock_conn: AsyncMock
    ):
        mock_conn.execute = AsyncMock(return_value="UPDATE 5")

        count = await mark_all_read("user-123")

        assert count == 5


class TestUnreadCount:
    """Tests for store.get_unread_count()."""

    async def test_returns_count(
        self, initialized_config: NotificationConfig, mock_conn: AsyncMock
    ):
        mock_conn.fetchval = AsyncMock(return_value=7)

        count = await get_unread_count("user-123")

        assert count == 7

    async def test_returns_zero_on_none(
        self, initialized_config: NotificationConfig, mock_conn: AsyncMock
    ):
        mock_conn.fetchval = AsyncMock(return_value=None)

        count = await get_unread_count("user-123")

        assert count == 0


class TestDeleteOld:
    """Tests for store.delete_old()."""

    async def test_deletes_old_notifications(
        self, initialized_config: NotificationConfig, mock_conn: AsyncMock
    ):
        mock_conn.execute = AsyncMock(return_value="DELETE 42")

        count = await delete_old(90)

        assert count == 42


class TestPreferences:
    """Tests for store.get_preferences() and store.set_preferences()."""

    async def test_get_preferences_returns_defaults_when_missing(
        self, initialized_config: NotificationConfig, mock_conn: AsyncMock
    ):
        mock_conn.fetchrow = AsyncMock(return_value=None)

        prefs = await get_preferences("user-999")

        assert prefs.user_id == "user-999"
        assert prefs.email_enabled is True
        assert prefs.in_app_enabled is True
        assert prefs.digest_frequency.value == "none"

    async def test_get_preferences_returns_stored_values(
        self, initialized_config: NotificationConfig, mock_conn: AsyncMock
    ):
        mock_conn.fetchrow = AsyncMock(
            return_value={
                "user_id": "user-123",
                "email_enabled": False,
                "in_app_enabled": True,
                "digest_frequency": "daily",
            }
        )

        prefs = await get_preferences("user-123")

        assert prefs.email_enabled is False
        assert prefs.digest_frequency.value == "daily"

    async def test_set_preferences_upserts(
        self, initialized_config: NotificationConfig, mock_conn: AsyncMock
    ):
        from notifications.models import DigestFrequency, NotificationPreferences

        mock_conn.execute = AsyncMock()

        prefs = NotificationPreferences(
            user_id="user-123",
            email_enabled=False,
            in_app_enabled=True,
            digest_frequency=DigestFrequency.WEEKLY,
        )

        result = await set_preferences("user-123", prefs)

        assert result.email_enabled is False
        assert result.digest_frequency == DigestFrequency.WEEKLY
        mock_conn.execute.assert_called_once()
