"""Tests for AssetStore — focusing on self-heal behavior on startup.

Regression guard for the "restored backup loses notification sounds" class
of bug: when the SQLite DB is restored from elsewhere but the ``assets/``
directory hasn't moved with it, prebuilt sound files end up with stale
DB rows pointing at missing files.  ``heal_prebuilt_files`` is the safety
net.
"""

import pytest

from ledgrab.storage.asset_store import AssetStore
from ledgrab.storage.database import Database


@pytest.fixture
def store_with_prebuilt(tmp_path):
    """AssetStore seeded with the shipped prebuilt sounds."""
    db = Database(tmp_path / "test.db")
    assets_dir = tmp_path / "assets"
    prebuilt_dir = tmp_path / "prebuilt"
    prebuilt_dir.mkdir()
    # Minimal valid WAV-ish bytes — content doesn't matter for these tests,
    # only that the file exists and is copied around correctly.
    for name in ("alert.wav", "bell.wav"):
        (prebuilt_dir / name).write_bytes(b"RIFF\x00\x00\x00\x00WAVEfmt " + name.encode())

    store = AssetStore(db, assets_dir)
    imported = store.import_prebuilt_sounds(prebuilt_dir)
    assert len(imported) == 2
    yield store, prebuilt_dir, assets_dir
    db.close()


class TestHealPrebuiltFiles:
    def test_heals_missing_stored_file(self, store_with_prebuilt):
        store, prebuilt_dir, assets_dir = store_with_prebuilt
        alert = next(a for a in store.get_all_assets() if a.filename == "alert.wav")

        # Simulate a partial restore: DB row survived, file vanished.
        (assets_dir / alert.stored_filename).unlink()
        assert store.get_file_path(alert.id) is None

        healed = store.heal_prebuilt_files(prebuilt_dir)

        assert [a.id for a in healed] == [alert.id]
        restored_path = assets_dir / alert.stored_filename
        assert restored_path.exists()
        # File content round-trips from the prebuilt source.
        assert restored_path.read_bytes() == (prebuilt_dir / "alert.wav").read_bytes()
        # Size metadata is updated to match the re-copied file.
        assert store.get_asset(alert.id).size_bytes == restored_path.stat().st_size

    def test_idempotent_when_files_present(self, store_with_prebuilt):
        store, prebuilt_dir, _ = store_with_prebuilt
        first = store.heal_prebuilt_files(prebuilt_dir)
        second = store.heal_prebuilt_files(prebuilt_dir)
        assert first == [] and second == []

    def test_skips_soft_deleted_prebuilt(self, store_with_prebuilt):
        store, prebuilt_dir, assets_dir = store_with_prebuilt
        alert = next(a for a in store.get_all_assets() if a.filename == "alert.wav")
        store.delete_asset(alert.id)  # prebuilt → soft-delete; file removed
        assert store.get_asset(alert.id).deleted is True

        healed = store.heal_prebuilt_files(prebuilt_dir)
        # Heal must not resurrect a soft-deleted asset — that is
        # `restore_prebuilt`'s job and is an explicit user action.
        assert healed == []
        assert not (assets_dir / alert.stored_filename).exists()

    def test_returns_empty_when_source_also_missing(self, store_with_prebuilt):
        """If both the stored file and the prebuilt source are gone, the
        heal is a no-op — the asset stays broken and the method does not
        raise (so startup is not aborted)."""
        store, prebuilt_dir, assets_dir = store_with_prebuilt
        alert = next(a for a in store.get_all_assets() if a.filename == "alert.wav")
        (assets_dir / alert.stored_filename).unlink()
        (prebuilt_dir / "alert.wav").unlink()

        healed = store.heal_prebuilt_files(prebuilt_dir)

        assert healed == []
        # Unrelated assets still heal correctly.
        bell = next(a for a in store.get_all_assets() if a.filename == "bell.wav")
        (assets_dir / bell.stored_filename).unlink()
        healed = store.heal_prebuilt_files(prebuilt_dir)
        assert [a.id for a in healed] == [bell.id]

    def test_import_prebuilt_sounds_runs_heal(self, tmp_path):
        """End-to-end: restoring a DB and re-running startup heals missing files."""
        # First run: import both sounds normally.
        db = Database(tmp_path / "first.db")
        assets_dir = tmp_path / "assets"
        prebuilt_dir = tmp_path / "prebuilt"
        prebuilt_dir.mkdir()
        (prebuilt_dir / "alert.wav").write_bytes(b"hello-alert")

        store = AssetStore(db, assets_dir)
        imported = store.import_prebuilt_sounds(prebuilt_dir)
        assert len(imported) == 1
        alert_stored = imported[0].stored_filename
        db.close()

        # Simulate partial restore: file gone, DB row preserved.
        (assets_dir / alert_stored).unlink()

        # Second run: reopen the same DB + assets dir.
        db2 = Database(tmp_path / "first.db")
        store2 = AssetStore(db2, assets_dir)
        store2.import_prebuilt_sounds(prebuilt_dir)
        assert (assets_dir / alert_stored).exists()
        db2.close()


class TestWarnMissingCustomFiles:
    def test_reports_custom_asset_with_missing_file(self, tmp_path):
        db = Database(tmp_path / "test.db")
        assets_dir = tmp_path / "assets"
        store = AssetStore(db, assets_dir)

        asset = store.create_asset(
            name="User Sound",
            filename="user.wav",
            file_data=b"RIFF\x00\x00\x00\x00WAVEfmt user",
        )
        (assets_dir / asset.stored_filename).unlink()

        missing = store.warn_missing_custom_files()

        assert [a.id for a in missing] == [asset.id]
        db.close()

    def test_quiet_when_all_custom_files_present(self, tmp_path):
        db = Database(tmp_path / "test.db")
        store = AssetStore(db, tmp_path / "assets")
        store.create_asset(name="User", filename="user.wav", file_data=b"data123")
        assert store.warn_missing_custom_files() == []
        db.close()

    def test_ignores_prebuilt_and_soft_deleted(self, tmp_path):
        db = Database(tmp_path / "test.db")
        assets_dir = tmp_path / "assets"
        prebuilt_dir = tmp_path / "prebuilt"
        prebuilt_dir.mkdir()
        (prebuilt_dir / "alert.wav").write_bytes(b"prebuilt-alert")

        store = AssetStore(db, assets_dir)
        store.import_prebuilt_sounds(prebuilt_dir)
        prebuilt_asset = store.get_all_assets()[0]
        (assets_dir / prebuilt_asset.stored_filename).unlink()

        # Missing prebuilt files are NOT reported here (that's heal's job).
        assert store.warn_missing_custom_files() == []
        db.close()
