from __future__ import annotations

from types import SimpleNamespace
from unittest.mock import AsyncMock

import pytest

from zoho_projects_sdk.api.projects import ProjectsAPI


@pytest.mark.asyncio
async def test_get_all_returns_validated_projects() -> None:
    client = SimpleNamespace(
        portal_id="portal-123",
        get=AsyncMock(
            return_value={
                "projects": [
                    {
                        "id": 101,
                        "name": "Sample Project",
                        "status": "active",
                    }
                ]
            }
        ),
    )

    projects = await ProjectsAPI(client).get_all(page=2, per_page=50)

    assert len(projects) == 1
    assert projects[0].id == 101
    assert projects[0].name == "Sample Project"
    client.get.assert_awaited_once_with(
        "/portal/portal-123/projects",
        params={"page": 2, "per_page": 50},
    )


@pytest.mark.asyncio
async def test_get_returns_empty_project_when_not_found() -> None:
    client = SimpleNamespace(
        portal_id="portal-123",
        get=AsyncMock(return_value={"projects": []}),
    )

    project = await ProjectsAPI(client).get(project_id=42)

    assert project.id == 0
    assert project.name == ""
    assert project.status == "active"
    client.get.assert_awaited_once_with(
        "/portal/portal-123/projects/42",
    )


@pytest.mark.asyncio
async def test_get_returns_first_project_when_present() -> None:
    client = SimpleNamespace(
        portal_id="portal-123",
        get=AsyncMock(
            return_value={
                "projects": [
                    {
                        "id": 7,
                        "name": "Primary",
                        "status": "active",
                    },
                    {
                        "id": 8,
                        "name": "Secondary",
                        "status": "on hold",
                    },
                ]
            }
        ),
    )

    project = await ProjectsAPI(client).get(project_id=7)

    assert project.id == 7
    assert project.name == "Primary"
    client.get.assert_awaited_once_with(
        "/portal/portal-123/projects/7",
    )


@pytest.mark.asyncio
async def test_portal_id_missing_raises_value_error() -> None:
    client = SimpleNamespace(portal_id=None, get=AsyncMock())
    projects_api = ProjectsAPI(client)

    with pytest.raises(ValueError, match="Portal ID is not configured"):
        await projects_api.get_all()


@pytest.mark.asyncio
async def test_create_posts_payload_and_returns_project() -> None:
    client = SimpleNamespace(
        portal_id="portal-123",
        post=AsyncMock(
            return_value={
                "project": {
                    "id": 10,
                    "name": "Created",
                    "status": "active",
                }
            }
        ),
    )

    class ProjectPayload:
        def model_dump(self, *, by_alias: bool, exclude_none: bool) -> dict[str, object]:
            assert by_alias is True
            assert exclude_none is True
            return {"name": "Created", "status": "active"}

    result = await ProjectsAPI(client).create(project_data=ProjectPayload())

    assert result.id == 10
    assert result.name == "Created"
    client.post.assert_awaited_once_with(
        "/portal/portal-123/projects",
        json={"name": "Created", "status": "active"},
    )


@pytest.mark.asyncio
async def test_update_patches_payload_and_returns_project() -> None:
    client = SimpleNamespace(
        portal_id="portal-123",
        patch=AsyncMock(
            return_value={
                "project": {
                    "id": 10,
                    "name": "Updated",
                    "status": "inactive",
                }
            }
        ),
    )

    class ProjectPayload:
        def model_dump(self, *, by_alias: bool, exclude_none: bool) -> dict[str, object]:
            assert by_alias is True
            assert exclude_none is True
            return {"name": "Updated", "status": "inactive"}

    result = await ProjectsAPI(client).update(
        project_id=10,
        project_data=ProjectPayload(),
    )

    assert result.id == 10
    assert result.status == "inactive"
    client.patch.assert_awaited_once_with(
        "/portal/portal-123/projects/10",
        json={"name": "Updated", "status": "inactive"},
    )


@pytest.mark.asyncio
async def test_delete_invokes_client_delete_and_returns_true() -> None:
    client = SimpleNamespace(
        portal_id="portal-123",
        delete=AsyncMock(return_value=None),
    )

    result = await ProjectsAPI(client).delete(project_id=5)

    assert result is True
    client.delete.assert_awaited_once_with(
        "/portal/portal-123/projects/5",
    )
