"""
Integration tests for the language preference PATCH endpoint.

Tests:
  - PATCH /api/portal/users/me/language with valid language returns 200
  - PATCH with unsupported language returns 400
  - PATCH to "pt" then GET /api/portal/auth/verify includes language="pt"
  - PATCH without auth returns 401

Uses the same pattern as existing integration tests:
  - Session override via app.dependency_overrides
  - X-Portal-User-Id / X-Portal-User-Role header injection for auth
  - db_session fixture from tests/conftest.py (Alembic migrations applied)
"""

from __future__ import annotations

import uuid

import bcrypt
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
from shared.models.auth import PortalUser


# ---------------------------------------------------------------------------
# App factory
# ---------------------------------------------------------------------------


def make_app(session: AsyncSession) -> FastAPI:
    """Build a minimal FastAPI test app with portal router."""
    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


# ---------------------------------------------------------------------------
# Header helpers
# ---------------------------------------------------------------------------


def auth_headers(user_id: uuid.UUID, role: str = "customer_admin") -> dict[str, str]:
    return {
        "X-Portal-User-Id": str(user_id),
        "X-Portal-User-Role": role,
    }


# ---------------------------------------------------------------------------
# DB helpers
# ---------------------------------------------------------------------------


async def _create_user(
    session: AsyncSession,
    role: str = "customer_admin",
    language: str = "en",
) -> PortalUser:
    suffix = uuid.uuid4().hex[:8]
    hashed = bcrypt.hashpw(b"testpassword123", bcrypt.gensalt()).decode()
    user = PortalUser(
        id=uuid.uuid4(),
        email=f"langtest-{suffix}@example.com",
        hashed_password=hashed,
        name=f"Language Test User {suffix}",
        role=role,
        language=language,
    )
    session.add(user)
    await session.flush()
    return user


# ---------------------------------------------------------------------------
# Fixtures
# ---------------------------------------------------------------------------


@pytest_asyncio.fixture
async def lang_client(db_session: AsyncSession) -> AsyncClient:
    """HTTP client with portal router mounted."""
    app = make_app(db_session)
    async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as c:
        yield c


@pytest_asyncio.fixture
async def lang_user(db_session: AsyncSession) -> PortalUser:
    """Create a customer_admin user with default language 'en'."""
    user = await _create_user(db_session, role="customer_admin", language="en")
    await db_session.commit()
    return user


# ---------------------------------------------------------------------------
# Tests
# ---------------------------------------------------------------------------


@pytest.mark.asyncio
async def test_patch_language_valid(
    lang_client: AsyncClient,
    lang_user: PortalUser,
) -> None:
    """PATCH /api/portal/users/me/language with valid language returns 200 and new language."""
    response = await lang_client.patch(
        "/api/portal/users/me/language",
        json={"language": "es"},
        headers=auth_headers(lang_user.id),
    )

    assert response.status_code == 200
    data = response.json()
    assert data["language"] == "es"


@pytest.mark.asyncio
async def test_patch_language_invalid(
    lang_client: AsyncClient,
    lang_user: PortalUser,
) -> None:
    """PATCH with unsupported language 'fr' returns 400."""
    response = await lang_client.patch(
        "/api/portal/users/me/language",
        json={"language": "fr"},
        headers=auth_headers(lang_user.id),
    )

    assert response.status_code == 400
    assert "fr" in response.json()["detail"].lower() or "unsupported" in response.json()["detail"].lower()


@pytest.mark.asyncio
async def test_patch_language_persists(
    lang_client: AsyncClient,
    lang_user: PortalUser,
    db_session: AsyncSession,
) -> None:
    """PATCH to 'pt', then GET /api/portal/auth/verify includes language='pt'."""
    # First PATCH to "pt"
    patch_response = await lang_client.patch(
        "/api/portal/users/me/language",
        json={"language": "pt"},
        headers=auth_headers(lang_user.id),
    )
    assert patch_response.status_code == 200
    assert patch_response.json()["language"] == "pt"

    # Verify via /auth/verify — need to pass email+password
    # Re-fetch user to get credentials, then call auth/verify
    verify_response = await lang_client.post(
        "/api/portal/auth/verify",
        json={"email": lang_user.email, "password": "testpassword123"},
    )
    assert verify_response.status_code == 200
    verify_data = verify_response.json()
    assert verify_data["language"] == "pt", (
        f"Expected language='pt' in auth/verify response, got: {verify_data.get('language')!r}"
    )


@pytest.mark.asyncio
async def test_patch_language_unauthenticated(
    lang_client: AsyncClient,
) -> None:
    """PATCH without auth headers returns 401 or 422 (missing required headers)."""
    response = await lang_client.patch(
        "/api/portal/users/me/language",
        json={"language": "es"},
    )

    # FastAPI raises 422 when required headers are missing entirely (before auth guard runs).
    # Both 401 and 422 are acceptable rejections of unauthenticated requests.
    assert response.status_code in (401, 422)
