"""Shared fixtures for end-to-end API tests.

Uses the real FastAPI app with a session-scoped TestClient.
All e2e tests run against an ISOLATED temporary database and assets
directory — never the production data.
"""

import shutil
import tempfile
from pathlib import Path

import pytest

# ---------------------------------------------------------------------------
# Isolate e2e tests from production data.
#
# We must set the config singleton BEFORE wled_controller.main is imported,
# because main.py reads get_config() at module level to create the DB and
# all stores.  By forcing the singleton here we guarantee the app opens a
# throwaway SQLite file in a temp directory.
# ---------------------------------------------------------------------------

_e2e_tmp = Path(tempfile.mkdtemp(prefix="wled_e2e_"))
_test_db_path = str(_e2e_tmp / "test_ledgrab.db")
_test_assets_dir = str(_e2e_tmp / "test_assets")

import wled_controller.config as _config_mod  # noqa: E402

# Build a Config that mirrors production settings but with isolated paths.
_original_config = _config_mod.Config.load()
_test_config = _original_config.model_copy(
    update={
        "storage": _config_mod.StorageConfig(database_file=_test_db_path),
        "assets": _config_mod.AssetsConfig(
            assets_dir=_test_assets_dir,
            max_file_size_mb=_original_config.assets.max_file_size_mb,
        ),
    },
)
# Install as the global singleton so all subsequent get_config() calls
# (including main.py module-level code) use isolated paths.
_config_mod.config = _test_config

API_KEY = next(iter(_test_config.auth.api_keys.values()), "")
AUTH_HEADERS = {"Authorization": f"Bearer {API_KEY}"}


@pytest.fixture(scope="session")
def _test_client():
    """Session-scoped TestClient to avoid lifespan re-entry issues.

    The app's lifespan (MQTT, automation engine, health monitoring, etc.)
    starts once for the entire e2e test session and shuts down after all
    tests complete.  The app uses the isolated test database set above.
    """
    from fastapi.testclient import TestClient
    from wled_controller.main import app

    with TestClient(app, raise_server_exceptions=False) as c:
        yield c

    # Clean up temp directory after all e2e tests finish
    shutil.rmtree(_e2e_tmp, ignore_errors=True)


@pytest.fixture
def client(_test_client):
    """Per-test client with auth headers and clean stores.

    Clears all entity stores before each test so tests are independent.
    """
    _clear_stores()
    _test_client.headers["Authorization"] = f"Bearer {API_KEY}"
    yield _test_client
    # Clean up after test
    _clear_stores()


def _clear_stores():
    """Remove all entities from all stores for test isolation."""
    # Reset frozen-writes flags that restore flows set.
    # Without this, subsequent tests can't persist data.
    from wled_controller.storage.base_store import unfreeze_saves
    unfreeze_saves()
    import wled_controller.storage.database as _db_mod
    _db_mod._writes_frozen = False

    from wled_controller.api import dependencies as deps

    store_clearers = [
        (deps.get_device_store, "get_all_devices", "delete_device"),
        (deps.get_output_target_store, "get_all_targets", "delete_target"),
        (deps.get_color_strip_store, "get_all_sources", "delete_source"),
        (deps.get_value_source_store, "get_all", "delete"),
        (deps.get_sync_clock_store, "get_all", "delete"),
        (deps.get_automation_store, "get_all", "delete"),
        (deps.get_scene_preset_store, "get_all", "delete"),
        (deps.get_asset_store, "get_all_assets", "delete_asset"),
    ]
    for getter, list_method, delete_method in store_clearers:
        try:
            store = getter()
            items = getattr(store, list_method)()
            for item in items:
                item_id = getattr(item, "id", getattr(item, "device_id", None))
                if item_id:
                    try:
                        getattr(store, delete_method)(item_id)
                    except Exception:
                        pass
        except RuntimeError:
            pass  # Store not initialized yet
