"""Unit tests for migrate_to_short_ids script logic."""

from unittest.mock import AsyncMock, MagicMock, patch

import pytest


@pytest.fixture
def mock_image_null_short_id():
    img = MagicMock()
    img.id = "img-uuid-1"
    img.short_id = None
    img.storage_key = "oldhashkey1234567890"
    img.thumbnail_key = "oldhashkey1234567890-thumb"
    img.mime_type = "image/jpeg"
    return img


@pytest.fixture
def mock_image_with_short_id():
    img = MagicMock()
    img.id = "img-uuid-2"
    img.short_id = "AbCd1234"
    img.storage_key = "AbCd1234"
    img.thumbnail_key = "AbCd1234-thumb"
    img.mime_type = "image/jpeg"
    return img


@pytest.mark.asyncio
async def test_migrate_processes_image_without_short_id(mock_image_null_short_id):
    """Images with short_id IS NULL are processed: storage copied, DB updated, old keys deleted."""
    from scripts.migrate_to_short_ids import migrate_image

    storage = MagicMock()
    storage.get = AsyncMock(return_value=b"imagedata")
    storage.put = AsyncMock()
    storage.delete = AsyncMock()

    session = MagicMock()
    session.execute = AsyncMock()
    session.flush = AsyncMock()

    old_key = mock_image_null_short_id.storage_key
    new_short_id = "NewSh123"
    with patch("scripts.migrate_to_short_ids.generate_short_id", return_value=new_short_id):
        result = await migrate_image(mock_image_null_short_id, storage, session)

    assert result is True
    storage.put.assert_any_call(new_short_id, b"imagedata", "image/jpeg")
    storage.delete.assert_any_call(old_key)


@pytest.mark.asyncio
async def test_migrate_skips_image_with_short_id(mock_image_with_short_id):
    """Images that already have a short_id are skipped."""
    from scripts.migrate_to_short_ids import migrate_image

    storage = MagicMock()
    session = MagicMock()

    result = await migrate_image(mock_image_with_short_id, storage, session)

    assert result is False
    storage.get.assert_not_called() if hasattr(storage.get, "assert_not_called") else None


@pytest.mark.asyncio
async def test_migrate_continues_on_storage_error(mock_image_null_short_id):
    """If storage copy fails, error is logged and migrate_image returns False without aborting."""
    from scripts.migrate_to_short_ids import migrate_image

    storage = MagicMock()
    storage.get = AsyncMock(side_effect=Exception("storage read error"))
    storage.put = AsyncMock()
    storage.delete = AsyncMock()

    session = MagicMock()
    session.execute = AsyncMock()
    session.flush = AsyncMock()

    with patch("scripts.migrate_to_short_ids.generate_short_id", return_value="ErrSh123"):
        result = await migrate_image(mock_image_null_short_id, storage, session)

    assert result is False
    storage.put.assert_not_called()


@pytest.mark.asyncio
async def test_migrate_summary_counts(mock_image_null_short_id, mock_image_with_short_id):
    """run_migration reports correct migrated and skipped counts."""
    from scripts.migrate_to_short_ids import run_migration

    storage = MagicMock()
    storage.get = AsyncMock(return_value=b"data")
    storage.put = AsyncMock()
    storage.delete = AsyncMock()

    session = MagicMock()
    session.execute = AsyncMock()
    session.flush = AsyncMock()

    images = [mock_image_null_short_id, mock_image_with_short_id]

    with patch("scripts.migrate_to_short_ids.generate_short_id", return_value="NewSh999"):
        migrated, skipped, failed = await run_migration(images, storage, session)

    assert migrated == 1
    assert skipped == 1
    assert failed == 0
