"""Test fitbit component."""

from collections.abc import Awaitable, Callable
from http import HTTPStatus

import pytest
from requests_mock.mocker import Mocker

from homeassistant.components.fitbit.const import (
    CONF_CLIENT_ID,
    CONF_CLIENT_SECRET,
    OAUTH2_TOKEN,
)
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant

from .conftest import (
    CLIENT_ID,
    CLIENT_SECRET,
    DEVICES_API_URL,
    FAKE_ACCESS_TOKEN,
    FAKE_REFRESH_TOKEN,
    SERVER_ACCESS_TOKEN,
)

from tests.common import MockConfigEntry
from tests.test_util.aiohttp import AiohttpClientMocker


async def test_setup(
    hass: HomeAssistant,
    integration_setup: Callable[[], Awaitable[bool]],
    config_entry: MockConfigEntry,
    setup_credentials: None,
) -> None:
    """Test setting up the integration."""
    assert config_entry.state is ConfigEntryState.NOT_LOADED

    assert await integration_setup()
    assert config_entry.state is ConfigEntryState.LOADED

    assert await hass.config_entries.async_unload(config_entry.entry_id)
    await hass.async_block_till_done()

    assert config_entry.state is ConfigEntryState.NOT_LOADED


@pytest.mark.parametrize(
    ("token_expiration_time", "server_status"),
    [
        (12345, HTTPStatus.INTERNAL_SERVER_ERROR),
        (12345, HTTPStatus.FORBIDDEN),
        (12345, HTTPStatus.NOT_FOUND),
    ],
)
async def test_token_refresh_failure(
    integration_setup: Callable[[], Awaitable[bool]],
    config_entry: MockConfigEntry,
    aioclient_mock: AiohttpClientMocker,
    setup_credentials: None,
    server_status: HTTPStatus,
) -> None:
    """Test where token is expired and the refresh attempt fails and will be retried."""

    aioclient_mock.post(
        OAUTH2_TOKEN,
        status=server_status,
    )

    assert not await integration_setup()
    assert config_entry.state is ConfigEntryState.SETUP_RETRY


@pytest.mark.parametrize("token_expiration_time", [12345])
async def test_token_refresh_success(
    integration_setup: Callable[[], Awaitable[bool]],
    config_entry: MockConfigEntry,
    aioclient_mock: AiohttpClientMocker,
    setup_credentials: None,
) -> None:
    """Test where token is expired and the refresh attempt succeeds."""

    assert config_entry.data["token"]["access_token"] == FAKE_ACCESS_TOKEN

    aioclient_mock.post(
        OAUTH2_TOKEN,
        json=SERVER_ACCESS_TOKEN,
    )

    assert await integration_setup()
    assert config_entry.state is ConfigEntryState.LOADED

    # Verify token request
    assert len(aioclient_mock.mock_calls) == 1
    assert aioclient_mock.mock_calls[0][2] == {
        CONF_CLIENT_ID: CLIENT_ID,
        CONF_CLIENT_SECRET: CLIENT_SECRET,
        "grant_type": "refresh_token",
        "refresh_token": FAKE_REFRESH_TOKEN,
    }

    # Verify updated token
    assert (
        config_entry.data["token"]["access_token"]
        == SERVER_ACCESS_TOKEN["access_token"]
    )


@pytest.mark.parametrize(
    ("token_expiration_time", "server_status"),
    [
        (12345, HTTPStatus.UNAUTHORIZED),
        (12345, HTTPStatus.BAD_REQUEST),
    ],
)
@pytest.mark.parametrize("closing", [True, False])
async def test_token_requires_reauth(
    hass: HomeAssistant,
    integration_setup: Callable[[], Awaitable[bool]],
    config_entry: MockConfigEntry,
    aioclient_mock: AiohttpClientMocker,
    setup_credentials: None,
    server_status: HTTPStatus,
    closing: bool,
) -> None:
    """Test where token is expired and the refresh attempt requires reauth."""

    aioclient_mock.post(
        OAUTH2_TOKEN,
        status=server_status,
        closing=closing,
    )

    assert not await integration_setup()
    assert config_entry.state is ConfigEntryState.SETUP_ERROR

    flows = hass.config_entries.flow.async_progress()
    assert len(flows) == 1
    assert flows[0]["step_id"] == "reauth_confirm"


async def test_device_update_coordinator_failure(
    hass: HomeAssistant,
    integration_setup: Callable[[], Awaitable[bool]],
    config_entry: MockConfigEntry,
    setup_credentials: None,
    requests_mock: Mocker,
) -> None:
    """Test case where the device update coordinator fails on the first request."""
    assert config_entry.state is ConfigEntryState.NOT_LOADED

    requests_mock.register_uri(
        "GET",
        DEVICES_API_URL,
        status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
    )

    assert not await integration_setup()
    assert config_entry.state is ConfigEntryState.SETUP_RETRY


async def test_device_update_coordinator_reauth(
    hass: HomeAssistant,
    integration_setup: Callable[[], Awaitable[bool]],
    config_entry: MockConfigEntry,
    setup_credentials: None,
    requests_mock: Mocker,
) -> None:
    """Test case where the device update coordinator fails on the first request."""
    assert config_entry.state is ConfigEntryState.NOT_LOADED

    requests_mock.register_uri(
        "GET",
        DEVICES_API_URL,
        status_code=HTTPStatus.UNAUTHORIZED,
        json={
            "errors": [{"errorType": "invalid_grant"}],
        },
    )

    assert not await integration_setup()
    assert config_entry.state is ConfigEntryState.SETUP_ERROR

    flows = hass.config_entries.flow.async_progress()
    assert len(flows) == 1
    assert flows[0]["step_id"] == "reauth_confirm"