"""
Review Queue API for Active Learning
=====================================
Prioritizes uncertain matches for human review to maximize learning.
"""

from fastapi import APIRouter, Depends, HTTPException, Query, Body
from typing import Dict, Any, List
from uuid import UUID
import pandas as pd
import logging

from ...auth import get_current_account
from ...db import get_session
from sqlalchemy.ext.asyncio import AsyncSession
from .jobs import get_redis, get_temp_dir

log = logging.getLogger(__name__)
router = APIRouter(prefix="/api/v2/review", tags=["review"])


@router.get("/queue/{job_id}")
async def get_review_queue(
    job_id: str,
    limit: int = Query(default=50, ge=1, le=200),
    strategy: str = Query(
        default="uncertainty", description="uncertainty, value, or mixed"
    ),
    db: AsyncSession = Depends(get_session),
    account_id: UUID = Depends(get_current_account),
) -> Dict[str, Any]:
    """
    Get prioritized matches for human review.
    Uses active learning to select most informative cases.

    Strategies:
    - uncertainty: Matches with scores near threshold
    - value: High-value matches needing confirmation
    - mixed: Combination of both
    """
    redis_conn = await get_redis()
    if not redis_conn:
        raise HTTPException(503, "Cache service unavailable")

    # Check job exists and is completed
    import json

    job_data = await redis_conn.get(f"job:{job_id}")
    if not job_data:
        raise HTTPException(404, f"Job {job_id} not found")

    job_metadata = json.loads(job_data)
    if job_metadata.get("status") != "completed":
        return {
            "job_id": job_id,
            "status": job_metadata.get("status"),
            "message": "Job must be completed before review",
            "queue": [],
        }

    # Load results
    output_dir = get_temp_dir(job_id)
    results_file = output_dir / "results.csv"

    if not results_file.exists():
        return {
            "job_id": job_id,
            "message": "No results available for review",
            "queue": [],
        }

    # Read results
    df = pd.read_csv(results_file)

    if df.empty:
        return {"job_id": job_id, "message": "No matches found", "queue": []}

    # Ensure score column exists and is numeric
    score_col = "score" if "score" in df.columns else "confidence_score"
    if score_col not in df.columns:
        log.warning(f"No score column found in results for job {job_id}")
        return {
            "job_id": job_id,
            "message": "Results lack score information",
            "queue": [],
        }

    # Calculate uncertainty (distance from threshold)
    threshold = job_metadata.get("configuration", {}).get("threshold", 80)
    if threshold > 1:
        threshold = threshold / 100  # Normalize to 0-1

    # Ensure scores are 0-1 scale for uncertainty calculation
    if df[score_col].max() > 1:
        df["normalized_score"] = df[score_col] / 100
    else:
        df["normalized_score"] = df[score_col]

    # Calculate uncertainty (how close to threshold)
    df["uncertainty"] = 1 - abs(df["normalized_score"] - threshold)

    # Calculate margin (difference between top 2 scores for same source)
    # This helps identify cases where multiple targets are similar
    if "source_id" in df.columns:

        def calculate_margin(scores):
            if len(scores) <= 1:
                return 1.0
            top2 = scores.nlargest(2)
            return float(top2.iloc[0] - top2.iloc[1])

        df["margin"] = df.groupby("source_id")["normalized_score"].transform(
            calculate_margin
        )
    else:
        df["margin"] = 1.0

    # Apply strategy
    if strategy == "uncertainty":
        # Pure uncertainty sampling
        df["priority"] = df["uncertainty"]

    elif strategy == "value":
        # Prioritize high-value matches (assuming we have value data)
        # For now, use a proxy: higher scores are more valuable to confirm
        df["priority"] = df["normalized_score"] * df["uncertainty"]

    elif strategy == "mixed":
        # Balanced approach
        df["priority"] = (
            0.5 * df["uncertainty"]  # Uncertainty component
            + 0.3 * (1 - df["margin"])  # Ambiguity component
            + 0.2 * df["normalized_score"]  # Value component
        )

    else:
        df["priority"] = df["uncertainty"]

    # Sort by priority and get top N
    df_sorted = df.nlargest(limit, "priority")

    # Prepare queue items
    queue = []
    for _, row in df_sorted.iterrows():
        item = {
            "match_id": row.get(
                "match_id", f"{row.get('source_id')}_{row.get('reference_id')}"
            ),
            "source_id": row.get("source_id"),
            "reference_id": row.get("reference_id", row.get("ref_id")),
            "score": float(row[score_col]),
            "uncertainty": float(row["uncertainty"]),
            "margin": float(row["margin"]),
            "priority": float(row["priority"]),
            "reason": _get_review_reason(row),
        }

        # Add field details if available
        if "field_score_details" in row:
            item["field_details"] = row["field_score_details"]

        queue.append(item)

    # Calculate queue statistics
    stats = {
        "total_matches": len(df),
        "uncertain_matches": int((df["uncertainty"] > 0.8).sum()),
        "ambiguous_matches": int((df["margin"] < 0.05).sum()),
        "threshold": threshold,
        "strategy": strategy,
    }

    return {
        "job_id": job_id,
        "queue": queue,
        "stats": stats,
        "message": f"Selected {len(queue)} matches for review using {strategy} strategy",
    }


@router.post("/queue/{job_id}/submit")
async def submit_review_results(
    job_id: str,
    reviews: List[Dict[str, Any]] = Body(...),
    db: AsyncSession = Depends(get_session),
    account_id: UUID = Depends(get_current_account),
) -> Dict[str, Any]:
    """
    Submit review results for a batch of matches.

    Expected format:
    [
        {
            "match_id": "...",
            "decision": "accept" | "reject" | "correct",
            "correct_target": "..." (if decision is "correct")
        }
    ]
    """
    from ...intelligence.decision_logger import DecisionLogger

    redis_conn = await get_redis()
    logger = DecisionLogger(redis_conn=redis_conn)

    processed = 0
    for review in reviews:
        match_id = review.get("match_id")
        decision = review.get("decision")

        if not match_id or not decision:
            continue

        # Map decision to feedback outcome
        if decision == "accept":
            outcome = "accepted"
        elif decision == "reject":
            outcome = "reverted"
        elif decision == "correct":
            outcome = "corrected"
        else:
            continue

        # Record feedback directly through logger
        await logger.log_feedback(
            job_id=job_id,
            match_id=match_id,
            outcome=outcome,
            user_id=str(account_id),
            corrected_target=review.get("correct_target"),
        )
        processed += 1

    return {
        "status": "success",
        "job_id": job_id,
        "processed": processed,
        "message": f"Processed {processed} review decisions",
    }


def _get_review_reason(row: pd.Series) -> str:
    """Generate human-readable reason for review"""
    uncertainty = row["uncertainty"]
    margin = row["margin"]
    score = row["normalized_score"]

    reasons = []

    if uncertainty > 0.8:
        reasons.append("score near threshold")

    if margin < 0.1:
        reasons.append("multiple similar candidates")

    if score > 0.9 and uncertainty > 0.5:
        reasons.append("high-confidence match needs verification")

    if score < 0.5 and uncertainty > 0.5:
        reasons.append("low-confidence match needs review")

    return " | ".join(reasons) if reasons else "selected for review"
