"""Tests for homekit_controller config flow."""
import json
from unittest import mock

import homekit
import pytest

from homeassistant.components.homekit_controller import config_flow
from homeassistant.components.homekit_controller.const import KNOWN_DEVICES
from tests.common import MockConfigEntry
from tests.components.homekit_controller.common import (
    Accessory,
    FakeService,
    setup_platform,
)


PAIRING_START_FORM_ERRORS = [
    (homekit.BusyError, "busy_error"),
    (homekit.MaxTriesError, "max_tries_error"),
    (KeyError, "pairing_failed"),
]

PAIRING_START_ABORT_ERRORS = [
    (homekit.AccessoryNotFoundError, "accessory_not_found_error"),
    (homekit.UnavailableError, "already_paired"),
]

PAIRING_FINISH_FORM_ERRORS = [
    (homekit.MaxPeersError, "max_peers_error"),
    (homekit.AuthenticationError, "authentication_error"),
    (homekit.UnknownError, "unknown_error"),
    (KeyError, "pairing_failed"),
]

PAIRING_FINISH_ABORT_ERRORS = [
    (homekit.AccessoryNotFoundError, "accessory_not_found_error")
]


def _setup_flow_handler(hass):
    flow = config_flow.HomekitControllerFlowHandler()
    flow.hass = hass
    flow.context = {}

    flow.controller = mock.Mock()
    flow.controller.pairings = {}

    return flow


async def _setup_flow_zeroconf(hass, discovery_info):
    result = await hass.config_entries.flow.async_init(
        "homekit_controller", context={"source": "zeroconf"}, data=discovery_info
    )
    return result


async def test_discovery_works(hass):
    """Test a device being discovered."""
    discovery_info = {
        "name": "TestDevice",
        "host": "127.0.0.1",
        "port": 8080,
        "properties": {"md": "TestDevice", "id": "00:00:00:00:00:00", "c#": 1, "sf": 1},
    }

    flow = _setup_flow_handler(hass)

    # Device is discovered
    result = await flow.async_step_zeroconf(discovery_info)
    assert result["type"] == "form"
    assert result["step_id"] == "pair"
    assert flow.context == {
        "hkid": "00:00:00:00:00:00",
        "title_placeholders": {"name": "TestDevice"},
    }

    # User initiates pairing - device enters pairing mode and displays code
    result = await flow.async_step_pair({})
    assert result["type"] == "form"
    assert result["step_id"] == "pair"
    assert flow.controller.start_pairing.call_count == 1

    pairing = mock.Mock(pairing_data={"AccessoryPairingID": "00:00:00:00:00:00"})

    pairing.list_accessories_and_characteristics.return_value = [
        {
            "aid": 1,
            "services": [
                {
                    "characteristics": [{"type": "23", "value": "Koogeek-LS1-20833F"}],
                    "type": "3e",
                }
            ],
        }
    ]

    # Pairing doesn't error error and pairing results
    flow.controller.pairings = {"00:00:00:00:00:00": pairing}
    result = await flow.async_step_pair({"pairing_code": "111-22-33"})
    assert result["type"] == "create_entry"
    assert result["title"] == "Koogeek-LS1-20833F"
    assert result["data"] == pairing.pairing_data


async def test_discovery_works_upper_case(hass):
    """Test a device being discovered."""
    discovery_info = {
        "name": "TestDevice",
        "host": "127.0.0.1",
        "port": 8080,
        "properties": {"MD": "TestDevice", "ID": "00:00:00:00:00:00", "C#": 1, "SF": 1},
    }

    flow = _setup_flow_handler(hass)

    # Device is discovered
    result = await flow.async_step_zeroconf(discovery_info)
    assert result["type"] == "form"
    assert result["step_id"] == "pair"
    assert flow.context == {
        "hkid": "00:00:00:00:00:00",
        "title_placeholders": {"name": "TestDevice"},
    }

    # User initiates pairing - device enters pairing mode and displays code
    result = await flow.async_step_pair({})
    assert result["type"] == "form"
    assert result["step_id"] == "pair"
    assert flow.controller.start_pairing.call_count == 1

    pairing = mock.Mock(pairing_data={"AccessoryPairingID": "00:00:00:00:00:00"})

    pairing.list_accessories_and_characteristics.return_value = [
        {
            "aid": 1,
            "services": [
                {
                    "characteristics": [{"type": "23", "value": "Koogeek-LS1-20833F"}],
                    "type": "3e",
                }
            ],
        }
    ]

    flow.controller.pairings = {"00:00:00:00:00:00": pairing}
    result = await flow.async_step_pair({"pairing_code": "111-22-33"})
    assert result["type"] == "create_entry"
    assert result["title"] == "Koogeek-LS1-20833F"
    assert result["data"] == pairing.pairing_data


async def test_discovery_works_missing_csharp(hass):
    """Test a device being discovered that has missing mdns attrs."""
    discovery_info = {
        "name": "TestDevice",
        "host": "127.0.0.1",
        "port": 8080,
        "properties": {"md": "TestDevice", "id": "00:00:00:00:00:00", "sf": 1},
    }

    flow = _setup_flow_handler(hass)

    # Device is discovered
    result = await flow.async_step_zeroconf(discovery_info)
    assert result["type"] == "form"
    assert result["step_id"] == "pair"
    assert flow.context == {
        "hkid": "00:00:00:00:00:00",
        "title_placeholders": {"name": "TestDevice"},
    }

    # User initiates pairing - device enters pairing mode and displays code
    result = await flow.async_step_pair({})
    assert result["type"] == "form"
    assert result["step_id"] == "pair"
    assert flow.controller.start_pairing.call_count == 1

    pairing = mock.Mock(pairing_data={"AccessoryPairingID": "00:00:00:00:00:00"})

    pairing.list_accessories_and_characteristics.return_value = [
        {
            "aid": 1,
            "services": [
                {
                    "characteristics": [{"type": "23", "value": "Koogeek-LS1-20833F"}],
                    "type": "3e",
                }
            ],
        }
    ]

    flow.controller.pairings = {"00:00:00:00:00:00": pairing}

    result = await flow.async_step_pair({"pairing_code": "111-22-33"})
    assert result["type"] == "create_entry"
    assert result["title"] == "Koogeek-LS1-20833F"
    assert result["data"] == pairing.pairing_data


async def test_abort_duplicate_flow(hass):
    """Already paired."""
    discovery_info = {
        "name": "TestDevice",
        "host": "127.0.0.1",
        "port": 8080,
        "properties": {"md": "TestDevice", "id": "00:00:00:00:00:00", "c#": 1, "sf": 1},
    }

    result = await _setup_flow_zeroconf(hass, discovery_info)
    assert result["type"] == "form"
    assert result["step_id"] == "pair"

    result = await _setup_flow_zeroconf(hass, discovery_info)
    assert result["type"] == "abort"
    assert result["reason"] == "already_in_progress"


async def test_pair_already_paired_1(hass):
    """Already paired."""
    discovery_info = {
        "name": "TestDevice",
        "host": "127.0.0.1",
        "port": 8080,
        "properties": {"md": "TestDevice", "id": "00:00:00:00:00:00", "c#": 1, "sf": 0},
    }

    flow = _setup_flow_handler(hass)

    result = await flow.async_step_zeroconf(discovery_info)
    assert result["type"] == "abort"
    assert result["reason"] == "already_paired"
    assert flow.context == {
        "hkid": "00:00:00:00:00:00",
        "title_placeholders": {"name": "TestDevice"},
    }


async def test_discovery_ignored_model(hass):
    """Already paired."""
    discovery_info = {
        "name": "TestDevice",
        "host": "127.0.0.1",
        "port": 8080,
        "properties": {
            "md": config_flow.HOMEKIT_IGNORE[0],
            "id": "00:00:00:00:00:00",
            "c#": 1,
            "sf": 1,
        },
    }

    flow = _setup_flow_handler(hass)

    result = await flow.async_step_zeroconf(discovery_info)
    assert result["type"] == "abort"
    assert result["reason"] == "ignored_model"
    assert flow.context == {
        "hkid": "00:00:00:00:00:00",
        "title_placeholders": {"name": "TestDevice"},
    }


async def test_discovery_invalid_config_entry(hass):
    """There is already a config entry for the pairing id but its invalid."""
    MockConfigEntry(
        domain="homekit_controller", data={"AccessoryPairingID": "00:00:00:00:00:00"}
    ).add_to_hass(hass)

    # We just added a mock config entry so it must be visible in hass
    assert len(hass.config_entries.async_entries()) == 1

    discovery_info = {
        "name": "TestDevice",
        "host": "127.0.0.1",
        "port": 8080,
        "properties": {"md": "TestDevice", "id": "00:00:00:00:00:00", "c#": 1, "sf": 1},
    }

    flow = _setup_flow_handler(hass)

    result = await flow.async_step_zeroconf(discovery_info)
    assert result["type"] == "form"
    assert result["step_id"] == "pair"
    assert flow.context == {
        "hkid": "00:00:00:00:00:00",
        "title_placeholders": {"name": "TestDevice"},
    }

    # Discovery of a HKID that is in a pairable state but for which there is
    # already a config entry - in that case the stale config entry is
    # automatically removed.
    config_entry_count = len(hass.config_entries.async_entries())
    assert config_entry_count == 0


async def test_discovery_already_configured(hass):
    """Already configured."""
    discovery_info = {
        "name": "TestDevice",
        "host": "127.0.0.1",
        "port": 8080,
        "properties": {"md": "TestDevice", "id": "00:00:00:00:00:00", "c#": 1, "sf": 0},
    }

    await setup_platform(hass)

    conn = mock.Mock()
    conn.config_num = 1
    hass.data[KNOWN_DEVICES]["00:00:00:00:00:00"] = conn

    flow = _setup_flow_handler(hass)

    result = await flow.async_step_zeroconf(discovery_info)
    assert result["type"] == "abort"
    assert result["reason"] == "already_configured"
    assert flow.context == {
        "hkid": "00:00:00:00:00:00",
        "title_placeholders": {"name": "TestDevice"},
    }

    assert conn.async_config_num_changed.call_count == 0


async def test_discovery_already_configured_config_change(hass):
    """Already configured."""
    discovery_info = {
        "name": "TestDevice",
        "host": "127.0.0.1",
        "port": 8080,
        "properties": {"md": "TestDevice", "id": "00:00:00:00:00:00", "c#": 2, "sf": 0},
    }

    await setup_platform(hass)

    conn = mock.Mock()
    conn.config_num = 1
    hass.data[KNOWN_DEVICES]["00:00:00:00:00:00"] = conn

    flow = _setup_flow_handler(hass)

    result = await flow.async_step_zeroconf(discovery_info)
    assert result["type"] == "abort"
    assert result["reason"] == "already_configured"
    assert flow.context == {
        "hkid": "00:00:00:00:00:00",
        "title_placeholders": {"name": "TestDevice"},
    }

    assert conn.async_refresh_entity_map.call_args == mock.call(2)


async def test_pair_unable_to_pair(hass):
    """Pairing completed without exception, but didn't create a pairing."""
    discovery_info = {
        "name": "TestDevice",
        "host": "127.0.0.1",
        "port": 8080,
        "properties": {"md": "TestDevice", "id": "00:00:00:00:00:00", "c#": 1, "sf": 1},
    }

    flow = _setup_flow_handler(hass)

    # Device is discovered
    result = await flow.async_step_zeroconf(discovery_info)
    assert result["type"] == "form"
    assert result["step_id"] == "pair"
    assert flow.context == {
        "hkid": "00:00:00:00:00:00",
        "title_placeholders": {"name": "TestDevice"},
    }

    # User initiates pairing - device enters pairing mode and displays code
    result = await flow.async_step_pair({})
    assert result["type"] == "form"
    assert result["step_id"] == "pair"
    assert flow.controller.start_pairing.call_count == 1

    # Pairing doesn't error but no pairing object is generated
    result = await flow.async_step_pair({"pairing_code": "111-22-33"})
    assert result["type"] == "form"
    assert result["errors"]["pairing_code"] == "unable_to_pair"


@pytest.mark.parametrize("exception,expected", PAIRING_START_ABORT_ERRORS)
async def test_pair_abort_errors_on_start(hass, exception, expected):
    """Test various pairing errors."""
    discovery_info = {
        "name": "TestDevice",
        "host": "127.0.0.1",
        "port": 8080,
        "properties": {"md": "TestDevice", "id": "00:00:00:00:00:00", "c#": 1, "sf": 1},
    }

    flow = _setup_flow_handler(hass)

    # Device is discovered
    result = await flow.async_step_zeroconf(discovery_info)
    assert result["type"] == "form"
    assert result["step_id"] == "pair"
    assert flow.context == {
        "hkid": "00:00:00:00:00:00",
        "title_placeholders": {"name": "TestDevice"},
    }

    # User initiates pairing - device refuses to enter pairing mode
    with mock.patch.object(flow.controller, "start_pairing") as start_pairing:
        start_pairing.side_effect = exception("error")
        result = await flow.async_step_pair({})

    assert result["type"] == "abort"
    assert result["reason"] == expected
    assert flow.context == {
        "hkid": "00:00:00:00:00:00",
        "title_placeholders": {"name": "TestDevice"},
    }


@pytest.mark.parametrize("exception,expected", PAIRING_START_FORM_ERRORS)
async def test_pair_form_errors_on_start(hass, exception, expected):
    """Test various pairing errors."""
    discovery_info = {
        "name": "TestDevice",
        "host": "127.0.0.1",
        "port": 8080,
        "properties": {"md": "TestDevice", "id": "00:00:00:00:00:00", "c#": 1, "sf": 1},
    }

    flow = _setup_flow_handler(hass)

    # Device is discovered
    result = await flow.async_step_zeroconf(discovery_info)
    assert result["type"] == "form"
    assert result["step_id"] == "pair"
    assert flow.context == {
        "hkid": "00:00:00:00:00:00",
        "title_placeholders": {"name": "TestDevice"},
    }

    # User initiates pairing - device refuses to enter pairing mode
    with mock.patch.object(flow.controller, "start_pairing") as start_pairing:
        start_pairing.side_effect = exception("error")
        result = await flow.async_step_pair({})

    assert result["type"] == "form"
    assert result["errors"]["pairing_code"] == expected
    assert flow.context == {
        "hkid": "00:00:00:00:00:00",
        "title_placeholders": {"name": "TestDevice"},
    }


@pytest.mark.parametrize("exception,expected", PAIRING_FINISH_ABORT_ERRORS)
async def test_pair_abort_errors_on_finish(hass, exception, expected):
    """Test various pairing errors."""
    discovery_info = {
        "name": "TestDevice",
        "host": "127.0.0.1",
        "port": 8080,
        "properties": {"md": "TestDevice", "id": "00:00:00:00:00:00", "c#": 1, "sf": 1},
    }

    flow = _setup_flow_handler(hass)

    # Device is discovered
    result = await flow.async_step_zeroconf(discovery_info)
    assert result["type"] == "form"
    assert result["step_id"] == "pair"
    assert flow.context == {
        "hkid": "00:00:00:00:00:00",
        "title_placeholders": {"name": "TestDevice"},
    }

    # User initiates pairing - device enters pairing mode and displays code
    result = await flow.async_step_pair({})
    assert result["type"] == "form"
    assert result["step_id"] == "pair"
    assert flow.controller.start_pairing.call_count == 1

    # User submits code - pairing fails but can be retried
    flow.finish_pairing.side_effect = exception("error")
    result = await flow.async_step_pair({"pairing_code": "111-22-33"})
    assert result["type"] == "abort"
    assert result["reason"] == expected
    assert flow.context == {
        "hkid": "00:00:00:00:00:00",
        "title_placeholders": {"name": "TestDevice"},
    }


@pytest.mark.parametrize("exception,expected", PAIRING_FINISH_FORM_ERRORS)
async def test_pair_form_errors_on_finish(hass, exception, expected):
    """Test various pairing errors."""
    discovery_info = {
        "name": "TestDevice",
        "host": "127.0.0.1",
        "port": 8080,
        "properties": {"md": "TestDevice", "id": "00:00:00:00:00:00", "c#": 1, "sf": 1},
    }

    flow = _setup_flow_handler(hass)

    # Device is discovered
    result = await flow.async_step_zeroconf(discovery_info)
    assert result["type"] == "form"
    assert result["step_id"] == "pair"
    assert flow.context == {
        "hkid": "00:00:00:00:00:00",
        "title_placeholders": {"name": "TestDevice"},
    }

    # User initiates pairing - device enters pairing mode and displays code
    result = await flow.async_step_pair({})
    assert result["type"] == "form"
    assert result["step_id"] == "pair"
    assert flow.controller.start_pairing.call_count == 1

    # User submits code - pairing fails but can be retried
    flow.finish_pairing.side_effect = exception("error")
    result = await flow.async_step_pair({"pairing_code": "111-22-33"})
    assert result["type"] == "form"
    assert result["errors"]["pairing_code"] == expected
    assert flow.context == {
        "hkid": "00:00:00:00:00:00",
        "title_placeholders": {"name": "TestDevice"},
    }


async def test_import_works(hass):
    """Test a device being discovered."""
    discovery_info = {
        "name": "TestDevice",
        "host": "127.0.0.1",
        "port": 8080,
        "properties": {"md": "TestDevice", "id": "00:00:00:00:00:00", "c#": 1, "sf": 1},
    }

    import_info = {"AccessoryPairingID": "00:00:00:00:00:00"}

    pairing = mock.Mock(pairing_data={"AccessoryPairingID": "00:00:00:00:00:00"})

    pairing.list_accessories_and_characteristics.return_value = [
        {
            "aid": 1,
            "services": [
                {
                    "characteristics": [{"type": "23", "value": "Koogeek-LS1-20833F"}],
                    "type": "3e",
                }
            ],
        }
    ]

    flow = _setup_flow_handler(hass)

    pairing_cls_imp = "homekit.controller.ip_implementation.IpPairing"

    with mock.patch(pairing_cls_imp) as pairing_cls:
        pairing_cls.return_value = pairing
        result = await flow.async_import_legacy_pairing(
            discovery_info["properties"], import_info
        )

    assert result["type"] == "create_entry"
    assert result["title"] == "Koogeek-LS1-20833F"
    assert result["data"] == pairing.pairing_data


async def test_import_already_configured(hass):
    """Test importing a device from .homekit that is already a ConfigEntry."""
    discovery_info = {
        "name": "TestDevice",
        "host": "127.0.0.1",
        "port": 8080,
        "properties": {"md": "TestDevice", "id": "00:00:00:00:00:00", "c#": 1, "sf": 1},
    }

    import_info = {"AccessoryPairingID": "00:00:00:00:00:00"}

    config_entry = MockConfigEntry(domain="homekit_controller", data=import_info)
    config_entry.add_to_hass(hass)

    flow = _setup_flow_handler(hass)

    result = await flow.async_import_legacy_pairing(
        discovery_info["properties"], import_info
    )
    assert result["type"] == "abort"
    assert result["reason"] == "already_configured"


async def test_user_works(hass):
    """Test user initiated disovers devices."""
    discovery_info = {
        "name": "TestDevice",
        "host": "127.0.0.1",
        "port": 8080,
        "md": "TestDevice",
        "id": "00:00:00:00:00:00",
        "c#": 1,
        "sf": 1,
    }

    pairing = mock.Mock(pairing_data={"AccessoryPairingID": "00:00:00:00:00:00"})
    pairing.list_accessories_and_characteristics.return_value = [
        {
            "aid": 1,
            "services": [
                {
                    "characteristics": [{"type": "23", "value": "Koogeek-LS1-20833F"}],
                    "type": "3e",
                }
            ],
        }
    ]

    flow = _setup_flow_handler(hass)

    flow.controller.pairings = {"00:00:00:00:00:00": pairing}
    flow.controller.discover.return_value = [discovery_info]

    result = await flow.async_step_user()
    assert result["type"] == "form"
    assert result["step_id"] == "user"

    result = await flow.async_step_user({"device": "TestDevice"})
    assert result["type"] == "form"
    assert result["step_id"] == "pair"

    result = await flow.async_step_pair({"pairing_code": "111-22-33"})
    assert result["type"] == "create_entry"
    assert result["title"] == "Koogeek-LS1-20833F"
    assert result["data"] == pairing.pairing_data


async def test_user_no_devices(hass):
    """Test user initiated pairing where no devices discovered."""
    flow = _setup_flow_handler(hass)

    flow.controller.discover.return_value = []
    result = await flow.async_step_user()

    assert result["type"] == "abort"
    assert result["reason"] == "no_devices"


async def test_user_no_unpaired_devices(hass):
    """Test user initiated pairing where no unpaired devices discovered."""
    flow = _setup_flow_handler(hass)

    discovery_info = {
        "name": "TestDevice",
        "host": "127.0.0.1",
        "port": 8080,
        "md": "TestDevice",
        "id": "00:00:00:00:00:00",
        "c#": 1,
        "sf": 0,
    }

    flow.controller.discover.return_value = [discovery_info]
    result = await flow.async_step_user()

    assert result["type"] == "abort"
    assert result["reason"] == "no_devices"


async def test_parse_new_homekit_json(hass):
    """Test migrating recent .homekit/pairings.json files."""
    service = FakeService("public.hap.service.lightbulb")
    on_char = service.add_characteristic("on")
    on_char.value = 1

    accessory = Accessory("TestDevice", "example.com", "Test", "0001", "0.1")
    accessory.services.append(service)

    fake_controller = await setup_platform(hass)
    pairing = fake_controller.add([accessory])
    pairing.pairing_data = {"AccessoryPairingID": "00:00:00:00:00:00"}

    mock_path = mock.Mock()
    mock_path.exists.side_effect = [True, False]

    read_data = {"00:00:00:00:00:00": pairing.pairing_data}
    mock_open = mock.mock_open(read_data=json.dumps(read_data))

    discovery_info = {
        "name": "TestDevice",
        "host": "127.0.0.1",
        "port": 8080,
        "properties": {"md": "TestDevice", "id": "00:00:00:00:00:00", "c#": 1, "sf": 0},
    }

    flow = _setup_flow_handler(hass)

    pairing_cls_imp = "homekit.controller.ip_implementation.IpPairing"

    with mock.patch(pairing_cls_imp) as pairing_cls:
        pairing_cls.return_value = pairing
        with mock.patch("builtins.open", mock_open):
            with mock.patch("os.path", mock_path):
                result = await flow.async_step_zeroconf(discovery_info)

    assert result["type"] == "create_entry"
    assert result["title"] == "TestDevice"
    assert result["data"]["AccessoryPairingID"] == "00:00:00:00:00:00"
    assert flow.context == {
        "hkid": "00:00:00:00:00:00",
        "title_placeholders": {"name": "TestDevice"},
    }


async def test_parse_old_homekit_json(hass):
    """Test migrating original .homekit/hk-00:00:00:00:00:00 files."""
    service = FakeService("public.hap.service.lightbulb")
    on_char = service.add_characteristic("on")
    on_char.value = 1

    accessory = Accessory("TestDevice", "example.com", "Test", "0001", "0.1")
    accessory.services.append(service)

    fake_controller = await setup_platform(hass)
    pairing = fake_controller.add([accessory])
    pairing.pairing_data = {"AccessoryPairingID": "00:00:00:00:00:00"}

    mock_path = mock.Mock()
    mock_path.exists.side_effect = [False, True]

    mock_listdir = mock.Mock()
    mock_listdir.return_value = ["hk-00:00:00:00:00:00", "pairings.json"]

    read_data = {"AccessoryPairingID": "00:00:00:00:00:00"}
    mock_open = mock.mock_open(read_data=json.dumps(read_data))

    discovery_info = {
        "name": "TestDevice",
        "host": "127.0.0.1",
        "port": 8080,
        "properties": {"md": "TestDevice", "id": "00:00:00:00:00:00", "c#": 1, "sf": 0},
    }

    flow = _setup_flow_handler(hass)

    pairing_cls_imp = "homekit.controller.ip_implementation.IpPairing"

    with mock.patch(pairing_cls_imp) as pairing_cls:
        pairing_cls.return_value = pairing
        with mock.patch("builtins.open", mock_open):
            with mock.patch("os.path", mock_path):
                with mock.patch("os.listdir", mock_listdir):
                    result = await flow.async_step_zeroconf(discovery_info)

    assert result["type"] == "create_entry"
    assert result["title"] == "TestDevice"
    assert result["data"]["AccessoryPairingID"] == "00:00:00:00:00:00"
    assert flow.context == {
        "hkid": "00:00:00:00:00:00",
        "title_placeholders": {"name": "TestDevice"},
    }


async def test_parse_overlapping_homekit_json(hass):
    """Test migrating .homekit/pairings.json files when hk- exists too."""
    service = FakeService("public.hap.service.lightbulb")
    on_char = service.add_characteristic("on")
    on_char.value = 1

    accessory = Accessory("TestDevice", "example.com", "Test", "0001", "0.1")
    accessory.services.append(service)

    fake_controller = await setup_platform(hass)
    pairing = fake_controller.add([accessory])
    pairing.pairing_data = {"AccessoryPairingID": "00:00:00:00:00:00"}

    mock_listdir = mock.Mock()
    mock_listdir.return_value = ["hk-00:00:00:00:00:00", "pairings.json"]

    mock_path = mock.Mock()
    mock_path.exists.side_effect = [True, True]

    # First file to get loaded is .homekit/pairing.json
    read_data_1 = {"00:00:00:00:00:00": {"AccessoryPairingID": "00:00:00:00:00:00"}}
    mock_open_1 = mock.mock_open(read_data=json.dumps(read_data_1))

    # Second file to get loaded is .homekit/hk-00:00:00:00:00:00
    read_data_2 = {"AccessoryPairingID": "00:00:00:00:00:00"}
    mock_open_2 = mock.mock_open(read_data=json.dumps(read_data_2))

    side_effects = [mock_open_1.return_value, mock_open_2.return_value]

    discovery_info = {
        "name": "TestDevice",
        "host": "127.0.0.1",
        "port": 8080,
        "properties": {"md": "TestDevice", "id": "00:00:00:00:00:00", "c#": 1, "sf": 0},
    }

    flow = _setup_flow_handler(hass)

    pairing_cls_imp = "homekit.controller.ip_implementation.IpPairing"

    with mock.patch(pairing_cls_imp) as pairing_cls:
        pairing_cls.return_value = pairing
        with mock.patch("builtins.open", side_effect=side_effects):
            with mock.patch("os.path", mock_path):
                with mock.patch("os.listdir", mock_listdir):
                    result = await flow.async_step_zeroconf(discovery_info)

        await hass.async_block_till_done()

    assert result["type"] == "create_entry"
    assert result["title"] == "TestDevice"
    assert result["data"]["AccessoryPairingID"] == "00:00:00:00:00:00"
    assert flow.context == {
        "hkid": "00:00:00:00:00:00",
        "title_placeholders": {"name": "TestDevice"},
    }
