import asyncio
from datetime import timedelta
import os

import pytest
from aiohttp.client_exceptions import ClientResponseError

from backup.config import Config, Setting, CreateOptions, Version
from backup.const import SOURCE_HA
from backup.exceptions import (HomeAssistantDeleteError, BackupInProgress,
                               BackupPasswordKeyInvalid, UploadFailed, SupervisorConnectionError, SupervisorPermissionError, SupervisorTimeoutError, UnknownNetworkStorageError, InactiveNetworkStorageError)
from backup.util import GlobalInfo, DataCache, KEY_CREATED, KEY_LAST_SEEN, KEY_NAME
from backup.ha import HaSource, PendingBackup, EVENT_BACKUP_END, EVENT_BACKUP_START, HABackup, Password, AddonStopper
from backup.model import DummyBackup
from dev.simulationserver import SimulationServer
from .faketime import FakeTime
from .helpers import all_addons, all_folders, createBackupTar, getTestStream
from dev.simulated_supervisor import SimulatedSupervisor, URL_MATCH_SELF_OPTIONS, URL_MATCH_START_ADDON, URL_MATCH_STOP_ADDON, URL_MATCH_BACKUP_FULL, URL_MATCH_BACKUP_DELETE, URL_MATCH_MISC_INFO, URL_MATCH_BACKUP_DOWNLOAD, URL_MATCH_BACKUPS, URL_MATCH_SNAPSHOT, URL_MATCH_MOUNT
from dev.request_interceptor import RequestInterceptor
from backup.model import Model
from backup.time import Time
from yarl import URL


@pytest.mark.asyncio
async def test_sync_empty(ha) -> None:
    assert len(await ha.get()) == 0


@pytest.mark.asyncio
async def test_CRUD(ha: HaSource, time, interceptor: RequestInterceptor, data_cache: DataCache) -> None:
    backup: HABackup = await ha.create(CreateOptions(time.now(), "Test Name"))

    assert backup.name() == "Test Name"
    assert type(backup) is HABackup
    assert not backup.retained()
    assert backup.backupType() == "full"
    assert not backup.protected()
    assert backup.name() == "Test Name"
    assert backup.source() == SOURCE_HA
    assert not backup.ignore()
    assert backup.madeByTheAddon()
    assert "pending" not in data_cache.backups

    # read the item directly, its metadata should match
    from_ha = await ha.harequests.backup(backup.slug())
    assert from_ha.size() == backup.size()
    assert from_ha.slug() == backup.slug()
    assert from_ha.source() == SOURCE_HA

    backups = await ha.get()
    assert len(backups) == 1
    assert backup.slug() in backups

    full = DummyBackup(from_ha.name(), from_ha.date(),
                       from_ha.size(), from_ha.slug(), "dummy")
    full.addSource(backup)

    # download the item, its bytes should match up
    download = await ha.read(full)
    await download.setup()
    direct_download = await ha.harequests.download(backup.slug())
    await direct_download.setup()
    while True:
        from_file = await direct_download.read(1024 * 1024)
        from_download = await download.read(1024 * 1024)
        if len(from_file.getbuffer()) == 0:
            assert len(from_download.getbuffer()) == 0
            break
        assert from_file.getbuffer() == from_download.getbuffer()

    # update retention
    assert not backup.retained()
    await ha.retain(full, True)
    assert (await ha.get())[full.slug()].retained()
    await ha.retain(full, False)
    assert not (await ha.get())[full.slug()].retained()

    # Delete the item, make sure its gone
    await ha.delete(full)
    assert full.getSource(ha.name()) is None
    backups = await ha.get()
    assert len(backups) == 0


@pytest.mark.asyncio
@pytest.mark.timeout(10)
async def test_pending_backup_nowait(ha: HaSource, time: Time, supervisor: SimulatedSupervisor, interceptor: RequestInterceptor, config: Config, data_cache: DataCache):
    interceptor.setSleep(URL_MATCH_BACKUP_FULL, sleep=5)
    config.override(Setting.NEW_BACKUP_TIMEOUT_SECONDS, 0.1)
    backup_immediate: PendingBackup = await ha.create(CreateOptions(time.now(), "Test Name"))
    assert isinstance(backup_immediate, PendingBackup)
    backup_pending: HABackup = (await ha.get())['pending']

    assert isinstance(backup_immediate, PendingBackup)
    assert isinstance(backup_pending, PendingBackup)
    assert backup_immediate is backup_pending
    assert backup_immediate.name() == "Test Name"
    assert backup_immediate.slug() == "pending"
    assert not backup_immediate.uploadable()
    assert backup_immediate.backupType() == "Full"
    assert backup_immediate.source() == SOURCE_HA
    assert backup_immediate.date() == time.now()
    assert not backup_immediate.protected()
    assert not backup_immediate.ignore()
    assert backup_immediate.madeByTheAddon()
    assert data_cache.backup("pending") == {
        KEY_CREATED: time.now().isoformat(),
        KEY_LAST_SEEN: time.now().isoformat(),
        KEY_NAME: "Test Name"
    }

    # Might be a little flaky but...whatever
    await asyncio.wait({ha._pending_backup_task})

    backups = await ha.get()
    assert 'pending' not in backups
    assert len(backups) == 1
    backup = next(iter(backups.values()))
    assert isinstance(backup, HABackup)
    assert not backup.ignore()
    assert backup.madeByTheAddon()
    assert data_cache.backup(backup.slug())[KEY_LAST_SEEN] == time.now().isoformat()
    assert "pending" not in data_cache.backups

    return
    # ignroe events for now
    assert supervisor.getEvents() == [
        (EVENT_BACKUP_START, {
            'backup_name': backup_immediate.name(),
            'backup_time': str(backup_immediate.date())})]
    ha.backup_thread.join()
    assert supervisor.getEvents() == [
        (EVENT_BACKUP_START, {
            'backup_name': backup_immediate.name(),
            'backup_time': str(backup_immediate.date())}),
        (EVENT_BACKUP_END, {
            'completed': True,
            'backup_name': backup_immediate.name(),
            'backup_time': str(backup_immediate.date())})]


@pytest.mark.asyncio
async def test_pending_backup_already_in_progress(ha, time, config: Config, supervisor: SimulatedSupervisor):
    await ha.create(CreateOptions(time.now(), "Test Name"))
    assert len(await ha.get()) == 1

    config.override(Setting.NEW_BACKUP_TIMEOUT_SECONDS, 100)
    await supervisor.toggleBlockBackup()
    with pytest.raises(BackupInProgress):
        await ha.create(CreateOptions(time.now(), "Test Name"))
    backups = list((await ha.get()).values())
    assert len(backups) == 2
    backup = backups[1]

    assert isinstance(backup, PendingBackup)
    assert backup.name() == "Pending Backup"
    assert backup.slug() == "pending"
    assert not backup.uploadable()
    assert backup.backupType() == "unknown"
    assert backup.source() == SOURCE_HA
    assert backup.date() == time.now()
    assert not backup.protected()

    with pytest.raises(BackupInProgress):
        await ha.create(CreateOptions(time.now(), "Test Name"))


@pytest.mark.asyncio
async def test_partial_backup(ha, time, server, config: Config):
    config.override(Setting.NEW_BACKUP_TIMEOUT_SECONDS, 100)
    for folder in all_folders:
        config.override(Setting.EXCLUDE_FOLDERS, folder)
        backup: HABackup = await ha.create(CreateOptions(time.now(), "Test Name"))

        assert backup.backupType() == "partial"
        for search in all_folders:
            if search == folder:
                assert search not in backup.details()['folders']
            else:
                assert search in backup.details()['folders']

    for addon in all_addons:
        config.override(Setting.EXCLUDE_ADDONS, addon['slug'])
        backup: HABackup = await ha.create(CreateOptions(time.now(), "Test Name"))
        assert backup.backupType() == "partial"
        list_of_addons = []
        for included in backup.details()['addons']:
            list_of_addons.append(included['slug'])
        for search in list_of_addons:
            if search == addon:
                assert search not in list_of_addons
            else:
                assert search in list_of_addons

    # excluding addon/folders that don't exist should actually make a full backup
    config.override(Setting.EXCLUDE_ADDONS, "none,of.these,are.addons")
    config.override(Setting.EXCLUDE_FOLDERS, "not,folders,either")
    backup: HABackup = await ha.create(CreateOptions(time.now(), "Test Name"))
    assert backup.backupType() == "full"


@pytest.mark.asyncio
async def test_backup_password(ha: HaSource, config: Config, time):
    config.override(Setting.NEW_BACKUP_TIMEOUT_SECONDS, 100)
    backup: HABackup = await ha.create(CreateOptions(time.now(), "Test Name"))
    assert not backup.protected()

    config.override(Setting.BACKUP_PASSWORD, 'test')
    backup = await ha.create(CreateOptions(time.now(), "Test Name"))
    assert backup.protected()

    config.override(Setting.BACKUP_PASSWORD, 'test')
    assert Password(ha.config).resolve() == 'test'

    config.override(Setting.BACKUP_PASSWORD, '!secret for_unit_tests')
    assert Password(ha.config).resolve() == 'password value'

    config.override(Setting.BACKUP_PASSWORD, '!secret bad_key')
    with pytest.raises(BackupPasswordKeyInvalid):
        Password(config).resolve()

    config.override(Setting.SECRETS_FILE_PATH, "/bad/file/path")
    config.override(Setting.BACKUP_PASSWORD, '!secret for_unit_tests')
    with pytest.raises(BackupPasswordKeyInvalid):
        Password(ha.config).resolve()


@pytest.mark.asyncio
async def test_backup_name(time: FakeTime, ha):
    time.setNow(time.local(1985, 12, 6, 15, 8, 9, 10))
    await assertName(ha, time.now(), "{type}", "Full")
    await assertName(ha, time.now(), "{year}", "1985")
    await assertName(ha, time.now(), "{year_short}", "85")
    await assertName(ha, time.now(), "{weekday}", "Friday")
    await assertName(ha, time.now(), "{weekday_short}", "Fri")
    await assertName(ha, time.now(), "{month}", "12")
    await assertName(ha, time.now(), "{month_long}", "December")
    await assertName(ha, time.now(), "{month_short}", "Dec")
    await assertName(ha, time.now(), "{ms}", "000010")
    await assertName(ha, time.now(), "{day}", "06")
    await assertName(ha, time.now(), "{hr24}", "15")
    await assertName(ha, time.now(), "{hr12}", "03")
    await assertName(ha, time.now(), "{min}", "08")
    await assertName(ha, time.now(), "{sec}", "09")
    await assertName(ha, time.now(), "{ampm}", "PM")
    await assertName(ha, time.now(), "{version_ha}", "ha version")
    await assertName(ha, time.now(), "{version_hassos}", "hassos version")
    await assertName(ha, time.now(), "{version_super}", "super version")
    await assertName(ha, time.now(), "{date}", "12/06/85")
    await assertName(ha, time.now(), "{time}", "15:08:09")
    await assertName(ha, time.now(), "{datetime}", "Fri Dec  6 15:08:09 1985")
    await assertName(ha, time.now(), "{isotime}", "1985-12-06T15:08:09.000010-05:00")


async def assertName(ha: HaSource, time, template: str, expected: str):
    backup: HABackup = await ha.create(CreateOptions(time, template))
    assert backup.name() == expected


@pytest.mark.asyncio
async def test_default_name(time: FakeTime, ha, server):
    backup = await ha.create(CreateOptions(time.now(), ""))
    assert backup.name() == "Full Backup 1985-12-06 00:00:00"


@pytest.mark.asyncio
async def test_pending_backup_timeout(time: FakeTime, ha: HaSource, config: Config, interceptor: RequestInterceptor, supervisor: SimulatedSupervisor):
    config.override(Setting.NEW_BACKUP_TIMEOUT_SECONDS, 1)
    config.override(Setting.FAILED_BACKUP_TIMEOUT_SECONDS, 1)
    config.override(Setting.PENDING_BACKUP_TIMEOUT_SECONDS, 1)

    async with supervisor._backup_inner_lock:
        backup_immediate: PendingBackup = await ha.create(CreateOptions(time.now(), "Test Name"))
        assert isinstance(backup_immediate, PendingBackup)
        assert backup_immediate.name() == "Test Name"
        assert not await ha.check()
        assert ha.pending_backup is backup_immediate

        await asyncio.wait({ha._pending_backup_task})
        assert ha.pending_backup is backup_immediate
        assert await ha.check()
        assert not await ha.check()

        time.advance(minutes=1)
        assert await ha.check()
        assert len(await ha.get()) == 0
        assert not await ha.check()
        assert ha.pending_backup is None
        assert backup_immediate.isStale()


@pytest.mark.asyncio
async def test_pending_backup_timeout_external(time, config, ha: HaSource, supervisor: SimulatedSupervisor):
    # now configure a snapshto to start outside of the addon
    config.override(Setting.NEW_BACKUP_TIMEOUT_SECONDS, 100)
    await supervisor.toggleBlockBackup()
    with pytest.raises(BackupInProgress):
        await ha.create(CreateOptions(time.now(), "Ignored"))
    backup_immediate = (await ha.get())['pending']
    await supervisor.toggleBlockBackup()
    assert isinstance(backup_immediate, PendingBackup)
    assert backup_immediate.name() == "Pending Backup"
    assert await ha.check()
    assert not await ha.check()
    assert ha.pending_backup is backup_immediate

    # should clean up after a day, since we're still waiting on the backup thread.
    time.advanceDay()
    assert await ha.check()
    assert len(await ha.get()) == 0


@pytest.mark.asyncio
async def test_pending_backup_replaces_original(time, ha: HaSource, config: Config, supervisor: SimulatedSupervisor):
    # now configure a snapshto to start outside of the addon
    config.override(Setting.NEW_BACKUP_TIMEOUT_SECONDS, 100)
    await supervisor.toggleBlockBackup()
    with pytest.raises(BackupInProgress):
        await ha.create(CreateOptions(time.now(), "Ignored"))
    backup_immediate = (await ha.get())['pending']
    await supervisor.toggleBlockBackup()
    assert isinstance(backup_immediate, PendingBackup)
    assert backup_immediate.name() == "Pending Backup"
    assert await ha.check()
    assert ha.pending_backup is backup_immediate
    assert await ha.get() == {backup_immediate.slug(): backup_immediate}

    # create a new backup behind the scenes, the pending backup should get replaced with the new one
    slug = (await ha.harequests.createBackup({'name': "Suddenly Appears", "hardlock": True}))['slug']
    results = await ha.get()
    assert len(results) == 1
    assert slug in results
    assert results[slug].name() == "Suddenly Appears"
    assert not results[slug].retained()


def test_retryable_errors():
    # SOMEDAY: retryable errors should be retried in the future
    pass


@pytest.mark.asyncio
async def test_retained_on_finish(ha: HaSource, server, time, config: Config, supervisor: SimulatedSupervisor):
    async with supervisor._backup_inner_lock:
        retention = {ha.name(): True}
        config.override(Setting.NEW_BACKUP_TIMEOUT_SECONDS, 0.0001)
        pending = await ha.create(CreateOptions(time.now(), "Test Name", retention))
        results = await ha.get()
        assert pending.name() == "Test Name"
        assert results == {pending.slug(): pending}
        assert type(pending) == PendingBackup
        assert not ha._pending_backup_task.done()

    await asyncio.wait({ha._pending_backup_task})
    results = list((await ha.get()).values())
    assert len(results) == 1
    assert results[0].name() == "Test Name"
    assert type(results[0]) == HABackup
    assert results[0].retained()
    assert config.isRetained(results[0].slug())


@pytest.mark.asyncio
async def test_upload(time, ha, server, uploader):
    data = await uploader.upload(createBackupTar("slug", "Test Name", time.now(), 1024 * 1024))
    dummy = DummyBackup("Test Name", time.now(), "src", "slug", "dummy")
    backup: HABackup = await ha.save(dummy, data)
    assert backup.name() == "Test Name"
    assert backup.slug() == "slug"
    assert backup.size() == round(data.size() / 1024.0 / 1024.0, 2) * 1024 * 1024
    assert backup.retained()
    # ensure its still retained on a refresh
    assert list((await ha.get()).values())[0].retained()


@pytest.mark.asyncio
async def test_corrupt_upload(time, ha, server, uploader):
    # verify a corrupt backup throws the right exception
    bad_data = await uploader.upload(getTestStream(100))
    dummy = DummyBackup("Test Name", time.now(), "src", "slug2", "dummy")

    with pytest.raises(UploadFailed):
        await ha.save(dummy, bad_data)


@pytest.mark.asyncio
async def test_upload_wrong_slug(time, ha, server, uploader):
    # verify a backup with the wrong slug also throws
    bad_data = await uploader.upload(createBackupTar("wrongslug", "Test Name", time.now(), 1024 * 1024))
    dummy = DummyBackup("Test Name", time.now(), "src", "slug", "dummy")
    with pytest.raises(UploadFailed):
        await ha.save(dummy, bad_data)


@pytest.mark.asyncio
async def test_failed_backup(time, ha: HaSource, supervisor: SimulatedSupervisor, config: Config, interceptor: RequestInterceptor):
    # create a blocking backup
    interceptor.setError(URL_MATCH_BACKUP_FULL, 524)
    config.override(Setting.NEW_BACKUP_TIMEOUT_SECONDS, 0)
    await supervisor.toggleBlockBackup()
    backup_immediate = await ha.create(CreateOptions(time.now(), "Some Name"))
    assert isinstance(backup_immediate, PendingBackup)
    assert backup_immediate.name() == "Some Name"
    assert not await ha.check()
    assert not backup_immediate.isFailed()
    await supervisor.toggleBlockBackup()

    # let the backup attempt to complete
    await asyncio.wait({ha._pending_backup_task})

    # verify it failed with the expected http error
    assert backup_immediate.isFailed()
    assert backup_immediate._exception.status == 524

    backups = list((await ha.get()).values())
    assert len(backups) == 1
    assert backups[0] is backup_immediate

    # verify we can create a new backup immediately
    interceptor.clear()
    await ha.create(CreateOptions(time.now(), "Some Name"))
    assert len(await ha.get()) == 1


@pytest.mark.asyncio
async def test_failed_backup_retry(ha: HaSource, time: FakeTime, config: Config, supervisor: SimulatedSupervisor, interceptor: RequestInterceptor):
    # create a blocking backup
    interceptor.setError(URL_MATCH_BACKUP_FULL, 524)
    config.override(Setting.NEW_BACKUP_TIMEOUT_SECONDS, 0)
    await supervisor.toggleBlockBackup()
    backup_immediate = await ha.create(CreateOptions(time.now(), "Some Name"))
    assert isinstance(backup_immediate, PendingBackup)
    assert backup_immediate.name() == "Some Name"
    assert not await ha.check()
    assert not backup_immediate.isFailed()
    await supervisor.toggleBlockBackup()

    # let the backup attempt to complete
    await asyncio.wait({ha._pending_backup_task})

    # verify it failed with the expected http error
    assert backup_immediate.isFailed()
    assert backup_immediate._exception.status == 524

    assert await ha.check()
    assert not await ha.check()
    time.advance(seconds=config.get(Setting.FAILED_BACKUP_TIMEOUT_SECONDS))

    # should trigger a sync after the failed backup timeout
    assert await ha.check()
    await ha.get()
    assert not await ha.check()


@pytest.mark.asyncio
async def test_immediate_backup_failure(time: FakeTime, ha: HaSource, config: Config, interceptor: RequestInterceptor):
    interceptor.setError(URL_MATCH_BACKUP_FULL, 524)
    with pytest.raises(ClientResponseError) as thrown:
        await ha.create(CreateOptions(time.now(), "Some Name"))
    assert thrown.value.status == 524

    assert ha.pending_backup is not None
    backups = list((await ha.get()).values())
    assert len(backups) == 1
    assert backups[0].isFailed()

    # Failed backup should go away after it times out
    assert await ha.check()
    assert not await ha.check()
    time.advance(seconds=config.get(
        Setting.FAILED_BACKUP_TIMEOUT_SECONDS) + 1)
    assert await ha.check()

    assert len(await ha.get()) == 0
    assert not await ha.check()


@pytest.mark.asyncio
async def test_delete_error(time, ha: HaSource, interceptor: RequestInterceptor):
    backup = await ha.create(CreateOptions(time.now(), "Some Name"))
    full = DummyBackup(backup.name(), backup.date(),
                       backup.size(), backup.slug(), "dummy")
    full.addSource(backup)
    interceptor.setError(URL_MATCH_BACKUP_DELETE, 400)
    with pytest.raises(HomeAssistantDeleteError):
        await ha.delete(full)

    interceptor.clear()
    await ha.delete(full)


@pytest.mark.asyncio
async def test_hostname(time, ha: HaSource, server, global_info: GlobalInfo):
    await ha.init()
    assert global_info.url == "/hassio/ingress/self_slug"


@pytest.mark.asyncio
async def test_supervisor_error(time, ha: HaSource, server: SimulationServer, global_info: GlobalInfo):
    await server.stop()
    with pytest.raises(SupervisorConnectionError):
        await ha.init()


@pytest.mark.asyncio
async def test_supervisor_permission_error(time, ha: HaSource, interceptor: RequestInterceptor, global_info: GlobalInfo):
    interceptor.setError(URL_MATCH_MISC_INFO, 403)
    with pytest.raises(SupervisorPermissionError):
        await ha.init()

    interceptor.clear()
    interceptor.setError(URL_MATCH_MISC_INFO, 404)
    with pytest.raises(ClientResponseError):
        await ha.init()


@pytest.mark.asyncio
async def test_download_timeout(ha: HaSource, time, interceptor: RequestInterceptor, config: Config) -> None:
    config.override(Setting.NEW_BACKUP_TIMEOUT_SECONDS, 100)
    backup: HABackup = await ha.create(CreateOptions(time.now(), "Test Name"))
    from_ha = await ha.harequests.backup(backup.slug())
    full = DummyBackup(from_ha.name(), from_ha.date(),
                       from_ha.size(), from_ha.slug(), "dummy")
    full.addSource(backup)

    interceptor.setSleep(URL_MATCH_BACKUP_DOWNLOAD, sleep=100)
    config.override(Setting.DOWNLOAD_TIMEOUT_SECONDS, 1)
    direct_download = await ha.harequests.download(backup.slug())

    with pytest.raises(SupervisorTimeoutError):
        await direct_download.setup()
        await direct_download.read(1)


@pytest.mark.asyncio
async def test_start_and_stop_addon(ha: HaSource, time, interceptor: RequestInterceptor, config: Config, supervisor: SimulatedSupervisor, addon_stopper: AddonStopper) -> None:
    addon_stopper.allowRun()
    slug = "test_slug"
    supervisor.installAddon(slug, "Test decription")
    config.override(Setting.STOP_ADDONS, slug)
    config.override(Setting.NEW_BACKUP_TIMEOUT_SECONDS, 0.001)

    assert supervisor.addon(slug)["state"] == "started"
    async with supervisor._backup_inner_lock:
        await ha.create(CreateOptions(time.now(), "Test Name"))
        assert supervisor.addon(slug)["state"] == "stopped"
    await ha._pending_backup_task
    assert supervisor.addon(slug)["state"] == "started"


@pytest.mark.asyncio
async def test_start_and_stop_two_addons(ha: HaSource, time, interceptor: RequestInterceptor, config: Config, supervisor: SimulatedSupervisor, addon_stopper: AddonStopper) -> None:
    addon_stopper.allowRun()
    slug1 = "test_slug_1"
    supervisor.installAddon(slug1, "Test decription")

    slug2 = "test_slug_2"
    supervisor.installAddon(slug2, "Test decription")
    config.override(Setting.STOP_ADDONS, ",".join([slug1, slug2]))
    config.override(Setting.NEW_BACKUP_TIMEOUT_SECONDS, 0.001)

    assert supervisor.addon(slug1)["state"] == "started"
    assert supervisor.addon(slug2)["state"] == "started"
    async with supervisor._backup_inner_lock:
        await ha.create(CreateOptions(time.now(), "Test Name"))
        assert supervisor.addon(slug1)["state"] == "stopped"
        assert supervisor.addon(slug2)["state"] == "stopped"
    await ha._pending_backup_task
    assert supervisor.addon(slug1)["state"] == "started"
    assert supervisor.addon(slug2)["state"] == "started"


@pytest.mark.asyncio
async def test_stop_addon_failure(ha: HaSource, time, interceptor: RequestInterceptor, config: Config, supervisor: SimulatedSupervisor, addon_stopper: AddonStopper) -> None:
    addon_stopper.allowRun()
    slug = "test_slug"
    supervisor.installAddon(slug, "Test decription")
    config.override(Setting.STOP_ADDONS, slug)
    config.override(Setting.NEW_BACKUP_TIMEOUT_SECONDS, 0.001)
    interceptor.setError(URL_MATCH_STOP_ADDON, 400)

    assert supervisor.addon(slug)["state"] == "started"
    async with supervisor._backup_inner_lock:
        await ha.create(CreateOptions(time.now(), "Test Name"))
        assert supervisor.addon(slug)["state"] == "started"
    await ha._pending_backup_task
    assert supervisor.addon(slug)["state"] == "started"
    assert len(await ha.get()) == 1


@pytest.mark.asyncio
async def test_start_addon_failure(ha: HaSource, time, interceptor: RequestInterceptor, config: Config, supervisor: SimulatedSupervisor, addon_stopper: AddonStopper) -> None:
    addon_stopper.allowRun()
    slug = "test_slug"
    supervisor.installAddon(slug, "Test decription")
    config.override(Setting.STOP_ADDONS, slug)
    config.override(Setting.NEW_BACKUP_TIMEOUT_SECONDS, 0.001)
    interceptor.setError(URL_MATCH_START_ADDON, 400)

    assert supervisor.addon(slug)["state"] == "started"
    async with supervisor._backup_inner_lock:
        await ha.create(CreateOptions(time.now(), "Test Name"))
        assert supervisor.addon(slug)["state"] == "stopped"
    await ha._pending_backup_task
    assert supervisor.addon(slug)["state"] == "stopped"
    assert len(await ha.get()) == 1


@pytest.mark.asyncio
async def test_ingore_self_when_stopping(ha: HaSource, time, interceptor: RequestInterceptor, config: Config, supervisor: SimulatedSupervisor, addon_stopper: AddonStopper) -> None:
    addon_stopper.allowRun()
    slug = supervisor._addon_slug
    config.override(Setting.STOP_ADDONS, slug)
    config.override(Setting.NEW_BACKUP_TIMEOUT_SECONDS, 0.001)
    interceptor.setError(URL_MATCH_START_ADDON, 400)

    assert supervisor.addon(slug)["state"] == "started"
    async with supervisor._backup_inner_lock:
        await ha.create(CreateOptions(time.now(), "Test Name"))
        assert supervisor.addon(slug)["state"] == "started"
    await ha._pending_backup_task
    assert supervisor.addon(slug)["state"] == "started"
    assert not interceptor.urlWasCalled(URL_MATCH_START_ADDON)
    assert not interceptor.urlWasCalled(URL_MATCH_STOP_ADDON)
    assert len(await ha.get()) == 1


@pytest.mark.asyncio
async def test_dont_purge_pending_backup(ha: HaSource, time, config: Config, supervisor: SimulatedSupervisor, model: Model, interceptor):
    config.override(Setting.MAX_BACKUPS_IN_HA, 4)
    await ha.create(CreateOptions(time.now(), "Test Name 1"))
    await ha.create(CreateOptions(time.now(), "Test Name 2"))
    await ha.create(CreateOptions(time.now(), "Test Name 3"))
    await ha.create(CreateOptions(time.now(), "Test Name 4"))
    await model.sync(time.now())

    config.override(Setting.NEW_BACKUP_TIMEOUT_SECONDS, 0.1)
    interceptor.setSleep(URL_MATCH_BACKUP_FULL, sleep=2)
    await ha.create(CreateOptions(time.now(), "Test Name"))
    backups = list((await ha.get()).values())
    assert len(backups) == 5
    backup = backups[4]
    assert isinstance(backup, PendingBackup)

    # no backup should get purged yet because the ending backup isn't considered for purging.
    await model.sync(time.now())
    backups = list((await ha.get()).values())
    assert len(backups) == 5

    # Wait for the backup to finish, then verify one gets purged.
    await ha._pending_backup_task
    await model.sync(time.now())
    backups = list((await ha.get()).values())
    assert len(backups) == 4


@pytest.mark.asyncio
async def test_matching_pending_backup(ha: HaSource, time: Time, config: Config, supervisor: SimulatedSupervisor, model: Model, interceptor, data_cache: DataCache):
    '''
    A pending backups with the same name and within a day of the backup time should be considered
    made by the addon
    '''
    data_cache.backup("pending")[KEY_NAME] = "Test Backup"
    data_cache.backup("pending")[KEY_CREATED] = time.now().isoformat()
    data_cache.backup("pending")[KEY_LAST_SEEN] = time.now().isoformat()

    await supervisor.createBackup({"name": "Test Backup"}, date=time.now() - timedelta(hours=12))

    backups = await ha.get()
    assert len(backups) == 1
    backup = next(iter(backups.values()))
    assert backup.madeByTheAddon()


@pytest.mark.asyncio
async def test_date_match_wrong_pending_backup(ha: HaSource, time: Time, config: Config, supervisor: SimulatedSupervisor, model: Model, interceptor, data_cache: DataCache):
    '''
    A pending backups with the same name but with the wrong date shoudl nto be considered made by the addon
    '''
    data_cache.backup("pending")[KEY_NAME] = "Test Backup"
    data_cache.backup("pending")[KEY_CREATED] = time.now().isoformat()
    data_cache.backup("pending")[KEY_LAST_SEEN] = time.now().isoformat()

    await supervisor.createBackup({"name": "Test Backup"}, date=time.now() - timedelta(hours=25))

    backups = await ha.get()
    assert len(backups) == 1
    backups = next(iter(backups.values()))
    assert not backups.madeByTheAddon()


@pytest.mark.asyncio
async def test_name_wrong_match_pending_backup(ha: HaSource, time: Time, config: Config, supervisor: SimulatedSupervisor, model: Model, interceptor, data_cache: DataCache):
    '''
    A pending backups with the wrong name shoudl not be considered made by the addon
    '''
    data_cache.backup("pending")[KEY_NAME] = "Test Backup"
    data_cache.backup("pending")[KEY_CREATED] = time.now().isoformat()
    data_cache.backup("pending")[KEY_LAST_SEEN] = time.now().isoformat()

    await supervisor.createBackup({"name": "Wrong Name"}, date=time.now() - timedelta(hours=12))

    backups = await ha.get()
    assert len(backups) == 1
    backup = next(iter(backups.values()))
    assert not backup.madeByTheAddon()


@pytest.mark.asyncio
async def test_bump_last_seen(ha: HaSource, time: Time, config: Config, supervisor: SimulatedSupervisor, model: Model, interceptor, data_cache: DataCache):
    backup = await ha.create(CreateOptions(time.now(), "Test Name"))
    time.advance(days=1)
    assert backup.slug() in await ha.get()
    assert data_cache.backup(backup.slug())[KEY_LAST_SEEN] == time.now().isoformat()

    time.advance(days=1)
    assert backup.slug() in await ha.get()
    assert data_cache.backup(backup.slug())[KEY_LAST_SEEN] == time.now().isoformat()


@pytest.mark.asyncio
async def test_backup_supervisor_path(ha: HaSource, supervisor: SimulatedSupervisor, interceptor: RequestInterceptor):
    supervisor._super_version = Version(2021, 8)
    await ha.get()
    assert interceptor.urlWasCalled(URL_MATCH_BACKUPS)
    assert not interceptor.urlWasCalled(URL_MATCH_SNAPSHOT)


@pytest.mark.asyncio
async def test_backup_supervisor_path_old_version(ha: HaSource, supervisor: SimulatedSupervisor, interceptor: RequestInterceptor):
    supervisor._super_version = Version(2021, 7)
    await ha.get()
    assert not interceptor.urlWasCalled(URL_MATCH_BACKUPS)
    assert interceptor.urlWasCalled(URL_MATCH_SNAPSHOT)


@pytest.mark.asyncio
async def test_supervisor_host(ha: HaSource, supervisor: SimulatedSupervisor, interceptor: RequestInterceptor, config: Config, server_url):
    assert ha.harequests.getSupervisorURL() == server_url

    config.override(Setting.SUPERVISOR_URL, "")
    assert ha.harequests.getSupervisorURL() == URL("http://hassio")

    os.environ['SUPERVISOR_TOKEN'] = "test"
    assert ha.harequests.getSupervisorURL() == URL("http://supervisor")


@pytest.mark.asyncio
async def test_upgrade_default_config(ha: HaSource, supervisor: SimulatedSupervisor, interceptor: RequestInterceptor, config: Config, server_url):
    """Verify that converting the original default config optiosn works as expected"""

    # overwrite the addon options with old values
    supervisor._options = {
        Setting.DEPRECTAED_MAX_BACKUPS_IN_HA.value: 4,
        Setting.DEPRECTAED_MAX_BACKUPS_IN_GOOGLE_DRIVE.value: 4,
        Setting.DEPRECATED_DAYS_BETWEEN_BACKUPS.value: 3,
        Setting.USE_SSL.value: False,
    }

    await ha.init()

    assert not config.mustSaveUpgradeChanges()
    assert interceptor.urlWasCalled(URL_MATCH_SELF_OPTIONS)

    # Verify the config was upgraded
    assert supervisor._options == {
        Setting.MAX_BACKUPS_IN_HA.value: 4,
        Setting.MAX_BACKUPS_IN_GOOGLE_DRIVE.value: 4,
        Setting.DAYS_BETWEEN_BACKUPS.value: 3,
        Setting.CALL_BACKUP_SNAPSHOT.value: True,
    }


@pytest.mark.asyncio
async def test_upgrade_all_config(ha: HaSource, supervisor: SimulatedSupervisor, interceptor: RequestInterceptor, config: Config, server_url):
    """Verify that converting all upgradeable config optiosn works as expected"""

    # overwrite the addon options with old values
    supervisor._options = {
        Setting.DEPRECTAED_MAX_BACKUPS_IN_HA.value: 1,
        Setting.DEPRECTAED_MAX_BACKUPS_IN_GOOGLE_DRIVE.value: 2,
        Setting.DEPRECATED_DAYS_BETWEEN_BACKUPS.value: 5,
        Setting.DEPRECTAED_IGNORE_OTHER_BACKUPS.value: True,
        Setting.DEPRECTAED_IGNORE_UPGRADE_BACKUPS.value: True,
        Setting.DEPRECTAED_BACKUP_TIME_OF_DAY.value: "01:11",
        Setting.DEPRECTAED_DELETE_BEFORE_NEW_BACKUP.value: True,
        Setting.DEPRECTAED_BACKUP_NAME.value: "test",
        Setting.DEPRECTAED_SPECIFY_BACKUP_FOLDER.value: True,
        Setting.DEPRECTAED_NOTIFY_FOR_STALE_BACKUPS.value: False,
        Setting.DEPRECTAED_ENABLE_BACKUP_STALE_SENSOR.value: False,
        Setting.DEPRECTAED_ENABLE_BACKUP_STATE_SENSOR.value: False,
        Setting.DEPRECATED_BACKUP_PASSWORD.value: "test password",
    }

    await ha.init()
    assert not config.mustSaveUpgradeChanges()
    assert interceptor.urlWasCalled(URL_MATCH_SELF_OPTIONS)

    # Verify the config was upgraded
    assert supervisor._options == {
        Setting.MAX_BACKUPS_IN_HA.value: 1,
        Setting.MAX_BACKUPS_IN_GOOGLE_DRIVE.value: 2,
        Setting.DAYS_BETWEEN_BACKUPS.value: 5,
        Setting.IGNORE_OTHER_BACKUPS.value: True,
        Setting.IGNORE_UPGRADE_BACKUPS.value: True,
        Setting.BACKUP_TIME_OF_DAY.value: "01:11",
        Setting.DELETE_BEFORE_NEW_BACKUP.value: True,
        Setting.BACKUP_NAME.value: "test",
        Setting.SPECIFY_BACKUP_FOLDER.value: True,
        Setting.NOTIFY_FOR_STALE_BACKUPS.value: False,
        Setting.ENABLE_BACKUP_STALE_SENSOR.value: False,
        Setting.ENABLE_BACKUP_STATE_SENSOR.value: False,
        Setting.BACKUP_PASSWORD.value: "test password",
        Setting.CALL_BACKUP_SNAPSHOT.value: True,
    }

    interceptor.clear()

    await ha.init()
    assert not interceptor.urlWasCalled(URL_MATCH_SELF_OPTIONS)


@pytest.mark.asyncio
async def test_upgrade_some_config(ha: HaSource, supervisor: SimulatedSupervisor, interceptor: RequestInterceptor, config: Config, server_url):
    """Verify that converting a mix of upgradeable and not upgradeable config works"""

    # overwrite the addon options with old values
    supervisor._options = {
        Setting.DEPRECTAED_MAX_BACKUPS_IN_HA.value: 4,
        Setting.DEPRECTAED_MAX_BACKUPS_IN_GOOGLE_DRIVE.value: 4,
        Setting.DEPRECATED_DAYS_BETWEEN_BACKUPS.value: 3,
        Setting.DEPRECTAED_BACKUP_TIME_OF_DAY.value: "01:11",
        Setting.EXCLUDE_ADDONS.value: "test",
        Setting.USE_SSL.value: False,
    }

    await ha.init()

    assert not config.mustSaveUpgradeChanges()
    assert interceptor.urlWasCalled(URL_MATCH_SELF_OPTIONS)

    # Verify the config was upgraded
    assert supervisor._options == {
        Setting.MAX_BACKUPS_IN_HA.value: 4,
        Setting.MAX_BACKUPS_IN_GOOGLE_DRIVE.value: 4,
        Setting.DAYS_BETWEEN_BACKUPS.value: 3,
        Setting.EXCLUDE_ADDONS.value: "test",
        Setting.BACKUP_TIME_OF_DAY.value: "01:11",
        Setting.CALL_BACKUP_SNAPSHOT.value: True,
    }


@pytest.mark.asyncio
async def test_upgrade_no_config(ha: HaSource, supervisor: SimulatedSupervisor, interceptor: RequestInterceptor, config: Config, server_url):
    """Verifies that config not in need of an upgrade doesn't get upgraded"""

    # overwrite the addon options with old values
    supervisor._options = {
        Setting.MAX_BACKUPS_IN_HA.value: 4,
        Setting.MAX_BACKUPS_IN_GOOGLE_DRIVE.value: 4,
        Setting.DAYS_BETWEEN_BACKUPS.value: 3,
        Setting.BACKUP_TIME_OF_DAY.value: "01:11",
        Setting.EXCLUDE_ADDONS.value: "test"
    }

    await ha.init()

    assert not config.mustSaveUpgradeChanges()
    assert not interceptor.urlWasCalled(URL_MATCH_SELF_OPTIONS)

    # Verify the config was upgraded
    assert supervisor._options == {
        Setting.MAX_BACKUPS_IN_HA.value: 4,
        Setting.MAX_BACKUPS_IN_GOOGLE_DRIVE.value: 4,
        Setting.DAYS_BETWEEN_BACKUPS.value: 3,
        Setting.BACKUP_TIME_OF_DAY.value: "01:11",
        Setting.EXCLUDE_ADDONS.value: "test",
    }


@pytest.mark.asyncio
async def test_old_delete_path(ha: HaSource, supervisor: SimulatedSupervisor, interceptor: RequestInterceptor, time: FakeTime):
    supervisor._super_version = Version(2020, 8)
    await ha.get()
    backup: HABackup = await ha.create(CreateOptions(time.now(), "Test Name"))
    full = DummyBackup(backup.name(), backup.date(),
                       backup.size(), backup.slug(), "dummy")
    full.addSource(backup)
    await ha.delete(full)
    assert interceptor.urlWasCalled("/snapshots/{0}/remove".format(backup.slug()))


@pytest.mark.asyncio
async def test_ignore_upgrade_backup_ha_config(ha: HaSource, time: Time, supervisor: SimulatedSupervisor, interceptor: RequestInterceptor, config: Config, data_cache: DataCache):
    config.override(Setting.IGNORE_UPGRADE_BACKUPS, True)
    slug = (await ha.harequests.createBackup({'name': "Suddenly Appears", 'folders': ['homeassistant'], 'addons': []}))['slug']

    backups = await ha.get()
    assert len(backups) == 1
    assert slug in backups
    assert backups[slug].ignore()


@pytest.mark.asyncio
async def test_ignore_upgrade_backup_single_folder(ha: HaSource, time: Time, supervisor: SimulatedSupervisor, interceptor: RequestInterceptor, config: Config, data_cache: DataCache):
    config.override(Setting.IGNORE_UPGRADE_BACKUPS, True)
    slug = (await ha.harequests.createBackup({'name': "Suddenly Appears", 'folders': ['share'], 'addons': []}))['slug']

    backups = await ha.get()
    assert len(backups) == 1
    assert slug in backups
    assert backups[slug].ignore()


@pytest.mark.asyncio
async def test_ignore_upgrade_backup_single_addon(ha: HaSource, time: Time, supervisor: SimulatedSupervisor, interceptor: RequestInterceptor, config: Config, data_cache: DataCache):
    config.override(Setting.IGNORE_UPGRADE_BACKUPS, True)
    slug = (await ha.harequests.createBackup({'name': "Suddenly Appears", 'folders': [], 'addons': ["particla_accel"]}))['slug']

    backups = await ha.get()
    assert len(backups) == 1
    assert slug in backups
    assert backups[slug].ignore()


@pytest.mark.asyncio
async def test_ignore_upgrade_backup_two_folders(ha: HaSource, time: Time, supervisor: SimulatedSupervisor, interceptor: RequestInterceptor, config: Config, data_cache: DataCache):
    config.override(Setting.IGNORE_UPGRADE_BACKUPS, True)
    slug = (await ha.harequests.createBackup({'name': "Suddenly Appears", 'folders': ['homeassistant', "share"], 'addons': []}))['slug']

    backups = await ha.get()
    assert len(backups) == 1
    assert slug in backups
    assert not backups[slug].ignore()


@pytest.mark.asyncio
async def test_ignore_upgrade_backup_empty(ha: HaSource, time: Time, supervisor: SimulatedSupervisor, interceptor: RequestInterceptor, config: Config, data_cache: DataCache):
    config.override(Setting.IGNORE_UPGRADE_BACKUPS, True)
    slug = (await ha.harequests.createBackup({'name': "Suddenly Appears", 'folders': [], 'addons': []}))['slug']

    backups = await ha.get()
    assert len(backups) == 1
    assert slug in backups
    assert not backups[slug].ignore()


@pytest.mark.asyncio
async def test_very_long_running_backup(time, config, ha: HaSource, supervisor: SimulatedSupervisor):
    config.override(Setting.NEW_BACKUP_TIMEOUT_SECONDS, 1)
    config.override(Setting.PENDING_BACKUP_TIMEOUT_SECONDS, 2)
    config.override(Setting.IGNORE_OTHER_BACKUPS, True)

    async with supervisor._backup_inner_lock:
        await ha.create(CreateOptions(time.now(), "Actually gets made"))

        for _ in range(4):
            time.advance(hours=1)
            assert "pending" in (await ha.get())

        # Let the task fail
        await ha._pending_backup_task

        # after 4 hours the pending backup should be assumed to have failed and cleaned up
        time.advance(hours=1)
        assert len(await ha.get()) == 0

        for _ in range(4):
            time.advance(hours=1)
            # Making a backup should keep failing
            with pytest.raises(BackupInProgress):
                await ha.create(CreateOptions(time.now(), "Ignored"))
            backups = await ha.get()
            assert len(backups) == 1
            assert "pending" in backups

    # Wait for the backup to complete
    async with supervisor._backup_lock:
        pass

    time.advance(hours=1)
    backups = await ha.get()
    assert len(backups) == 1
    assert "pending" not in backups
    assert list(backups.values())[0].madeByTheAddon()


@pytest.mark.asyncio
async def test_note(time, config, ha: HaSource, supervisor: SimulatedSupervisor):
    backup = await ha.create(CreateOptions(time.now(), "Backup"))
    assert isinstance(backup, HABackup)
    assert backup.note() is None

    full = DummyBackup(backup.name(), backup.date(),
                       backup.size(), backup.slug(), "dummy")
    full.addSource(backup)
    await ha.note(full, "new note")
    assert backup.note() == "new note"

    await ha.note(full, None)
    assert backup.note() is None


@pytest.mark.asyncio
async def test_note_creation(time, config, ha: HaSource, supervisor: SimulatedSupervisor):
    backup = await ha.create(CreateOptions(time.now(), "Backup", note="creation note"))
    assert isinstance(backup, HABackup)
    assert backup.note() == "creation note"
    assert (await ha.get())[backup.slug()].note() == "creation note"

    full = DummyBackup(backup.name(), backup.date(),
                       backup.size(), backup.slug(), "dummy")
    full.addSource(backup)
    await ha.note(full, "new note")
    assert backup.note() == "new note"
    assert (await ha.get())[full.slug()].note() == "new note"

    await ha.note(full, None)
    assert backup.note() is None


@pytest.mark.asyncio
async def test_note_long_backup(time, config, ha: HaSource, supervisor: SimulatedSupervisor):
    config.override(Setting.NEW_BACKUP_TIMEOUT_SECONDS, 1)

    async with supervisor._backup_inner_lock:
        backup = await ha.create(CreateOptions(time.now(), "Backup", note="Creation note"))
        assert isinstance(backup, PendingBackup)
        assert backup.note() == "Creation note"

        pending = (await ha.get())[backup.slug()]
        assert pending.note() == "Creation note"
    assert ha._pending_backup_task is not None
    await ha._pending_backup_task
    completed = next(iter((await ha.get()).values()))
    assert not isinstance(completed, PendingBackup)
    assert completed.note() == "Creation note"


@pytest.mark.asyncio
async def test_note_long_backup_changed_during_creation(time, config, ha: HaSource, supervisor: SimulatedSupervisor):
    config.override(Setting.NEW_BACKUP_TIMEOUT_SECONDS, 1)

    async with supervisor._backup_inner_lock:
        backup = await ha.create(CreateOptions(time.now(), "Backup", note="Creation note"))
        assert isinstance(backup, PendingBackup)
        assert backup.note() == "Creation note"

        pending = (await ha.get())[backup.slug()]
        assert pending.note() == "Creation note"

        full = DummyBackup(pending.name(), pending.date(), pending.size(), pending.slug(), "dummy")
        full.addSource(pending)
        await ha.note(full, "changed")

        still_pending = next(iter((await ha.get()).values()))
        assert isinstance(still_pending, PendingBackup)
        assert still_pending.note() == "changed"

    assert ha._pending_backup_task is not None
    await ha._pending_backup_task
    completed = next(iter((await ha.get()).values()))
    assert not isinstance(completed, PendingBackup)
    assert completed.note() == "changed"


@pytest.mark.asyncio
async def test_note_change_external_backup(time, config, ha: HaSource, supervisor: SimulatedSupervisor):
    config.override(Setting.NEW_BACKUP_TIMEOUT_SECONDS, 100)

    await supervisor.toggleBlockBackup()
    with pytest.raises(BackupInProgress):
        await ha.create(CreateOptions(time.now(), "Ignored", note="ignored"))
    pending = next(iter((await ha.get()).values()))
    assert isinstance(pending, PendingBackup)
    assert pending.note() is None

    full = DummyBackup(pending.name(), pending.date(), pending.size(), pending.slug(), "dummy")
    full.addSource(pending)
    await ha.note(full, "changed note")
    assert full.note() == "changed note"

    # create a new backup in the background, this should get the note of the pending backup.
    await supervisor.toggleBlockBackup()
    await supervisor.createBackup({"name": "Test Backup"}, date=time.now() - timedelta(hours=12))
    completed = next(iter((await ha.get()).values()))
    assert not isinstance(completed, PendingBackup)
    assert completed.note() == "changed note"


# Verify that if the supervisor is below the minimum version, we don't query for mount information and its populated with a reasonable default.
@pytest.mark.asyncio
async def test_mount_info_old_supervisor(time, config, ha: HaSource, supervisor: SimulatedSupervisor, interceptor: RequestInterceptor):
    supervisor._super_version = Version.parse("2023.5")
    await ha.refresh()
    assert not interceptor.urlWasCalled(URL_MATCH_MOUNT)
    assert len(ha.mount_info.get("mounts")) == 0


# Verify that if the supervisor is above the minimum version, we we do query for mount info and populate it
@pytest.mark.asyncio
async def test_mount_info_new_supervisor(time, config, ha: HaSource, supervisor: SimulatedSupervisor, interceptor: RequestInterceptor):
    supervisor._super_version = Version.parse("2023.6")
    await ha.refresh()
    assert interceptor.urlWasCalled(URL_MATCH_MOUNT)
    assert len(ha.mount_info.get("mounts")) > 0


# Verify that the default backup location is HA's configured default if the backup location is unspecified
# and the supervisor is above the minimum version
@pytest.mark.asyncio
async def test_default_backup_location_new_supervisor(time, config, ha: HaSource, supervisor: SimulatedSupervisor, interceptor: RequestInterceptor):
    await ha.refresh()
    req, _name, _protected = ha._buildBackupInfo(CreateOptions(time.now(), "Backup"))
    assert req.get("location") is None
    assert 'location' in req

    supervisor._mounts["default_backup_mount"] = "my_backup_share"

    await ha.refresh()
    req, _name, _protected = ha._buildBackupInfo(CreateOptions(time.now(), "Backup"))
    assert req.get("location") == "my_backup_share"


# Verify that having a backup storage location of "local-disk" always uses the default even if HA has another default configured
@pytest.mark.asyncio
async def test_default_backup_location_local_disk(time, config: Config, ha: HaSource, supervisor: SimulatedSupervisor, interceptor: RequestInterceptor):
    config.override(Setting.BACKUP_STORAGE, "local-disk")
    await ha.refresh()
    req, _name, _protected = ha._buildBackupInfo(CreateOptions(time.now(), "Backup"))
    assert req.get("location") is None
    assert 'location' in req

    supervisor._mounts["default_backup_mount"] = "my_backup_share"
    await ha.refresh()
    req, _name, _protected = ha._buildBackupInfo(CreateOptions(time.now(), "Backup"))
    assert req.get("location") is None
    assert 'location' in req


# Verify that using a non-active share results in an error before attempting to request the backup 
@pytest.mark.asyncio
async def test_inactive_backup_location(time, config: Config, ha: HaSource, supervisor: SimulatedSupervisor, interceptor: RequestInterceptor):
    config.override(Setting.BACKUP_STORAGE, "my_backup_share")
    supervisor._mounts["mounts"][1]["state"] = "starting"
    await ha.refresh()
    with pytest.raises(InactiveNetworkStorageError):
        ha._buildBackupInfo(CreateOptions(time.now(), "Backup"))


# Verify that using a non-existant share results in an error before attempting to request the backup 
@pytest.mark.asyncio
async def test_unknown_backup_location(time, config: Config, ha: HaSource, supervisor: SimulatedSupervisor, interceptor: RequestInterceptor):
    config.override(Setting.BACKUP_STORAGE, "doesn't_exists")
    await ha.refresh()
    with pytest.raises(UnknownNetworkStorageError):
        ha._buildBackupInfo(CreateOptions(time.now(), "Backup"))
