import pytest

from servala.core.models import User

pytestmark = pytest.mark.django_db


@pytest.fixture
def user():
    return User.objects.create(email="testuser@example.org", password="test")


def test_create_user():
    user = User.objects.create_user(
        email="test@example.org",
        password="testpassword123",
        first_name="Test",
        last_name="User",
    )
    assert user.email == "test@example.org"
    assert user.first_name == "Test"
    assert user.last_name == "User"
    assert user.check_password("testpassword123")
    assert not user.is_staff
    assert not user.is_superuser


def test_create_user_normalizes_email():
    user = User.objects.create_user(
        email="  TEST@EXAMPLE.ORG  ",
        password="testpassword123",
    )
    assert user.email == "TEST@example.org"


def test_create_user_without_email_raises_error():
    with pytest.raises(ValueError, match="Please provide an email address"):
        User.objects.create_user(email="", password="testpassword123")


def test_create_superuser():
    user = User.objects.create_superuser(
        email="admin@example.org",
        password="adminpassword123",
    )
    assert user.email == "admin@example.org"
    assert user.check_password("adminpassword123")
    assert user.is_staff
    assert user.is_superuser


@pytest.mark.parametrize(
    "first_name,last_name,email,expected",
    [
        ("John", "Doe", "john@example.org", "John Doe"),
        ("John", "", "john@example.org", "John"),
        ("", "Doe", "john@example.org", "Doe"),
        ("", "", "john@example.org", "john@example.org"),
    ],
)
def test_user_str(first_name, last_name, email, expected):
    user = User(email=email, first_name=first_name, last_name=last_name)
    assert str(user) == expected


def test_normalize_username():
    user = User()
    assert user.normalize_username("  TEST@EXAMPLE.ORG  ") == "test@example.org"


def test_get_odoo_contact_returns_none_without_billing_entity(user, organization):
    assert organization.billing_entity is None
    result = user.get_odoo_contact(organization)
    assert result is None


def test_get_odoo_contact_returns_none_without_odoo_company_id(
    user, organization, billing_entity
):
    organization.billing_entity = billing_entity
    organization.save()
    assert billing_entity.odoo_company_id is None
    result = user.get_odoo_contact(organization)
    assert result is None


def test_get_odoo_contact_returns_contact_from_odoo(
    user, organization, billing_entity, mocker
):
    billing_entity.odoo_company_id = 123
    billing_entity.save()
    organization.billing_entity = billing_entity
    organization.save()

    mock_client = mocker.patch("servala.core.models.user.odoo.CLIENT")
    mock_client.search_read.return_value = [
        {"id": 456, "name": "Test User", "email": user.email}
    ]

    result = user.get_odoo_contact(organization)

    assert result == {"id": 456, "name": "Test User", "email": user.email}
    mock_client.search_read.assert_called_once()


def test_get_odoo_contact_returns_none_when_not_found(
    user, organization, billing_entity, mocker
):
    billing_entity.odoo_company_id = 123
    billing_entity.save()
    organization.billing_entity = billing_entity
    organization.save()

    mock_client = mocker.patch("servala.core.models.user.odoo.CLIENT")
    mock_client.search_read.return_value = []

    result = user.get_odoo_contact(organization)

    assert result is None


def test_get_or_create_odoo_contact_returns_none_without_billing_entity(
    user, organization
):
    assert organization.billing_entity is None
    result = user.get_or_create_odoo_contact(organization)
    assert result is None


def test_get_or_create_odoo_contact_returns_none_without_odoo_company_id(
    user, organization, billing_entity
):
    organization.billing_entity = billing_entity
    organization.save()
    assert billing_entity.odoo_company_id is None
    result = user.get_or_create_odoo_contact(organization)
    assert result is None


def test_get_or_create_odoo_contact_returns_existing_contact(
    user, organization, billing_entity, mocker
):
    billing_entity.odoo_company_id = 123
    billing_entity.save()
    organization.billing_entity = billing_entity
    organization.save()

    mock_client = mocker.patch("servala.core.models.user.odoo.CLIENT")
    mock_client.search_read.return_value = [{"id": 456, "name": "Test User"}]

    result = user.get_or_create_odoo_contact(organization)

    assert result == 456
    mock_client.execute.assert_not_called()


def test_get_or_create_odoo_contact_creates_new_contact(
    organization, billing_entity, mocker
):
    new_user = User.objects.create_user(
        email="newuser@example.org",
        password="test",
        first_name="New",
        last_name="User",
    )
    billing_entity.odoo_company_id = 123
    billing_entity.save()
    organization.billing_entity = billing_entity
    organization.save()

    mock_client = mocker.patch("servala.core.models.user.odoo.CLIENT")
    mock_client.search_read.return_value = []
    mock_client.execute.return_value = 789

    result = new_user.get_or_create_odoo_contact(organization)

    assert result == 789
    mock_client.execute.assert_called_once()
    call_args = mock_client.execute.call_args
    assert call_args[0][0] == "res.partner"
    assert call_args[0][1] == "create"
    partner_data = call_args[0][2][0]
    assert partner_data["name"] == "New User"
    assert partner_data["email"] == "newuser@example.org"
    assert partner_data["parent_id"] == 123


def test_get_or_create_odoo_contact_uses_email_as_name_fallback(
    organization, billing_entity, mocker
):
    new_user = User.objects.create_user(
        email="noname@example.org",
        password="test",
    )
    billing_entity.odoo_company_id = 123
    billing_entity.save()
    organization.billing_entity = billing_entity
    organization.save()

    mock_client = mocker.patch("servala.core.models.user.odoo.CLIENT")
    mock_client.search_read.return_value = []
    mock_client.execute.return_value = 789

    new_user.get_or_create_odoo_contact(organization)

    call_args = mock_client.execute.call_args
    partner_data = call_args[0][2][0]
    assert partner_data["name"] == "noname@example.org"
