"""Tests for Rentman equipment import pipeline (T04)."""
import pytest
import pytest_asyncio
from unittest.mock import AsyncMock, patch, MagicMock
from sqlalchemy import select
from app.models.equipment import EquipmentCache
from app.models.sync_log import SyncLog
from app.services.sync_service import SyncService
from app.services.rentman_service import RentmanService


def make_raw_equipment(rid: str, name: str, category: str = "Lautsprecher") -> dict:
    return {
        "id": rid,
        "name": name,
        "number": f"{name[:3].upper()}-001",
        "code": f"{name[:3].upper()}-001",
        "equipment_group": {"name": category},
        "description": f"Description for {name}",
        "specifications": {"weight": 50, "power": 750},
        "images": [{"url": f"https://example.com/{rid}.jpg"}],
        "rental_price": 150.00,
        "brand": "L-Acoustics",
        "available": True,
    }


@pytest.mark.asyncio
async def test_transform_equipment():
    raw = make_raw_equipment("42", "K2 Line Array", "Lautsprecher")
    result = RentmanService.transform_equipment(raw)
    assert result["rentman_id"] == "42"
    assert result["name"] == "K2 Line Array"
    assert result["category"] == "Lautsprecher"
    assert result["images"] == ["https://example.com/42.jpg"]
    assert result["brand"] == "L-Acoustics"
    assert result["available"] is True


@pytest.mark.asyncio
async def test_paginated_import(test_db):
    """Import service iterates all pages (3 pages with 250 items)."""
    page1 = {"data": [make_raw_equipment(str(i), f"Item {i}") for i in range(100)], "itemCount": 250}
    page2 = {"data": [make_raw_equipment(str(i), f"Item {i}") for i in range(100, 200)], "itemCount": 250}
    page3 = {"data": [make_raw_equipment(str(i), f"Item {i}") for i in range(200, 250)], "itemCount": 250}
    page4 = {"data": [], "itemCount": 250}

    mock_rentman = MagicMock()
    mock_rentman.get_all_equipment = AsyncMock(return_value=[
        *[make_raw_equipment(str(i), f"Item {i}") for i in range(250)]
    ])

    with patch("app.services.sync_service.cache") as mock_cache:
        mock_cache.delete_pattern = AsyncMock(return_value=0)
        sync_service = SyncService(test_db, rentman=mock_rentman)
        result = await sync_service.run_sync()

    assert result["status"] == "completed"
    assert result["items_processed"] == 250
    assert result["items_failed"] == 0

    # Verify equipment was upserted
    db_result = await test_db.execute(select(EquipmentCache))
    items = db_result.scalars().all()
    assert len(items) == 250

    # Verify sync_log entry
    log_result = await test_db.execute(select(SyncLog))
    logs = log_result.scalars().all()
    assert len(logs) == 1
    assert logs[0].status == "completed"
    assert logs[0].items_processed == 250


@pytest.mark.asyncio
async def test_sync_upsert_existing(test_db):
    """Upsert should update existing equipment, not duplicate."""
    existing = EquipmentCache(rentman_id="100", name="Old Name", category="Old")
    test_db.add(existing)
    await test_db.commit()

    mock_rentman = MagicMock()
    mock_rentman.get_all_equipment = AsyncMock(return_value=[
        make_raw_equipment("100", "New Name", "Lautsprecher")
    ])

    with patch("app.services.sync_service.cache") as mock_cache:
        mock_cache.delete_pattern = AsyncMock(return_value=0)
        sync_service = SyncService(test_db, rentman=mock_rentman)
        result = await sync_service.run_sync()

    assert result["items_processed"] == 1
    db_result = await test_db.execute(select(EquipmentCache))
    items = db_result.scalars().all()
    assert len(items) == 1
    assert items[0].name == "New Name"


@pytest.mark.asyncio
async def test_sync_failure_logs_error(test_db):
    """Sync failure should be logged with error message."""
    mock_rentman = MagicMock()
    mock_rentman.get_all_equipment = AsyncMock(side_effect=Exception("API unreachable"))

    with patch("app.services.sync_service.cache") as mock_cache:
        mock_cache.delete_pattern = AsyncMock(return_value=0)
        sync_service = SyncService(test_db, rentman=mock_rentman)
        result = await sync_service.run_sync()

    assert result["status"] == "failed"
    assert result["items_processed"] == 0

    log_result = await test_db.execute(select(SyncLog))
    log = log_result.scalar_one()
    assert log.status == "failed"
    assert "API unreachable" in (log.error_message or "")


@pytest.mark.asyncio
async def test_get_all_equipment_paginates():
    """RentmanService.get_all_equipment iterates until data is empty."""
    page1 = {"data": [{"id": str(i), "name": f"Item {i}"} for i in range(100)]}
    page2 = {"data": [{"id": str(i), "name": f"Item {i}"} for i in range(100, 150)]}
    page3 = {"data": []}

    svc = RentmanService(token="test-token")
    svc.get_equipment_page = AsyncMock(side_effect=[page1, page2, page3])
    result = await svc.get_all_equipment(limit=100)

    assert len(result) == 150
    assert svc.get_equipment_page.call_count == 3
