from typing import Annotated
from uuid import UUID

from fastapi import APIRouter, Depends, HTTPException, Query, Response, status
from psycopg import Connection
from pydantic import BaseModel, Field
from src.__types.error_responses import CONFLICT_DETAIL, NOT_FOUND_DETAIL, ErrorCode, ErrorResponse, InvalidParameter
from src.__types.errors import ResourceDuplicatedError, ResourceNotFoundError
from src.__types.index import Tenant, User, UserWriteAttribute
from src.const import MANAGEMENT_API_BASE_RESOURCE
from src.logger import logger
from src.routers.management.authorizer import get_tenant, get_user_id
from src.routers.management.connection import get_connection
from src.usecases.user_command_usecase import UserCommandUseCase
from src.usecases.user_query_usecase import UserQueryUseCase

router = APIRouter(
    prefix="/users",
    tags=["ユーザー管理"],
)


# モデル定義


class UserDeleteRequest(BaseModel):
    user_ids: list[UUID] = Field(
        ...,
        description="削除するユーザーIDのリスト",
        min_length=1,
        max_length=100,
    )


class PaginationLinks(BaseModel):
    first: str | None = Field(..., description="先頭ページのリンク。現在のページが先頭ページの場合はnull")
    prev: str | None = Field(..., description="前ページのリンク。現在のページが先頭ページ以前の場合はnull")
    next: str | None = Field(..., description="次ページのリンク。現在のページが最終ページ以降の場合はnull")
    last: str | None = Field(..., description="最終ページのリンク。現在のページが最終ページの場合はnull")

    @classmethod
    def build(cls, *, page: int, page_size: int, total: int) -> "PaginationLinks":
        base_path = f"{MANAGEMENT_API_BASE_RESOURCE}/users"
        last_page = (total + page_size - 1) // page_size if total > 0 else 1
        return cls(
            first=f"{base_path}?page=1&page_size={page_size}" if page != 1 else None,
            prev=f"{base_path}?page={page - 1}&page_size={page_size}" if page > 1 else None,
            next=f"{base_path}?page={page + 1}&page_size={page_size}" if page < last_page else None,
            last=f"{base_path}?page={last_page}&page_size={page_size}" if page != last_page else None,
        )


class PaginationMeta(BaseModel):
    page: int = Field(..., description="現在のページ")
    page_size: int = Field(..., description="1ページあたりの件数")
    total: int = Field(..., description="全件数")


class GetResponse(BaseModel):
    links: PaginationLinks = Field(..., description="ページネーションのリンク情報")
    meta: PaginationMeta = Field(..., description="ページネーションのメタ情報")
    data: list[User] = Field(..., description="ユーザーデータのリスト")

    model_config = {
        "json_schema_extra": {
            "examples": [
                {
                    "links": {
                        "first": f"{MANAGEMENT_API_BASE_RESOURCE}/users?page=1&page_size=10",
                        "prev": None,
                        "next": f"{MANAGEMENT_API_BASE_RESOURCE}/users?page=2&page_size=10",
                        "last": f"{MANAGEMENT_API_BASE_RESOURCE}/users?page=3&page_size=10",
                    },
                    "meta": {
                        "page": 1,
                        "page_size": 10,
                        "total": 23,
                    },
                    "data": [
                        {
                            "id": "11111111-1111-1111-1111-000000000001",
                            "display_id": "U0001",
                            "display_name": "Mock User 1",
                            "email": "mock.user1@example.com",
                        },
                    ],
                },
            ],
        },
    }


# ルーティング
@router.post(
    "",
    summary="作成",
    description="ユーザーを作成します。",
    status_code=status.HTTP_201_CREATED,
    responses={
        status.HTTP_201_CREATED: {
            "description": "ユーザーの作成に成功",
        },
        status.HTTP_409_CONFLICT: {
            "description": "display_id または email が既存ユーザーと重複している",
            "model": ErrorResponse,
        },
    },
)
def create_user(
    tenant: Annotated[Tenant, Depends(get_tenant)],
    connection: Annotated[Connection, Depends(get_connection)],
    request: UserWriteAttribute,
) -> Response:
    usecase = UserCommandUseCase.create(tenant=tenant, connection=connection)
    try:
        usecase.add(display_id=request.display_id, display_name=request.display_name, email=request.email)

    except ResourceDuplicatedError as e:
        logger.warning("User already exists.", exc_info=e, tenant_id=tenant.id)
        invalid_parameters = []
        if e.duplicated_field:
            invalid_parameters.append(InvalidParameter(name=e.duplicated_field, code=ErrorCode.ALREADY_EXISTS))

        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail=ErrorResponse(
                detail=CONFLICT_DETAIL,
                invalid_parameters=invalid_parameters,
            ).model_dump(),
        ) from e

    return Response(status_code=status.HTTP_201_CREATED)


@router.get(
    "",
    summary="一覧取得",
    description="ユーザー一覧を取得します。",
)
def list_users(
    tenant: Annotated[Tenant, Depends(get_tenant)],
    connection: Annotated[Connection, Depends(get_connection)],
    page: Annotated[int, Query(description="ページ番号", ge=1)] = 1,
    page_size: Annotated[int, Query(description="1ページあたりの件数", ge=1, le=100)] = 10,
) -> GetResponse:
    usecase = UserQueryUseCase.create(tenant=tenant, connection=connection)
    result = usecase.list(page=page, page_size=page_size)

    links = PaginationLinks.build(
        page=page,
        page_size=page_size,
        total=result.total,
    )
    return GetResponse.model_validate(
        {
            "links": links,
            "meta": {
                "page": page,
                "page_size": page_size,
                "total": result.total,
            },
            "data": result.items,
        },
    )


@router.get(
    "/me",
    summary="自身の情報取得",
    description="認証情報に基づいて、自身のユーザー情報を取得します。",
    responses={
        status.HTTP_403_FORBIDDEN: {
            "description": "ユーザー専用トークン以外でアクセスした場合",
            "model": ErrorResponse,
        },
        status.HTTP_404_NOT_FOUND: {
            "description": "ユーザーが存在しない",
            "model": ErrorResponse,
        },
    },
)
def get_current_user(
    tenant: Annotated[Tenant, Depends(get_tenant)],
    connection: Annotated[Connection, Depends(get_connection)],
    current_user_id: Annotated[UUID, Depends(get_user_id)],
) -> User:
    usecase = UserQueryUseCase.create(tenant=tenant, connection=connection)
    try:
        return usecase.get(user_id=current_user_id)

    except ResourceNotFoundError as e:
        logger.warning("User not found.", exc_info=e, user_id=current_user_id, tenant_id=tenant.id)

        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=ErrorResponse(detail=NOT_FOUND_DETAIL).model_dump(),
        ) from e


@router.get(
    "/{user_id}",
    summary="詳細取得",
    description="ユーザーの詳細情報を取得します。",
    responses={
        status.HTTP_404_NOT_FOUND: {
            "description": "ユーザーが存在しない",
            "model": ErrorResponse,
        },
    },
)
def get_user(
    tenant: Annotated[Tenant, Depends(get_tenant)],
    connection: Annotated[Connection, Depends(get_connection)],
    user_id: UUID,
) -> User:
    usecase = UserQueryUseCase.create(tenant=tenant, connection=connection)
    try:
        return usecase.get(user_id=user_id)

    except ResourceNotFoundError as e:
        logger.warning("User not found.", exc_info=e, user_id=user_id, tenant_id=tenant.id)

        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=ErrorResponse(detail=NOT_FOUND_DETAIL).model_dump(),
        ) from e


@router.put(
    "/{user_id}",
    summary="更新",
    description="ユーザーを更新します。",
    status_code=status.HTTP_204_NO_CONTENT,
    responses={
        status.HTTP_204_NO_CONTENT: {
            "description": "ユーザーの更新に成功",
        },
        status.HTTP_404_NOT_FOUND: {
            "description": "ユーザーが存在しない",
            "model": ErrorResponse,
        },
        status.HTTP_409_CONFLICT: {
            "description": "display_id または email が既存ユーザーと重複している",
            "model": ErrorResponse,
        },
    },
)
def update_user(
    tenant: Annotated[Tenant, Depends(get_tenant)],
    connection: Annotated[Connection, Depends(get_connection)],
    user_id: UUID,
    request: UserWriteAttribute,
) -> Response:
    usecase = UserCommandUseCase.create(tenant=tenant, connection=connection)
    try:
        usecase.update(
            user_id=user_id,
            display_id=request.display_id,
            display_name=request.display_name,
            email=request.email,
        )

    except ResourceNotFoundError as e:
        logger.warning("User not found.", exc_info=e, user_id=user_id, tenant_id=tenant.id)
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=ErrorResponse(detail=NOT_FOUND_DETAIL).model_dump(),
        ) from e

    except ResourceDuplicatedError as e:
        logger.warning("User already exists.", exc_info=e, user_id=user_id, tenant_id=tenant.id)
        invalid_parameters = []
        if e.duplicated_field:
            invalid_parameters.append(InvalidParameter(name=e.duplicated_field, code=ErrorCode.ALREADY_EXISTS))

        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail=ErrorResponse(
                detail=CONFLICT_DETAIL,
                invalid_parameters=invalid_parameters,
            ).model_dump(),
        ) from e

    return Response(status_code=status.HTTP_204_NO_CONTENT)


@router.delete(
    "",
    summary="一括削除",
    description="ユーザーを一括で削除します。",
    status_code=status.HTTP_204_NO_CONTENT,
    responses={
        status.HTTP_204_NO_CONTENT: {
            "description": "ユーザーの一括削除に成功",
        },
        status.HTTP_400_BAD_REQUEST: {
            "description": "ユーザー自身を削除しようとした場合",
            "model": ErrorResponse,
        },
    },
)
def delete_users(
    tenant: Annotated[Tenant, Depends(get_tenant)],
    connection: Annotated[Connection, Depends(get_connection)],
    request: UserDeleteRequest,
    current_user_id: Annotated[UUID, Depends(get_user_id)],
) -> Response:
    logger.info("user bulk delete request.", tenant_id=tenant.id, request=request.model_dump())

    if current_user_id in request.user_ids:
        logger.warning("User requested to delete itself.", tenant_id=tenant.id, user_id=current_user_id)
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=ErrorResponse(detail="Cannot delete the user itself.").model_dump(),
        )

    usecase = UserCommandUseCase.create(tenant=tenant, connection=connection)
    usecase.bulk_delete(user_ids=request.user_ids)
    return Response(status_code=status.HTTP_204_NO_CONTENT)
