"""
Integration tests for portal tenant CRUD endpoints (PRTA-01).

Tests: create, read, update, delete, list, validation, uniqueness constraints.
Uses httpx.AsyncClient with a live FastAPI app backed by the test PostgreSQL DB.
"""

from __future__ import annotations

import uuid
from typing import Any

import pytest
import pytest_asyncio
from fastapi import FastAPI
from httpx import ASGITransport, AsyncClient
from sqlalchemy.ext.asyncio import AsyncSession

from shared.api.portal import portal_router
from shared.db import get_session


def make_app(session: AsyncSession) -> FastAPI:
    """Build a FastAPI test app with the portal router and session override."""
    app = FastAPI()
    app.include_router(portal_router)

    async def override_get_session():  # type: ignore[return]
        yield session

    app.dependency_overrides[get_session] = override_get_session
    return app


@pytest_asyncio.fixture
async def client(db_session: AsyncSession):  # type: ignore[no-untyped-def]
    """HTTP client wired to the portal API with the test DB session."""
    app = make_app(db_session)
    async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as c:
        yield c


@pytest.mark.asyncio
class TestTenantCreate:
    async def test_create_tenant_valid_returns_201(self, client: AsyncClient) -> None:
        slug = f"acme-corp-{uuid.uuid4().hex[:6]}"
        resp = await client.post(
            "/api/portal/tenants",
            json={"name": f"Acme Corp {slug}", "slug": slug},
        )
        assert resp.status_code == 201
        data = resp.json()
        assert data["slug"] == slug
        assert "id" in data
        assert "created_at" in data

    async def test_create_tenant_with_settings(self, client: AsyncClient) -> None:
        slug = f"settings-test-{uuid.uuid4().hex[:6]}"
        resp = await client.post(
            "/api/portal/tenants",
            json={"name": f"Settings Test {slug}", "slug": slug, "settings": {"tier": "starter"}},
        )
        assert resp.status_code == 201
        assert resp.json()["settings"]["tier"] == "starter"

    async def test_create_tenant_duplicate_slug_returns_409(self, client: AsyncClient) -> None:
        slug = f"dup-slug-{uuid.uuid4().hex[:6]}"
        await client.post("/api/portal/tenants", json={"name": f"First {slug}", "slug": slug})
        resp = await client.post("/api/portal/tenants", json={"name": f"Second {slug}", "slug": slug})
        assert resp.status_code == 409

    async def test_create_tenant_duplicate_name_returns_409(self, client: AsyncClient) -> None:
        slug1 = f"name-dup-a-{uuid.uuid4().hex[:6]}"
        slug2 = f"name-dup-b-{uuid.uuid4().hex[:6]}"
        name = f"Duplicate Name {uuid.uuid4().hex[:6]}"
        await client.post("/api/portal/tenants", json={"name": name, "slug": slug1})
        resp = await client.post("/api/portal/tenants", json={"name": name, "slug": slug2})
        assert resp.status_code == 409

    async def test_create_tenant_invalid_slug_uppercase_returns_422(self, client: AsyncClient) -> None:
        resp = await client.post(
            "/api/portal/tenants",
            json={"name": "Bad Slug Company", "slug": "BadSlug"},
        )
        assert resp.status_code == 422

    async def test_create_tenant_invalid_slug_too_short_returns_422(self, client: AsyncClient) -> None:
        resp = await client.post(
            "/api/portal/tenants",
            json={"name": "Too Short Slug", "slug": "a"},
        )
        assert resp.status_code == 422

    async def test_create_tenant_invalid_slug_leading_hyphen_returns_422(self, client: AsyncClient) -> None:
        resp = await client.post(
            "/api/portal/tenants",
            json={"name": "Leading Hyphen Co", "slug": "-leading-hyphen"},
        )
        assert resp.status_code == 422

    async def test_create_tenant_name_too_short_returns_422(self, client: AsyncClient) -> None:
        resp = await client.post(
            "/api/portal/tenants",
            json={"name": "X", "slug": "valid-slug"},
        )
        assert resp.status_code == 422


@pytest.mark.asyncio
class TestTenantList:
    async def test_list_tenants_returns_created_tenants(self, client: AsyncClient) -> None:
        slug1 = f"list-a-{uuid.uuid4().hex[:6]}"
        slug2 = f"list-b-{uuid.uuid4().hex[:6]}"
        await client.post("/api/portal/tenants", json={"name": f"List A {slug1}", "slug": slug1})
        await client.post("/api/portal/tenants", json={"name": f"List B {slug2}", "slug": slug2})

        resp = await client.get("/api/portal/tenants")
        assert resp.status_code == 200
        data = resp.json()
        assert "items" in data
        assert "total" in data
        slugs = [t["slug"] for t in data["items"]]
        assert slug1 in slugs
        assert slug2 in slugs

    async def test_list_tenants_pagination(self, client: AsyncClient) -> None:
        # Create 3 tenants
        for i in range(3):
            slug = f"pag-{i}-{uuid.uuid4().hex[:6]}"
            await client.post("/api/portal/tenants", json={"name": f"Pag {i} {slug}", "slug": slug})

        resp = await client.get("/api/portal/tenants?page=1&page_size=2")
        assert resp.status_code == 200
        data = resp.json()
        assert len(data["items"]) <= 2
        assert data["page"] == 1
        assert data["page_size"] == 2


@pytest.mark.asyncio
class TestTenantGet:
    async def test_get_tenant_by_id_returns_correct_tenant(self, client: AsyncClient) -> None:
        slug = f"get-test-{uuid.uuid4().hex[:6]}"
        create_resp = await client.post(
            "/api/portal/tenants",
            json={"name": f"Get Test {slug}", "slug": slug},
        )
        tenant_id = create_resp.json()["id"]

        resp = await client.get(f"/api/portal/tenants/{tenant_id}")
        assert resp.status_code == 200
        assert resp.json()["id"] == tenant_id
        assert resp.json()["slug"] == slug

    async def test_get_tenant_not_found_returns_404(self, client: AsyncClient) -> None:
        resp = await client.get(f"/api/portal/tenants/{uuid.uuid4()}")
        assert resp.status_code == 404


@pytest.mark.asyncio
class TestTenantUpdate:
    async def test_update_tenant_name(self, client: AsyncClient) -> None:
        slug = f"upd-test-{uuid.uuid4().hex[:6]}"
        create_resp = await client.post(
            "/api/portal/tenants",
            json={"name": f"Original Name {slug}", "slug": slug},
        )
        tenant_id = create_resp.json()["id"]

        new_name = f"Updated Name {uuid.uuid4().hex[:6]}"
        resp = await client.put(
            f"/api/portal/tenants/{tenant_id}",
            json={"name": new_name},
        )
        assert resp.status_code == 200
        assert resp.json()["name"] == new_name
        assert resp.json()["slug"] == slug  # unchanged

    async def test_update_tenant_slug(self, client: AsyncClient) -> None:
        slug = f"old-slug-{uuid.uuid4().hex[:6]}"
        create_resp = await client.post(
            "/api/portal/tenants",
            json={"name": f"Slug Update Test {slug}", "slug": slug},
        )
        tenant_id = create_resp.json()["id"]

        new_slug = f"new-slug-{uuid.uuid4().hex[:6]}"
        resp = await client.put(
            f"/api/portal/tenants/{tenant_id}",
            json={"slug": new_slug},
        )
        assert resp.status_code == 200
        assert resp.json()["slug"] == new_slug

    async def test_update_tenant_not_found_returns_404(self, client: AsyncClient) -> None:
        resp = await client.put(f"/api/portal/tenants/{uuid.uuid4()}", json={"name": "Does Not Exist"})
        assert resp.status_code == 404


@pytest.mark.asyncio
class TestTenantDelete:
    async def test_delete_tenant_returns_204(self, client: AsyncClient) -> None:
        slug = f"del-test-{uuid.uuid4().hex[:6]}"
        create_resp = await client.post(
            "/api/portal/tenants",
            json={"name": f"Delete Test {slug}", "slug": slug},
        )
        tenant_id = create_resp.json()["id"]

        resp = await client.delete(f"/api/portal/tenants/{tenant_id}")
        assert resp.status_code == 204

    async def test_delete_tenant_is_gone_after_delete(self, client: AsyncClient) -> None:
        slug = f"del-gone-{uuid.uuid4().hex[:6]}"
        create_resp = await client.post(
            "/api/portal/tenants",
            json={"name": f"Delete Gone Test {slug}", "slug": slug},
        )
        tenant_id = create_resp.json()["id"]

        await client.delete(f"/api/portal/tenants/{tenant_id}")
        get_resp = await client.get(f"/api/portal/tenants/{tenant_id}")
        assert get_resp.status_code == 404

    async def test_delete_tenant_not_found_returns_404(self, client: AsyncClient) -> None:
        resp = await client.delete(f"/api/portal/tenants/{uuid.uuid4()}")
        assert resp.status_code == 404
