"""Review queue: CRUD operations for ReviewItem."""
from __future__ import annotations

import uuid
from typing import Any

from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from libs.common.logging import get_logger
from libs.common.time_utils import utc_now

logger = get_logger(__name__)

_PRIORITY_ORDER = {"P0": 0, "P1": 1, "P2": 2}


async def create_review_item(
    session: AsyncSession,
    entity_type: str,
    entity_id: str,
    priority: str,
    reason_codes: list[str],
    snapshot_refs: dict[str, Any],
    suggested_overrides: dict[str, Any] | None = None,
) -> Any:
    """Create a ReviewItem, or update an existing open one (deduplicated).

    Args:
        session: Async DB session.
        entity_type: "parser_event" | "feature_record" | "label_record".
        entity_id: ID of the entity to review.
        priority: "P0" | "P1" | "P2".
        reason_codes: List of reason code strings.
        snapshot_refs: Dict of snapshot references.
        suggested_overrides: Optional suggested field overrides.

    Returns:
        ReviewItem ORM instance.
    """
    from libs.db.models import ReviewItem

    # Check for existing open item
    result = await session.execute(
        select(ReviewItem).where(
            ReviewItem.entity_type == entity_type,
            ReviewItem.entity_id == entity_id,
            ReviewItem.status == "open",
        )
    )
    existing = result.scalar_one_or_none()

    if existing is not None:
        # Update existing: escalate priority if needed, merge reason_codes
        existing_priority_rank = _PRIORITY_ORDER.get(existing.priority, 99)
        new_priority_rank = _PRIORITY_ORDER.get(priority, 99)
        if new_priority_rank < existing_priority_rank:
            existing.priority = priority  # type: ignore[assignment]

        existing_codes: list[str] = list(existing.reason_codes or [])
        for code in reason_codes:
            if code not in existing_codes:
                existing_codes.append(code)
        existing.reason_codes = existing_codes  # type: ignore[assignment]

        if suggested_overrides:
            existing.suggested_overrides = suggested_overrides  # type: ignore[assignment]

        existing.updated_at_utc = utc_now()  # type: ignore[assignment]
        await session.flush()
        logger.info("review_item_updated", entity_id=entity_id, entity_type=entity_type)
        return existing

    item = ReviewItem(
        review_id=uuid.uuid4(),
        entity_type=entity_type,
        entity_id=entity_id,
        priority=priority,
        reason_codes=reason_codes,
        status="open",
        snapshot_refs=snapshot_refs,
        suggested_overrides=suggested_overrides,
        created_at_utc=utc_now(),
        updated_at_utc=utc_now(),
    )
    session.add(item)
    await session.flush()
    logger.info("review_item_created", entity_id=entity_id, priority=priority, reasons=reason_codes)
    return item


async def resolve_review_item(
    session: AsyncSession,
    review_id: str | uuid.UUID,
    reviewer_id: str,
    resolution_type: str,
    root_cause: str,
    notes: str,
    field_overrides: dict[str, Any] | None = None,
) -> Any:
    """Resolve a ReviewItem.

    Args:
        session: Async DB session.
        review_id: UUID of the ReviewItem to resolve.
        reviewer_id: ID/name of the reviewer.
        resolution_type: E.g. "accepted", "corrected", "wont_fix".
        root_cause: Short description of root cause.
        notes: Free-text reviewer notes.
        field_overrides: Optional field overrides applied.

    Returns:
        Resolved ReviewItem.

    Raises:
        ValueError: If item not found or already resolved.
    """
    from libs.db.models import ReviewItem

    if isinstance(review_id, str):
        review_id = uuid.UUID(review_id)

    result = await session.execute(
        select(ReviewItem).where(ReviewItem.review_id == review_id)
    )
    item = result.scalar_one_or_none()

    if item is None:
        raise ValueError(f"ReviewItem {review_id} not found")
    if item.status != "open":  # type: ignore[comparison-overlap]
        raise ValueError(f"ReviewItem {review_id} is already {item.status}")

    item.status = "resolved"  # type: ignore[assignment]
    item.reviewer_id = reviewer_id  # type: ignore[assignment]
    item.resolution_type = resolution_type  # type: ignore[assignment]
    item.root_cause = root_cause  # type: ignore[assignment]
    item.notes = notes  # type: ignore[assignment]
    item.field_overrides = field_overrides  # type: ignore[assignment]
    item.resolved_at = utc_now()  # type: ignore[assignment]
    item.updated_at_utc = utc_now()  # type: ignore[assignment]

    await session.flush()
    logger.info("review_item_resolved", review_id=str(review_id), resolution_type=resolution_type)
    return item


async def list_review_items(
    session: AsyncSession,
    status: str | None = None,
    priority: str | None = None,
    entity_type: str | None = None,
) -> list[Any]:
    """List ReviewItems with optional filters.

    Args:
        session: Async DB session.
        status: Filter by status ("open", "resolved", etc.).
        priority: Filter by priority ("P0", "P1", "P2").
        entity_type: Filter by entity_type.

    Returns:
        List of ReviewItem ORM instances.
    """
    from libs.db.models import ReviewItem

    stmt = select(ReviewItem)
    if status is not None:
        stmt = stmt.where(ReviewItem.status == status)
    if priority is not None:
        stmt = stmt.where(ReviewItem.priority == priority)
    if entity_type is not None:
        stmt = stmt.where(ReviewItem.entity_type == entity_type)

    stmt = stmt.order_by(ReviewItem.created_at_utc.desc())
    result = await session.execute(stmt)
    return list(result.scalars().all())
