import numpy as np
import pytest

from pyvelora import Vector


def test_vector_init_valid_1d_data():
	v = Vector([1, 2, 3])
	assert isinstance(v, Vector)
	assert v.shape == (3,)
	assert v.ndim == 1
	assert v.size == 3


def test_vector_init_raises_for_non_1d_data():
	with pytest.raises(ValueError, match="Vector must be 1D"):
		Vector([[1, 2], [3, 4]])


def test_vector_getitem_scalar_returns_scalar():
	v = Vector([10, 20, 30])
	assert v[1] == 20


def test_vector_getitem_slice_returns_vector():
	v = Vector([10, 20, 30, 40])
	sliced = v[1:3]
	assert isinstance(sliced, Vector)
	assert np.array_equal(sliced.data, np.array([20, 30]))


def test_vector_setitem_updates_value():
	v = Vector([1, 2, 3])
	v[1] = 99
	assert v[1] == 99


def test_vector_len_iter_and_contains():
	v = Vector([4, 5, 6])
	assert len(v) == 3
	assert list(v) == [4, 5, 6]
	assert 5 in v
	assert 9 not in v


def test_vector_bool_true_for_non_empty_false_for_empty():
	assert bool(Vector([1])) is True
	assert bool(Vector([])) is False


def test_vector_add_and_subtract():
	v1 = Vector([1, 2, 3])
	v2 = Vector([4, 5, 6])

	summed = v1 + v2
	diff = v2 - v1

	assert isinstance(summed, Vector)
	assert isinstance(diff, Vector)
	assert np.array_equal(summed.data, np.array([5, 7, 9]))
	assert np.array_equal(diff.data, np.array([3, 3, 3]))


def test_vector_mul_and_div_elementwise():
	v1 = Vector([2, 4, 6])
	v2 = Vector([1, 2, 3])
	multiplied = v1 * v2
	divided = v1 / v2

	assert np.allclose(multiplied.data, np.array([2.0, 8.0, 18.0]))
	assert np.allclose(divided.data, np.array([2.0, 2.0, 2.0]))


def test_vector_repr_and_str():
	v = Vector([1, 2, 3])
	assert repr(v).startswith("Vector(")
	assert str(v) == "[1 2 3]"
	assert np.allclose(v.data, np.array([1, 2, 3]))


def test_vector_radd_rsub_rmul():
	v1 = Vector([1, 2, 3])
	v2 = Vector([4, 5, 6])

	radd_result = v1.__radd__(v2)
	rsub_result = v1.__rsub__(v2)
	rmul_result = v1.__rmul__(v2)

	assert isinstance(radd_result, Vector)
	assert isinstance(rsub_result, Vector)
	assert isinstance(rmul_result, Vector)
	assert np.allclose(radd_result.data, np.array([5, 7, 9]))
	assert np.allclose(rsub_result.data, np.array([3, 3, 3]))
	assert np.allclose(rmul_result.data, np.array([4, 10, 18]))


def test_vector_rtruediv():
	v1 = Vector([2, 4, 6])
	v2 = Vector([4, 8, 12])

	rtruediv_result = v1.__rtruediv__(v2)

	assert isinstance(rtruediv_result, Vector)
	assert np.allclose(rtruediv_result.data, np.array([2.0, 2.0, 2.0]))


def test_vector_iadd_isub_imul():
	v1 = Vector([1, 2, 3])
	v2 = Vector([4, 5, 6])
	orig_id = id(v1.data)

	result = v1.__iadd__(v2)
	assert result is v1
	assert np.allclose(v1.data, np.array([5, 7, 9]))

	v1 = Vector([10, 15, 20])
	v2 = Vector([2, 3, 4])
	result = v1.__isub__(v2)
	assert result is v1
	assert np.allclose(v1.data, np.array([8, 12, 16]))

	v1 = Vector([2, 3, 4])
	v2 = Vector([5, 6, 7])
	result = v1.__imul__(v2)
	assert result is v1
	assert np.allclose(v1.data, np.array([10, 18, 28]))


def test_vector_itruediv():
	v1 = Vector([10, 20, 30])
	v2 = Vector([2, 4, 5])

	result = v1.__itruediv__(v2)
	assert result is v1
	assert np.allclose(v1.data, np.array([5.0, 5.0, 6.0]))


def test_vector_polar_accepts_degrees_for_theta():
	v = Vector([2, 90], type="polar", degrees=True)
	assert np.allclose(v.data, np.array([0.0, 2.0]), atol=1e-9)


def test_vector_spherical_accepts_degrees_for_theta_phi():
	v = Vector([1, 90, 0], type="spherical", degrees=True)
	assert np.allclose(v.data, np.array([1.0, 0.0, 0.0]), atol=1e-9)


def test_vector_cylindrical_accepts_degrees_for_phi():
	v = Vector([2, 90, 3], type="cylindrical", degrees=True)
	assert np.allclose(v.data, np.array([0.0, 2.0, 3.0]), atol=1e-9)



