from __future__ import annotations

from datetime import datetime
from typing import Any, Dict, List, Optional
from uuid import uuid4

from pydantic import BaseModel

REVIEW_QUEUE_KEY = "gremlin:review_queue:{conversation_id}"
REVIEW_QUEUE_TTL = 86400  # 24 hours
REVIEW_QUEUE_MAX = 200

ALLOWED_REVIEW_BANDS = {"POSSIBLE", "REVIEW", "UNLIKELY"}


class ReviewQueueEntry(BaseModel):
    entry_id: str
    row_id: str
    cluster_id: Optional[str] = None
    score: float
    band: str
    source_values: Dict[str, Any] = {}
    proposed_action: str
    status: str = "pending"
    created_at: datetime
    resolved_at: Optional[datetime] = None
    decision: Optional[str] = None


class ReviewQueueState(BaseModel):
    queue_id: str
    conversation_id: str
    created_at: datetime
    updated_at: datetime
    items: List[ReviewQueueEntry]


async def _get_redis(redis=None):
    if redis is not None:
        return redis
    from ..api.progress import get_redis

    return await get_redis()


def _normalize_band(value: Optional[str]) -> str:
    return str(value or "").strip().upper()


def _should_include(entry: ReviewQueueEntry) -> bool:
    return _normalize_band(entry.band) in ALLOWED_REVIEW_BANDS


async def create_review_queue(
    conversation_id: str,
    entries: List[ReviewQueueEntry],
    redis=None,
    max_items: int = REVIEW_QUEUE_MAX,
) -> Optional[ReviewQueueState]:
    if not conversation_id:
        return None
    filtered = [entry for entry in entries if _should_include(entry)]
    if not filtered:
        return None

    now = datetime.utcnow()
    queue = ReviewQueueState(
        queue_id=str(uuid4()),
        conversation_id=conversation_id,
        created_at=now,
        updated_at=now,
        items=filtered[:max_items],
    )
    redis_conn = await _get_redis(redis)
    if not redis_conn:
        return queue
    key = REVIEW_QUEUE_KEY.format(conversation_id=conversation_id)
    await redis_conn.set(key, queue.model_dump_json(), ex=REVIEW_QUEUE_TTL)
    return queue


async def _load_queue(conversation_id: str, redis=None) -> Optional[ReviewQueueState]:
    if not conversation_id:
        return None
    redis_conn = await _get_redis(redis)
    if not redis_conn:
        return None
    key = REVIEW_QUEUE_KEY.format(conversation_id=conversation_id)
    raw = await redis_conn.get(key)
    if not raw:
        return None
    if isinstance(raw, bytes):
        raw = raw.decode("utf-8")
    try:
        return ReviewQueueState.model_validate_json(raw)
    except Exception:
        return None


async def get_queue_item(
    conversation_id: str,
    entry_id: str,
    redis=None,
) -> Optional[ReviewQueueEntry]:
    queue = await _load_queue(conversation_id, redis=redis)
    if not queue or not entry_id:
        return None
    for item in queue.items:
        if item.entry_id == entry_id:
            return item
    return None


async def resolve_queue_item(
    conversation_id: str,
    entry_id: str,
    decision: str,
    redis=None,
) -> Optional[ReviewQueueEntry]:
    queue = await _load_queue(conversation_id, redis=redis)
    if not queue or not entry_id:
        return None
    normalized = str(decision or "").strip().lower()
    if normalized not in {"accept", "reject", "skip"}:
        return None
    updated = None
    now = datetime.utcnow()
    for item in queue.items:
        if item.entry_id != entry_id:
            continue
        item.status = "resolved"
        item.decision = normalized
        item.resolved_at = now
        updated = item
        break
    if not updated:
        return None
    queue.updated_at = now
    redis_conn = await _get_redis(redis)
    if redis_conn:
        key = REVIEW_QUEUE_KEY.format(conversation_id=conversation_id)
        await redis_conn.set(key, queue.model_dump_json(), ex=REVIEW_QUEUE_TTL)
    return updated
