import asyncio
import json
import pytest
from pathlib import Path
from typing import Any
from py.services.settings_manager import get_settings_manager
from py.utils import example_images_download_manager as download_module

class RecordingWebSocketManager:
    def __init__(self) -> None:
        self.payloads: list[dict[str, Any]] = []
    async def broadcast(self, payload: dict[str, Any]) -> None:
        self.payloads.append(payload)

class StubScanner:
    def __init__(self, models: list[dict[str, Any]]) -> None:
        self.raw_data = models
    async def get_cached_data(self):
        class Cache:
            def __init__(self, data): self.raw_data = data
        return Cache(self.raw_data)

@pytest.mark.asyncio
async def test_reprocessing_triggered_when_folder_missing(monkeypatch, tmp_path):
    # Setup paths
    images_root = tmp_path / "examples"
    images_root.mkdir()
    
    settings_manager = get_settings_manager()
    monkeypatch.setitem(settings_manager.settings, "example_images_path", str(images_root))
    monkeypatch.setitem(settings_manager.settings, "libraries", {"default": {}})
    monkeypatch.setitem(settings_manager.settings, "active_library", "default")
    
    model_hash = "f" * 64
    model_name = "Issue 760 Model"
    
    # Create a progress file where this model is already processed
    progress_file = images_root / ".download_progress.json"
    progress_file.write_text(json.dumps({
        "processed_models": [model_hash],
        "failed_models": []
    }))
    
    # But the model folder is missing! (repro condition)
    
    model_data = {
        "sha256": model_hash,
        "model_name": model_name,
        "file_path": str(tmp_path / "model.safetensors"),
        "file_name": "model.safetensors",
        "civitai": {"images": [{"url": "https://example.com/img.png"}]}
    }
    
    scanner = StubScanner([model_data])
    async def mock_get_lora_scanner():
        return scanner
    monkeypatch.setattr(download_module.ServiceRegistry, "get_lora_scanner", mock_get_lora_scanner)
    
    # Mock downloader and processor to avoid actual network/file ops
    async def fake_get_downloader():
        class MockDownloader:
            async def download_to_memory(self, *args, **kwargs):
                return True, b"data", {"content-type": "image/png"}
        return MockDownloader()
        
    monkeypatch.setattr(download_module, "get_downloader", fake_get_downloader)
    
    process_called = False
    async def fake_process_local_examples(*args):
        nonlocal process_called
        process_called = True
        return False # Fallback to remote
        
    monkeypatch.setattr(download_module.ExampleImagesProcessor, "process_local_examples", fake_process_local_examples)
    
    async def fake_download_model_images(*args):
        # Create the directory so it's "fixed"
        model_dir = args[3]
        Path(model_dir).mkdir(parents=True, exist_ok=True)
        (Path(model_dir) / "image_0.png").write_text("fixed")
        return True, False, [], []
        
    monkeypatch.setattr(download_module.ExampleImagesProcessor, "download_model_images_with_tracking", fake_download_model_images)
    
    # Run the manager
    ws_manager = RecordingWebSocketManager()
    manager = download_module.DownloadManager(ws_manager=ws_manager)
    
    result = await manager.start_download({"model_types": ["lora"], "delay": 0})
    assert result["success"] is True
    
    # Wait for completion
    if manager._download_task:
        await asyncio.wait_for(manager._download_task, timeout=2)
        
    # Verify reprocessing was triggered
    assert model_hash in manager._progress["reprocessed_models"]
    assert model_hash in manager._progress["processed_models"] # Should be back in processed
    
    # Verify the progress was saved (discarding reprocessed in memory, but summary logged)
    saved_progress = json.loads(progress_file.read_text())
    assert model_hash in saved_progress["processed_models"]
