import pytest
from fastapi.testclient import TestClient
from hypothesis import given, strategies as st
from bson import ObjectId
from fastapi_server import app, db

client = TestClient(app)


@pytest.fixture(autouse=True)
def clear_db():
  db.items.delete_many({})
  yield
  db.items.delete_many({})

# --======-----------====
# Unit tests
# --======-----------====


def test_create_item():
  response = client.post(
      "/items/",
      json={"name": "Test Item", "price": 10.99, "quantity": 5, "unit": "piece"}
  )
  assert response.status_code == 200
  assert "id" in response.json()


def test_read_items():
  # Create a test item
  client.post("/items/", json={"name": "Test Item",
              "price": 10.99, "quantity": 5, "unit": "piece"})

  response = client.get("/items/")
  assert response.status_code == 200
  items = response.json()
  assert len(items) > 0
  assert items[0]["name"] == "Test Item"


def test_read_item():
  # Create a test item
  create_response = client.post(
      "/items/", json={"name": "Test Item", "price": 10.99, "quantity": 5, "unit": "piece"})
  item_id = create_response.json()["id"]

  response = client.get(f"/items/{item_id}")
  assert response.status_code == 200
  assert response.json()["name"] == "Test Item"


def test_update_item():
  # Create a test item
  create_response = client.post(
      "/items/", json={"name": "Test Item", "price": 10.99, "quantity": 5, "unit": "piece"})
  item_id = create_response.json()["id"]

  update_data = {"name": "Updated Item",
                 "price": 15.99, "quantity": 10, "unit": "piece"}
  response = client.put(f"/items/{item_id}", json=update_data)
  assert response.status_code == 200
  assert response.json()["message"] == "Item updated successfully"

  # Verify the update
  get_response = client.get(f"/items/{item_id}")
  assert get_response.json()["name"] == "Updated Item"


def test_delete_item():
  # Create a test item
  create_response = client.post(
      "/items/", json={"name": "Test Item", "price": 10.99, "quantity": 5, "unit": "piece"})
  item_id = create_response.json()["id"]

  response = client.delete(f"/items/{item_id}")
  assert response.status_code == 200
  assert response.json()["message"] == "Item deleted successfully"

  # Verify the deletion
  get_response = client.get(f"/items/{item_id}")
  assert get_response.status_code == 404

# --======-----------====
# Property-based tests
# --======-----------====


@given(
    name=st.text(min_size=1, max_size=50),
    price=st.floats(min_value=0.01, max_value=1000000,
                    allow_nan=False, allow_infinity=False),
    quantity=st.integers(min_value=0, max_value=1000000),
    unit=st.text(min_size=1, max_size=10)
)
def test_create_item_property(name, price, quantity, unit):
  response = client.post(
      "/items/",
      json={"name": name, "price": price, "quantity": quantity, "unit": unit}
  )
  assert response.status_code == 200
  assert "id" in response.json()

  # Verify the created item
  item_id = response.json()["id"]
  get_response = client.get(f"/items/{item_id}")
  assert get_response.status_code == 200
  item = get_response.json()
  assert item["name"] == name
  assert pytest.approx(item["price"], 0.01) == price
  assert item["quantity"] == quantity
  assert item["unit"] == unit


@given(
    st.lists(
        st.fixed_dictionaries({
            "name": st.text(min_size=1, max_size=50),
            "price": st.floats(min_value=0.01, max_value=1000000, allow_nan=False, allow_infinity=False),
            "quantity": st.integers(min_value=0, max_value=1000000),
            "unit": st.text(min_size=1, max_size=10)
        }),
        min_size=1,
        max_size=10
    )
)
def test_read_items_property(items):
  # Create multiple items
  for item in items:
    client.post("/items/", json=item)

  response = client.get("/items/")
  assert response.status_code == 200
  retrieved_items = response.json()
  assert len(retrieved_items) == len(items)

  for retrieved_item in retrieved_items:
    assert "name" in retrieved_item
    assert "price" in retrieved_item
    assert "quantity" in retrieved_item
    assert "unit" in retrieved_item


if __name__ == "__main__":
  pytest.main([__file__])
