"""cortex/api/routes/memory/review.py — Review queue endpoints: flag, approve, reject, bulk flag."""

from __future__ import annotations

import logging
from typing import Any, Dict, List, Optional

from fastapi import Depends, HTTPException, Request, status

from cortex.api.deps import PREFIX, _resolve_owner_id, get_plugin
from cortex.api.models import (
    MemoryApproveResponse,
    MemoryBulkFlagRequest,
    MemoryBulkFlagResponse,
    MemoryFlagRequest,
    MemoryFlagResponse,
    MemoryRejectResponse,
)

logger = logging.getLogger(__name__)


def _register(router, verify_api_key):
    """Register review queue endpoints."""

    @router.patch(
        f"{PREFIX}/memories/{{memory_id}}/flag",
        response_model=MemoryFlagResponse,
        summary="Flag memory for review",
        tags=["review-queue"],
        operation_id="flag_memory",
    )
    async def flag_memory(
        request: Request,
        memory_id: str,
        body: MemoryFlagRequest = MemoryFlagRequest(),
        owner_id: Optional[str] = None,
        _: None = Depends(verify_api_key),
        plugin: Any = Depends(get_plugin),
    ) -> MemoryFlagResponse:
        """Flag a memory for human review. Sets status to 'needs_review'."""
        storage = getattr(plugin, "storage", None)
        if storage is None:
            raise HTTPException(
                status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
                detail="Storage not available",
            )

        effective_owner = _resolve_owner_id(request, owner_id)
        claim = await storage.get_claim(memory_id, owner_id=effective_owner)
        if claim is None:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Memory '{memory_id}' not found",
            )

        updates: Dict[str, Any] = {"status": "needs_review"}
        if body.reason is not None:
            updates["flag_reason"] = body.reason

        await storage.update_claim(memory_id, owner_id=effective_owner, **updates)

        # Log to changelog
        try:
            existing_log = await storage.get_changelog(memory_id)
            next_version = (max((e.get("version", 0) for e in existing_log), default=0) + 1
                            if existing_log else 1)
            snapshot = f"{claim.subject} {claim.predicate} {claim.object_value}"
            await storage.log_changelog(
                claim_id=memory_id,
                version=next_version,
                content_snapshot=snapshot,
                action="flagged_for_review",
                reason=body.reason or "Operator flagged for review",
                changed_by="operator",
            )
        except Exception:
            pass

        return MemoryFlagResponse(
            id=memory_id,
            status="needs_review",
            flag_reason=body.reason,
        )

    @router.patch(
        f"{PREFIX}/memories/{{memory_id}}/approve",
        response_model=MemoryApproveResponse,
        summary="Approve a flagged memory",
        tags=["review-queue"],
        operation_id="approve_memory",
    )
    async def approve_memory(
        request: Request,
        memory_id: str,
        owner_id: Optional[str] = None,
        _: None = Depends(verify_api_key),
        plugin: Any = Depends(get_plugin),
    ) -> MemoryApproveResponse:
        """Approve a flagged memory — sets status back to 'active'."""
        storage = getattr(plugin, "storage", None)
        if storage is None:
            raise HTTPException(
                status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
                detail="Storage not available",
            )

        effective_owner = _resolve_owner_id(request, owner_id)
        claim = await storage.get_claim(memory_id, owner_id=effective_owner)
        if claim is None:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Memory '{memory_id}' not found",
            )

        if claim.status != "needs_review":
            raise HTTPException(
                status_code=status.HTTP_409_CONFLICT,
                detail=f"Memory is not in review queue (status='{claim.status}')",
            )

        await storage.update_claim(
            memory_id,
            owner_id=effective_owner,
            status="active",
            flag_reason=None,
        )

        # Log to changelog
        try:
            existing_log = await storage.get_changelog(memory_id)
            next_version = (max((e.get("version", 0) for e in existing_log), default=0) + 1
                            if existing_log else 1)
            snapshot = f"{claim.subject} {claim.predicate} {claim.object_value}"
            await storage.log_changelog(
                claim_id=memory_id,
                version=next_version,
                content_snapshot=snapshot,
                action="approved_from_review",
                reason="Operator approved memory",
                changed_by="operator",
            )
        except Exception:
            pass

        return MemoryApproveResponse(id=memory_id, status="active")

    @router.patch(
        f"{PREFIX}/memories/{{memory_id}}/reject",
        response_model=MemoryRejectResponse,
        summary="Reject a flagged memory",
        tags=["review-queue"],
        operation_id="reject_memory",
    )
    async def reject_memory(
        request: Request,
        memory_id: str,
        owner_id: Optional[str] = None,
        _: None = Depends(verify_api_key),
        plugin: Any = Depends(get_plugin),
    ) -> MemoryRejectResponse:
        """Reject a flagged memory — sets status to 'rejected'."""
        storage = getattr(plugin, "storage", None)
        if storage is None:
            raise HTTPException(
                status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
                detail="Storage not available",
            )

        effective_owner = _resolve_owner_id(request, owner_id)
        claim = await storage.get_claim(memory_id, owner_id=effective_owner)
        if claim is None:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Memory '{memory_id}' not found",
            )

        if claim.status != "needs_review":
            raise HTTPException(
                status_code=status.HTTP_409_CONFLICT,
                detail=f"Memory is not in review queue (status='{claim.status}')",
            )

        await storage.update_claim(
            memory_id,
            owner_id=effective_owner,
            status="rejected",
        )

        # Log to changelog
        try:
            existing_log = await storage.get_changelog(memory_id)
            next_version = (max((e.get("version", 0) for e in existing_log), default=0) + 1
                            if existing_log else 1)
            snapshot = f"{claim.subject} {claim.predicate} {claim.object_value}"
            await storage.log_changelog(
                claim_id=memory_id,
                version=next_version,
                content_snapshot=snapshot,
                action="rejected_from_review",
                reason="Operator rejected memory",
                changed_by="operator",
            )
        except Exception:
            pass

        return MemoryRejectResponse(id=memory_id, status="rejected")

    @router.post(
        f"{PREFIX}/memories/flag-batch",
        response_model=MemoryBulkFlagResponse,
        summary="Bulk flag memories for review",
        tags=["review-queue"],
        operation_id="bulk_flag_memories",
    )
    async def bulk_flag_memories(
        request: Request,
        body: MemoryBulkFlagRequest,
        owner_id: Optional[str] = None,
        _: None = Depends(verify_api_key),
        plugin: Any = Depends(get_plugin),
    ) -> MemoryBulkFlagResponse:
        """Flag multiple memories for review at once."""
        storage = getattr(plugin, "storage", None)
        if storage is None:
            raise HTTPException(
                status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
                detail="Storage not available",
            )

        effective_owner = _resolve_owner_id(request, owner_id)
        flagged = 0
        failed: List[Dict[str, str]] = []

        for mid in body.ids:
            try:
                claim = await storage.get_claim(mid, owner_id=effective_owner)
                if claim is None:
                    failed.append({"id": mid, "error": "not found"})
                    continue

                updates: Dict[str, Any] = {"status": "needs_review"}
                if body.reason is not None:
                    updates["flag_reason"] = body.reason

                await storage.update_claim(mid, owner_id=effective_owner, **updates)
                flagged += 1
            except Exception as exc:
                failed.append({"id": mid, "error": str(exc)})

        return MemoryBulkFlagResponse(flagged=flagged, failed=failed)
