"""Tests for rental request router (T05)."""
import pytest
from unittest.mock import AsyncMock, patch
from sqlalchemy import select

from app.models.rental_request import RentalRequest, RentalRequestItem


VALID_RENTAL_PAYLOAD = {
    "event_name": "Sommerfestival 2026",
    "date_start": "2026-08-15",
    "date_end": "2026-08-17",
    "location": "Berlin",
    "person_count": 500,
    "contact_name": "Max Veranstalter",
    "contact_company": "Event GmbH",
    "contact_email": "max@event.de",
    "contact_phone": "+49 30 1234567",
    "contact_street": "Hauptstr. 42",
    "contact_postalcode": "10115",
    "contact_city": "Berlin",
    "message": "Bitte um Angebot",
    "items": [{"equipment_id": 1, "quantity": 2}],
}


@pytest.mark.asyncio
async def test_rental_valid_returns_201(client, seeded_equipment):
    """POST /api/rental-requests with valid data returns 201 with reference number."""
    with patch(
        "app.services.rentman_service.RentmanService.create_project_request",
        new_callable=AsyncMock,
        return_value={"id": "rentman-123"},
    ), patch(
        "app.services.rentman_service.RentmanService.add_equipment_to_request",
        new_callable=AsyncMock,
        return_value={"id": "eq-1"},
    ), patch(
        "app.services.email_service.EmailService.send_rental_confirmation",
        new_callable=AsyncMock,
        return_value=True,
    ):
        resp = await client.post("/api/rental-requests", json=VALID_RENTAL_PAYLOAD)
    assert resp.status_code == 201
    data = resp.json()
    assert "reference_number" in data
    ref = data["reference_number"]
    assert ref.startswith("HMS-")
    parts = ref.split("-")
    assert len(parts) == 3
    assert parts[1].isdigit()
    assert len(parts[2]) == 5
    assert parts[2].isdigit()
    assert data["status"] == "pending"


@pytest.mark.asyncio
async def test_rental_date_end_before_start_returns_422(client, seeded_equipment):
    """POST /api/rental-requests with date_end < date_start returns 422."""
    payload = {**VALID_RENTAL_PAYLOAD, "date_start": "2026-08-20", "date_end": "2026-08-15"}
    resp = await client.post("/api/rental-requests", json=payload)
    assert resp.status_code == 422


@pytest.mark.asyncio
async def test_rental_empty_items_returns_422(client, seeded_equipment):
    """POST /api/rental-requests with empty items array returns 422."""
    payload = {**VALID_RENTAL_PAYLOAD, "items": []}
    resp = await client.post("/api/rental-requests", json=payload)
    assert resp.status_code == 422


@pytest.mark.asyncio
async def test_rental_saved_to_db(client, seeded_equipment, test_db):
    """POST /api/rental-requests saves to rental_requests and rental_request_items tables."""
    with patch(
        "app.services.rentman_service.RentmanService.create_project_request",
        new_callable=AsyncMock,
        return_value={"id": "rentman-456"},
    ), patch(
        "app.services.rentman_service.RentmanService.add_equipment_to_request",
        new_callable=AsyncMock,
        return_value={"id": "eq-2"},
    ), patch(
        "app.services.email_service.EmailService.send_rental_confirmation",
        new_callable=AsyncMock,
        return_value=True,
    ):
        resp = await client.post("/api/rental-requests", json=VALID_RENTAL_PAYLOAD)
    assert resp.status_code == 201
    ref_number = resp.json()["reference_number"]

    result = await test_db.execute(
        select(RentalRequest).where(RentalRequest.reference_number == ref_number)
    )
    rental = result.scalar_one_or_none()
    assert rental is not None
    assert rental.event_name == "Sommerfestival 2026"
    assert rental.contact_name == "Max Veranstalter"
    assert rental.status == "pending"
    assert rental.rentman_request_id == "rentman-456"
    assert rental.rentman_sync_status == "success"

    items_result = await test_db.execute(
        select(RentalRequestItem).where(RentalRequestItem.rental_request_id == rental.id)
    )
    items = items_result.scalars().all()
    assert len(items) == 1
    assert items[0].equipment_id == 1
    assert items[0].quantity == 2
    assert items[0].equipment_name == "L-Acoustics K2"


@pytest.mark.asyncio
async def test_rental_triggers_rentman(client, seeded_equipment):
    """POST /api/rental-requests triggers Rentman create_project_request."""
    with patch(
        "app.services.rentman_service.RentmanService.create_project_request",
        new_callable=AsyncMock,
        return_value={"id": "rentman-789"},
    ) as mock_create, patch(
        "app.services.rentman_service.RentmanService.add_equipment_to_request",
        new_callable=AsyncMock,
        return_value={"id": "eq-3"},
    ) as mock_add, patch(
        "app.services.email_service.EmailService.send_rental_confirmation",
        new_callable=AsyncMock,
        return_value=True,
    ):
        resp = await client.post("/api/rental-requests", json=VALID_RENTAL_PAYLOAD)
    assert resp.status_code == 201
    mock_create.assert_awaited_once()
    mock_add.assert_awaited_once()


@pytest.mark.asyncio
async def test_rental_triggers_email(client, seeded_equipment):
    """POST /api/rental-requests triggers confirmation email."""
    with patch(
        "app.services.rentman_service.RentmanService.create_project_request",
        new_callable=AsyncMock,
        return_value={"id": "rentman-email-test"},
    ), patch(
        "app.services.rentman_service.RentmanService.add_equipment_to_request",
        new_callable=AsyncMock,
        return_value={"id": "eq-4"},
    ), patch(
        "app.services.email_service.EmailService.send_rental_confirmation",
        new_callable=AsyncMock,
        return_value=True,
    ) as mock_email:
        resp = await client.post("/api/rental-requests", json=VALID_RENTAL_PAYLOAD)
    assert resp.status_code == 201
    mock_email.assert_awaited_once()
    call_kwargs = mock_email.call_args.kwargs
    assert call_kwargs["to_addr"] == "max@event.de"
    assert call_kwargs["event_name"] == "Sommerfestival 2026"
    assert call_kwargs["reference_number"].startswith("HMS-")
    assert len(call_kwargs["items"]) == 1


@pytest.mark.asyncio
async def test_rental_rentman_failure_still_returns_201(client, seeded_equipment):
    """POST /api/rental-requests still returns 201 when Rentman API fails."""
    with patch(
        "app.services.rentman_service.RentmanService.create_project_request",
        new_callable=AsyncMock,
        side_effect=Exception("Rentman API error"),
    ), patch(
        "app.services.email_service.EmailService.send_rental_confirmation",
        new_callable=AsyncMock,
        return_value=True,
    ):
        resp = await client.post("/api/rental-requests", json=VALID_RENTAL_PAYLOAD)
    assert resp.status_code == 201
