"""Tests for the home_user module.

This module contains comprehensive tests for the HomeUser class
which manages shared users within a Kwikset home.
"""

from __future__ import annotations

from typing import Any
from unittest.mock import AsyncMock

import pytest

from aiokwikset.home_user import HomeUser
from tests.conftest import MOCK_HOME_USERS


class TestGetUsers:
    """Tests for HomeUser.get_users method."""

    @pytest.mark.asyncio
    async def test_get_users_success(
        self, home_user_handler: HomeUser, mock_request: AsyncMock
    ) -> None:
        """Test successfully getting home users."""
        mock_request.return_value = MOCK_HOME_USERS
        result = await home_user_handler.get_users("home-001")
        mock_request.assert_called_once_with("get", mock_request.call_args[0][1])
        assert len(result) == 2
        assert result[0]["email"] == "alexandrialaken@gmail.com"
        assert result[1]["email"] == "ashtonboomer123@gmail.com"

    @pytest.mark.asyncio
    async def test_get_users_url_construction(
        self, home_user_handler: HomeUser, mock_request: AsyncMock
    ) -> None:
        """Test URL contains the correct home ID."""
        mock_request.return_value = MOCK_HOME_USERS
        await home_user_handler.get_users("test-home-id")
        call_url = mock_request.call_args[0][1]
        assert "test-home-id" in call_url
        assert "/sharedusers" in call_url

    @pytest.mark.asyncio
    async def test_get_users_empty(
        self, home_user_handler: HomeUser, mock_request: AsyncMock
    ) -> None:
        """Test getting users when none exist."""
        mock_request.return_value = {"data": [], "total": 0}
        result = await home_user_handler.get_users("home-001")
        assert result == []

    @pytest.mark.asyncio
    async def test_get_users_missing_data_key(
        self, home_user_handler: HomeUser, mock_request: AsyncMock
    ) -> None:
        """Test response missing data key returns empty list."""
        mock_request.return_value = {}
        result = await home_user_handler.get_users("home-001")
        assert result == []

    @pytest.mark.asyncio
    async def test_get_users_access_level_fields(
        self, home_user_handler: HomeUser, mock_request: AsyncMock
    ) -> None:
        """Test that access level fields are present in response."""
        mock_request.return_value = MOCK_HOME_USERS
        result = await home_user_handler.get_users("home-001")
        assert result[0]["useraccesslevel"] == "Member"
        assert result[0]["useraccesslevelstatus"] == "Enabled"
        assert result[0]["alloweddevices"] == ["1041d5b8b6eb2ea917"]


class TestInviteUser:
    """Tests for HomeUser.invite_user method."""

    @pytest.mark.asyncio
    async def test_invite_user_success(
        self, home_user_handler: HomeUser, mock_request: AsyncMock
    ) -> None:
        """Test successfully inviting a user."""
        mock_request.return_value = {}
        result = await home_user_handler.invite_user(
            home_id="home-001",
            email="test@example.com",
            access_level="Member",
            nickname="Test User",
            allowed_devices=["device-001"],
        )
        assert result == {}
        mock_request.assert_called_once()

    @pytest.mark.asyncio
    async def test_invite_user_url_construction(
        self, home_user_handler: HomeUser, mock_request: AsyncMock
    ) -> None:
        """Test invite URL contains correct home ID."""
        mock_request.return_value = {}
        await home_user_handler.invite_user(
            home_id="test-home-id",
            email="test@example.com",
            access_level="Member",
            nickname="Test",
            allowed_devices=["device-001"],
        )
        call_url = mock_request.call_args[0][1]
        assert "test-home-id" in call_url
        assert "/sharedusers_v2" in call_url

    @pytest.mark.asyncio
    async def test_invite_user_request_body(
        self, home_user_handler: HomeUser, mock_request: AsyncMock
    ) -> None:
        """Test invite sends correct JSON body."""
        mock_request.return_value = {}
        await home_user_handler.invite_user(
            home_id="home-001",
            email="newuser@example.com",
            access_level="Member",
            nickname="New User",
            allowed_devices=["device-001", "device-002"],
        )
        call_kwargs = mock_request.call_args[1]
        assert call_kwargs["json"]["email"] == "newuser@example.com"
        assert call_kwargs["json"]["accesslevel"] == "Member"
        assert call_kwargs["json"]["shareenickname"] == "New User"
        assert call_kwargs["json"]["alloweddevices"] == ["device-001", "device-002"]

    @pytest.mark.asyncio
    async def test_invite_user_method_is_post(
        self, home_user_handler: HomeUser, mock_request: AsyncMock
    ) -> None:
        """Test invite uses POST method."""
        mock_request.return_value = {}
        await home_user_handler.invite_user(
            home_id="home-001",
            email="test@example.com",
            access_level="Member",
            nickname="Test",
            allowed_devices=["device-001"],
        )
        assert mock_request.call_args[0][0] == "post"

    @pytest.mark.asyncio
    async def test_invite_user_with_date_range_schedule(
        self, home_user_handler: HomeUser, mock_request: AsyncMock
    ) -> None:
        """Test invite with DATE_RANGE access time."""
        access_time: dict[str, Any] = {
            "accesstimetype": "DATE_RANGE",
            "startdate": "1771263195",
            "enddate": "1771608795",
        }
        mock_request.return_value = {}
        await home_user_handler.invite_user(
            home_id="home-001",
            email="test@example.com",
            access_level="Member",
            nickname="HA",
            allowed_devices=["1041d5b8b6eb2ea917"],
            access_time=access_time,
        )
        call_kwargs = mock_request.call_args[1]
        assert call_kwargs["json"]["accesstime"] == access_time
        assert call_kwargs["json"]["accesstime"]["accesstimetype"] == "DATE_RANGE"

    @pytest.mark.asyncio
    async def test_invite_user_with_weekly_repeat(
        self, home_user_handler: HomeUser, mock_request: AsyncMock
    ) -> None:
        """Test invite with WEEKLY repeat access time."""
        access_time: dict[str, Any] = {
            "accesstimetype": "WEEKLY",
            "starttime": "1771221655",
            "endtime": "1771307995",
            "days": ["Mon", "Tue", "Wed", "Thu", "Fri"],
        }
        mock_request.return_value = {}
        await home_user_handler.invite_user(
            home_id="home-001",
            email="test@example.com",
            access_level="Member",
            nickname="HA",
            allowed_devices=["1041d5b8b6eb2ea917"],
            access_time=access_time,
        )
        call_kwargs = mock_request.call_args[1]
        assert call_kwargs["json"]["accesstime"] == access_time
        assert call_kwargs["json"]["accesstime"]["days"] == [
            "Mon",
            "Tue",
            "Wed",
            "Thu",
            "Fri",
        ]

    @pytest.mark.asyncio
    async def test_invite_user_without_access_time_omits_key(
        self, home_user_handler: HomeUser, mock_request: AsyncMock
    ) -> None:
        """Test invite without access_time does not include accesstime in body."""
        mock_request.return_value = {}
        await home_user_handler.invite_user(
            home_id="home-001",
            email="test@example.com",
            access_level="Member",
            nickname="Test",
            allowed_devices=["device-001"],
        )
        call_kwargs = mock_request.call_args[1]
        assert "accesstime" not in call_kwargs["json"]


class TestUpdateUser:
    """Tests for HomeUser.update_user method."""

    @pytest.mark.asyncio
    async def test_update_user_guest_access(
        self, home_user_handler: HomeUser, mock_request: AsyncMock
    ) -> None:
        """Test updating user to guest/member access."""
        mock_request.return_value = {}
        result = await home_user_handler.update_user(
            home_id="home-001",
            email="test@example.com",
            access_level="Member",
            allowed_devices=["device-001"],
        )
        assert result == {}
        call_kwargs = mock_request.call_args[1]
        assert call_kwargs["json"]["accesslevel"] == "Member"
        assert call_kwargs["json"]["accesstime"] == {}

    @pytest.mark.asyncio
    async def test_update_user_admin_access(
        self, home_user_handler: HomeUser, mock_request: AsyncMock
    ) -> None:
        """Test updating user to admin access."""
        mock_request.return_value = {}
        await home_user_handler.update_user(
            home_id="home-001",
            email="test@example.com",
            access_level="Admin",
            allowed_devices=["device-001"],
        )
        call_kwargs = mock_request.call_args[1]
        assert call_kwargs["json"]["accesslevel"] == "Admin"

    @pytest.mark.asyncio
    async def test_update_user_with_schedule(
        self, home_user_handler: HomeUser, mock_request: AsyncMock
    ) -> None:
        """Test updating user with date range schedule."""
        access_time: dict[str, Any] = {
            "accesstimetype": "DATE_RANGE",
            "startdate": "1771254332",
            "enddate": "1771599932",
        }
        mock_request.return_value = {}
        await home_user_handler.update_user(
            home_id="home-001",
            email="test@example.com",
            access_level="Member",
            allowed_devices=["device-001"],
            access_time=access_time,
        )
        call_kwargs = mock_request.call_args[1]
        assert call_kwargs["json"]["accesstime"] == access_time

    @pytest.mark.asyncio
    async def test_update_user_url_construction(
        self, home_user_handler: HomeUser, mock_request: AsyncMock
    ) -> None:
        """Test update URL contains home ID and email."""
        mock_request.return_value = {}
        await home_user_handler.update_user(
            home_id="test-home-id",
            email="user@example.com",
            access_level="Member",
            allowed_devices=["device-001"],
        )
        call_url = mock_request.call_args[0][1]
        assert "test-home-id" in call_url
        assert "user@example.com" in call_url
        assert "/sharedusers_v2/" in call_url

    @pytest.mark.asyncio
    async def test_update_user_method_is_patch(
        self, home_user_handler: HomeUser, mock_request: AsyncMock
    ) -> None:
        """Test update uses PATCH method."""
        mock_request.return_value = {}
        await home_user_handler.update_user(
            home_id="home-001",
            email="test@example.com",
            access_level="Member",
            allowed_devices=["device-001"],
        )
        assert mock_request.call_args[0][0] == "patch"


class TestDeleteUser:
    """Tests for HomeUser.delete_user method."""

    @pytest.mark.asyncio
    async def test_delete_user_success(
        self, home_user_handler: HomeUser, mock_request: AsyncMock
    ) -> None:
        """Test successfully deleting a user."""
        mock_request.return_value = {}
        result = await home_user_handler.delete_user("home-001", "test@example.com")
        assert result == {}
        mock_request.assert_called_once()

    @pytest.mark.asyncio
    async def test_delete_user_url_construction(
        self, home_user_handler: HomeUser, mock_request: AsyncMock
    ) -> None:
        """Test delete URL contains home ID and email."""
        mock_request.return_value = {}
        await home_user_handler.delete_user("test-home-id", "user@example.com")
        call_url = mock_request.call_args[0][1]
        assert "test-home-id" in call_url
        assert "user@example.com" in call_url
        assert "/sharedusers/" in call_url

    @pytest.mark.asyncio
    async def test_delete_user_method_is_delete(
        self, home_user_handler: HomeUser, mock_request: AsyncMock
    ) -> None:
        """Test delete uses DELETE method."""
        mock_request.return_value = {}
        await home_user_handler.delete_user("home-001", "test@example.com")
        assert mock_request.call_args[0][0] == "delete"


class TestHomeUserInit:
    """Tests for HomeUser initialization."""

    def test_init_with_request(self, mock_request: AsyncMock) -> None:
        """Test HomeUser can be initialized with a request callable."""
        handler = HomeUser(mock_request)
        assert handler._request is mock_request

    def test_slots(self) -> None:
        """Test HomeUser uses __slots__ for memory efficiency."""
        assert hasattr(HomeUser, "__slots__")
        assert "_request" in HomeUser.__slots__
