"""
Test catalog and reference range endpoints.

Covers test categories, individual lab tests, test parameters, and reference ranges.
"""

from typing import Annotated
from uuid import UUID

from fastapi import APIRouter, Depends, Query, status
from sqlalchemy.ext.asyncio import AsyncSession

from app.core.auth_deps import require_permission
from app.core.dependencies import get_db
from app.core.pagination import PaginationParams
from app.core.permissions import PermissionCode
from app.core.responses import PaginatedResponse, SuccessResponse
from app.schemas.test import (
    SeedCatalogRequest,
    TestCategoryCreate,
    TestCategoryResponse,
    TestCategoryUpdate,
    TestCreate,
    TestDetailResponse,
    TestParameterCreate,
    TestParameterRangeCreate,
    TestParameterRangeResponse,
    TestParameterRangeUpdate,
    TestParameterResponse,
    TestParameterUpdate,
    TestResponse,
    TestUpdate,
)
from app.services.test_service import TestCatalogService

router = APIRouter(tags=["Tests"])
_service = TestCatalogService()


# ── Test Categories ───────────────────────────────────────────────────────────


@router.get(
    "/test-categories",
    response_model=PaginatedResponse[TestCategoryResponse],
    status_code=status.HTTP_200_OK,
    summary="List test categories",
    description="Retrieve test categories (e.g. Hematology, Biochemistry).",
    dependencies=[Depends(require_permission(PermissionCode.TEST_READ))],
)
async def list_test_categories(
    db: Annotated[AsyncSession, Depends(get_db)],
    pagination: Annotated[PaginationParams, Depends()],
    lab_id: UUID | None = Query(None, description="Filter by lab branch ID"),
) -> PaginatedResponse[TestCategoryResponse]:
    return await _service.list_categories(db, pagination, lab_id=lab_id)


@router.post(
    "/test-categories",
    response_model=SuccessResponse[TestCategoryResponse],
    status_code=status.HTTP_201_CREATED,
    summary="Create test category",
    description="Create a new test category.",
    dependencies=[Depends(require_permission(PermissionCode.TEST_WRITE))],
)
async def create_test_category(
    data: TestCategoryCreate,
    db: Annotated[AsyncSession, Depends(get_db)],
) -> SuccessResponse[TestCategoryResponse]:
    cat = await _service.create_category(db, data)
    return SuccessResponse(data=cat, message="Test category created successfully.")


@router.get(
    "/test-categories/{category_id}",
    response_model=SuccessResponse[TestCategoryResponse],
    status_code=status.HTTP_200_OK,
    summary="Get test category details",
    description="Retrieve details of a specific test category.",
    dependencies=[Depends(require_permission(PermissionCode.TEST_READ))],
)
async def get_test_category(
    category_id: UUID,
    db: Annotated[AsyncSession, Depends(get_db)],
) -> SuccessResponse[TestCategoryResponse]:
    cat = await _service.get_category(db, category_id)
    return SuccessResponse(data=cat)


@router.patch(
    "/test-categories/{category_id}",
    response_model=SuccessResponse[TestCategoryResponse],
    status_code=status.HTTP_200_OK,
    summary="Update test category",
    description="Update an existing test category.",
    dependencies=[Depends(require_permission(PermissionCode.TEST_WRITE))],
)
async def update_test_category(
    category_id: UUID,
    data: TestCategoryUpdate,
    db: Annotated[AsyncSession, Depends(get_db)],
) -> SuccessResponse[TestCategoryResponse]:
    cat = await _service.update_category(db, category_id, data)
    return SuccessResponse(data=cat, message="Test category updated successfully.")


@router.delete(
    "/test-categories/{category_id}",
    response_model=SuccessResponse[dict[str, str]],
    status_code=status.HTTP_200_OK,
    summary="Delete test category",
    description="Soft delete a test category.",
    dependencies=[Depends(require_permission(PermissionCode.TEST_WRITE))],
)
async def delete_test_category(
    category_id: UUID,
    db: Annotated[AsyncSession, Depends(get_db)],
) -> SuccessResponse[dict[str, str]]:
    await _service.delete_category(db, category_id)
    return SuccessResponse(
        data={"id": str(category_id)},
        message="Test category deleted successfully.",
    )


# ── Bulk Seed ───────────────────────────────────────────────────────────────────
# NOTE: registered before "/tests/{test_id}" routes so "seed" is never
# captured as a path parameter.


@router.post(
    "/tests/seed",
    response_model=SuccessResponse[dict[str, int]],
    status_code=status.HTTP_201_CREATED,
    summary="Bulk seed standard catalog",
    description=(
        "Idempotent full-catalog upsert (categories → tests → parameters → ranges)"
        " in one request. Re-running only fills gaps."
    ),
    dependencies=[Depends(require_permission(PermissionCode.TEST_WRITE))],
)
async def seed_standard_catalog(
    data: SeedCatalogRequest,
    db: Annotated[AsyncSession, Depends(get_db)],
) -> SuccessResponse[dict[str, int]]:
    counts = await _service.seed_standard_catalog(db, data)
    return SuccessResponse(data=counts, message="Standard catalog seeded successfully.")


# ── Tests ─────────────────────────────────────────────────────────────────────


@router.get(
    "/tests",
    response_model=PaginatedResponse[TestResponse],
    status_code=status.HTTP_200_OK,
    summary="List tests",
    description="Retrieve paginated list of catalog tests.",
    dependencies=[Depends(require_permission(PermissionCode.TEST_READ))],
)
async def list_tests(
    db: Annotated[AsyncSession, Depends(get_db)],
    pagination: Annotated[PaginationParams, Depends()],
    lab_id: UUID | None = Query(None, description="Filter by lab branch ID"),
    category_id: UUID | None = Query(None, description="Filter by category ID"),
    q: str | None = Query(None, description="Search query across test name or code"),
) -> PaginatedResponse[TestResponse]:
    return await _service.list_tests(db, pagination, lab_id=lab_id, category_id=category_id, q=q)


@router.post(
    "/tests",
    response_model=SuccessResponse[TestResponse],
    status_code=status.HTTP_201_CREATED,
    summary="Create test",
    description="Create a new laboratory test in the catalog.",
    dependencies=[Depends(require_permission(PermissionCode.TEST_WRITE))],
)
async def create_test(
    data: TestCreate,
    db: Annotated[AsyncSession, Depends(get_db)],
) -> SuccessResponse[TestResponse]:
    test = await _service.create_test(db, data)
    return SuccessResponse(data=test, message="Test created successfully.")


@router.get(
    "/tests/{test_id}",
    response_model=SuccessResponse[TestDetailResponse],
    status_code=status.HTTP_200_OK,
    summary="Get test details",
    description="Retrieve details for a specific test including parameters.",
    dependencies=[Depends(require_permission(PermissionCode.TEST_READ))],
)
async def get_test(
    test_id: UUID,
    db: Annotated[AsyncSession, Depends(get_db)],
) -> SuccessResponse[TestDetailResponse]:
    test = await _service.get_test(db, test_id)
    return SuccessResponse(data=test)


@router.patch(
    "/tests/{test_id}",
    response_model=SuccessResponse[TestResponse],
    status_code=status.HTTP_200_OK,
    summary="Update test",
    description="Update a test definition or price.",
    dependencies=[Depends(require_permission(PermissionCode.TEST_WRITE))],
)
async def update_test(
    test_id: UUID,
    data: TestUpdate,
    db: Annotated[AsyncSession, Depends(get_db)],
) -> SuccessResponse[TestResponse]:
    test = await _service.update_test(db, test_id, data)
    return SuccessResponse(data=test, message="Test updated successfully.")


@router.delete(
    "/tests/{test_id}",
    response_model=SuccessResponse[dict[str, str]],
    status_code=status.HTTP_200_OK,
    summary="Delete test",
    description="Soft delete a test from the catalog.",
    dependencies=[Depends(require_permission(PermissionCode.TEST_WRITE))],
)
async def delete_test(
    test_id: UUID,
    db: Annotated[AsyncSession, Depends(get_db)],
) -> SuccessResponse[dict[str, str]]:
    await _service.delete_test(db, test_id)
    return SuccessResponse(data={"id": str(test_id)}, message="Test deleted successfully.")


# ── Parameters ────────────────────────────────────────────────────────────────


@router.get(
    "/tests/{test_id}/parameters",
    response_model=SuccessResponse[list[TestParameterResponse]],
    status_code=status.HTTP_200_OK,
    summary="List test parameters",
    description="Retrieve parameters defined for a specific test.",
    dependencies=[Depends(require_permission(PermissionCode.TEST_READ))],
)
async def list_test_parameters(
    test_id: UUID,
    db: Annotated[AsyncSession, Depends(get_db)],
) -> SuccessResponse[list[TestParameterResponse]]:
    params = await _service.list_parameters(db, test_id)
    return SuccessResponse(data=params)


@router.post(
    "/tests/{test_id}/parameters",
    response_model=SuccessResponse[TestParameterResponse],
    status_code=status.HTTP_201_CREATED,
    summary="Add parameter to test",
    description="Add a measured parameter to a test.",
    dependencies=[Depends(require_permission(PermissionCode.TEST_WRITE))],
)
async def create_test_parameter(
    test_id: UUID,
    data: TestParameterCreate,
    db: Annotated[AsyncSession, Depends(get_db)],
) -> SuccessResponse[TestParameterResponse]:
    param = await _service.create_parameter(db, test_id, data)
    return SuccessResponse(data=param, message="Parameter added successfully.")


@router.get(
    "/tests/{test_id}/parameters/{param_id}",
    response_model=SuccessResponse[TestParameterResponse],
    status_code=status.HTTP_200_OK,
    summary="Get test parameter details",
    description="Retrieve parameter details.",
    dependencies=[Depends(require_permission(PermissionCode.TEST_READ))],
)
async def get_test_parameter(
    test_id: UUID,
    param_id: UUID,
    db: Annotated[AsyncSession, Depends(get_db)],
) -> SuccessResponse[TestParameterResponse]:
    param = await _service.get_parameter(db, test_id, param_id)
    return SuccessResponse(data=param)


@router.patch(
    "/tests/{test_id}/parameters/{param_id}",
    response_model=SuccessResponse[TestParameterResponse],
    status_code=status.HTTP_200_OK,
    summary="Update test parameter",
    description="Update a test parameter definition.",
    dependencies=[Depends(require_permission(PermissionCode.TEST_WRITE))],
)
async def update_test_parameter(
    test_id: UUID,
    param_id: UUID,
    data: TestParameterUpdate,
    db: Annotated[AsyncSession, Depends(get_db)],
) -> SuccessResponse[TestParameterResponse]:
    param = await _service.update_parameter(db, test_id, param_id, data)
    return SuccessResponse(data=param, message="Parameter updated successfully.")


@router.delete(
    "/tests/{test_id}/parameters/{param_id}",
    response_model=SuccessResponse[dict[str, str]],
    status_code=status.HTTP_200_OK,
    summary="Delete test parameter",
    description="Delete a parameter from a test.",
    dependencies=[Depends(require_permission(PermissionCode.TEST_WRITE))],
)
async def delete_test_parameter(
    test_id: UUID,
    param_id: UUID,
    db: Annotated[AsyncSession, Depends(get_db)],
) -> SuccessResponse[dict[str, str]]:
    await _service.delete_parameter(db, test_id, param_id)
    return SuccessResponse(
        data={"id": str(param_id)},
        message="Parameter deleted successfully.",
    )


# ── Reference Ranges ──────────────────────────────────────────────────────────


@router.get(
    "/tests/{test_id}/parameters/{param_id}/ranges",
    response_model=SuccessResponse[list[TestParameterRangeResponse]],
    status_code=status.HTTP_200_OK,
    summary="List reference ranges",
    description="Retrieve reference ranges for a test parameter.",
    dependencies=[Depends(require_permission(PermissionCode.TEST_READ))],
)
async def list_reference_ranges(
    test_id: UUID,
    param_id: UUID,
    db: Annotated[AsyncSession, Depends(get_db)],
) -> SuccessResponse[list[TestParameterRangeResponse]]:
    ranges = await _service.list_ranges(db, test_id, param_id)
    return SuccessResponse(data=ranges)


@router.post(
    "/tests/{test_id}/parameters/{param_id}/ranges",
    response_model=SuccessResponse[TestParameterRangeResponse],
    status_code=status.HTTP_201_CREATED,
    summary="Create reference range",
    description="Add age/gender-specific normal reference range for a parameter.",
    dependencies=[Depends(require_permission(PermissionCode.TEST_WRITE))],
)
async def create_reference_range(
    test_id: UUID,
    param_id: UUID,
    data: TestParameterRangeCreate,
    db: Annotated[AsyncSession, Depends(get_db)],
) -> SuccessResponse[TestParameterRangeResponse]:
    rng = await _service.create_range(db, test_id, param_id, data)
    return SuccessResponse(data=rng, message="Reference range created successfully.")


@router.get(
    "/tests/{test_id}/parameters/{param_id}/ranges/{range_id}",
    response_model=SuccessResponse[TestParameterRangeResponse],
    status_code=status.HTTP_200_OK,
    summary="Get reference range",
    description="Retrieve details of a reference range.",
    dependencies=[Depends(require_permission(PermissionCode.TEST_READ))],
)
async def get_reference_range(
    test_id: UUID,
    param_id: UUID,
    range_id: UUID,
    db: Annotated[AsyncSession, Depends(get_db)],
) -> SuccessResponse[TestParameterRangeResponse]:
    rng = await _service.get_range(db, test_id, param_id, range_id)
    return SuccessResponse(data=rng)


@router.patch(
    "/tests/{test_id}/parameters/{param_id}/ranges/{range_id}",
    response_model=SuccessResponse[TestParameterRangeResponse],
    status_code=status.HTTP_200_OK,
    summary="Update reference range",
    description="Update a reference range.",
    dependencies=[Depends(require_permission(PermissionCode.TEST_WRITE))],
)
async def update_reference_range(
    test_id: UUID,
    param_id: UUID,
    range_id: UUID,
    data: TestParameterRangeUpdate,
    db: Annotated[AsyncSession, Depends(get_db)],
) -> SuccessResponse[TestParameterRangeResponse]:
    rng = await _service.update_range(db, test_id, param_id, range_id, data)
    return SuccessResponse(data=rng, message="Reference range updated successfully.")


@router.delete(
    "/tests/{test_id}/parameters/{param_id}/ranges/{range_id}",
    response_model=SuccessResponse[dict[str, str]],
    status_code=status.HTTP_200_OK,
    summary="Delete reference range",
    description="Delete a reference range.",
    dependencies=[Depends(require_permission(PermissionCode.TEST_WRITE))],
)
async def delete_reference_range(
    test_id: UUID,
    param_id: UUID,
    range_id: UUID,
    db: Annotated[AsyncSession, Depends(get_db)],
) -> SuccessResponse[dict[str, str]]:
    await _service.delete_range(db, test_id, param_id, range_id)
    return SuccessResponse(
        data={"id": str(range_id)},
        message="Reference range deleted successfully.",
    )
