"""Per-language QC endpoints — two-stage (linguist + reviewer) assignment, workflow, comments."""

from datetime import datetime

from fastapi import APIRouter, Depends, HTTPException, Query, Request
from motor.motor_asyncio import AsyncIOMotorDatabase
from pydantic import BaseModel, Field

from ...core.database import get_database
from ...core.dependencies import require_roles
from ...models.audit_log import AuditAction
from ...models.job import LanguageQCComment, LanguageQCState
from ...models.user import User, UserRole
from ...services import language_qc as lqc
from ...services.audit_logger import audit_logger

router = APIRouter(tags=["language-qc"])


# ── Request / response schemas ────────────────────────────────────────────────

class AssignRequest(BaseModel):
    linguist_user_id: str
    notes: str | None = None
    deadline: datetime | None = None


class ReassignRequest(BaseModel):
    linguist_user_id: str
    notes: str | None = None
    deadline: datetime | None = None


class AssignReviewerRequest(BaseModel):
    reviewer_user_id: str
    notes: str | None = None
    deadline: datetime | None = None


class ReassignReviewerRequest(BaseModel):
    reviewer_user_id: str
    notes: str | None = None
    deadline: datetime | None = None


class ApproveLanguageRequest(BaseModel):
    notes: str | None = None


class RejectLanguageRequest(BaseModel):
    notes: str
    category: str | None = None  # timing | mistranslation | terminology | profanity | length | other


class ReopenLanguageRequest(BaseModel):
    notes: str | None = None


class AddCommentRequest(BaseModel):
    body: str = Field(..., min_length=1, max_length=4000)


class LanguageQCStateResponse(BaseModel):
    lang: str
    state: LanguageQCState


class LanguageQCMapResponse(BaseModel):
    job_id: str
    language_qc: dict[str, LanguageQCState]


class QueueItem(BaseModel):
    job_id: str
    job_title: str
    job_status: str
    lang: str
    lang_qc_status: str
    assigned_at: str | None = None
    reviewed_at: str | None = None


class QueueResponse(BaseModel):
    items: list[QueueItem]
    total: int


class BulkAssignRequest(BaseModel):
    linguist_user_id: str
    reviewer_user_id: str | None = None
    languages: list[str] | None = None  # None = all available languages
    only_unassigned: bool = False  # skip languages that already have an assignment
    deadline: datetime | None = None


class BulkAssignResponse(BaseModel):
    assigned: list[str]
    skipped: list[str]
    errors: dict[str, str]


# ── Routes ────────────────────────────────────────────────────────────────────

@router.get("/jobs/{job_id}/language-qc", response_model=LanguageQCMapResponse)
async def get_language_qc(
    job_id: str,
    current_user: User = Depends(require_roles(
        UserRole.REVIEWER, UserRole.LINGUIST, UserRole.PRODUCTION,
        UserRole.PROJECT_MANAGER, UserRole.ADMIN,
    )),
    db: AsyncIOMotorDatabase = Depends(get_database),
):
    # Lazy auto-assignment: apply project/job defaults on first open in PENDING_QC
    await lqc.auto_assign_defaults(db, job_id)
    states = await lqc.get_all_states(db, job_id)
    return LanguageQCMapResponse(job_id=job_id, language_qc=states)


# ── Linguist assignment ───────────────────────────────────────────────────────

@router.post("/jobs/{job_id}/languages/{lang}/assign", response_model=LanguageQCStateResponse)
async def assign_language(
    job_id: str,
    lang: str,
    request: AssignRequest,
    http_request: Request,
    current_user: User = Depends(require_roles(
        UserRole.REVIEWER, UserRole.PROJECT_MANAGER, UserRole.PRODUCTION, UserRole.ADMIN,
    )),
    db: AsyncIOMotorDatabase = Depends(get_database),
):
    state = await lqc.assign_linguist(
        db, job_id, lang, request.linguist_user_id, current_user,
        http_request=http_request, notes=request.notes, deadline=request.deadline,
    )
    await audit_logger.log_action(
        action=AuditAction.LANGUAGE_QC_ASSIGN,
        description=f"Language '{lang}' assigned to linguist '{request.linguist_user_id}' for job {job_id}",
        user=current_user,
        request=http_request,
        resource_type="job",
        resource_id=job_id,
        details={"lang": lang, "linguist_user_id": request.linguist_user_id},
    )
    return LanguageQCStateResponse(lang=lang, state=state)


@router.post("/jobs/{job_id}/languages/{lang}/reassign", response_model=LanguageQCStateResponse)
async def reassign_language(
    job_id: str,
    lang: str,
    request: ReassignRequest,
    http_request: Request,
    current_user: User = Depends(require_roles(
        UserRole.LINGUIST, UserRole.PROJECT_MANAGER, UserRole.PRODUCTION, UserRole.ADMIN,
    )),
    db: AsyncIOMotorDatabase = Depends(get_database),
):
    state = await lqc.reassign_linguist(
        db, job_id, lang, request.linguist_user_id, current_user,
        http_request=http_request, notes=request.notes, deadline=request.deadline,
    )
    await audit_logger.log_action(
        action=AuditAction.LANGUAGE_QC_REASSIGN,
        description=f"Language '{lang}' reassigned to linguist '{request.linguist_user_id}' for job {job_id}",
        user=current_user,
        request=http_request,
        resource_type="job",
        resource_id=job_id,
        details={"lang": lang, "linguist_user_id": request.linguist_user_id},
    )
    return LanguageQCStateResponse(lang=lang, state=state)


# ── Reviewer assignment ───────────────────────────────────────────────────────

@router.post("/jobs/{job_id}/languages/{lang}/assign-reviewer", response_model=LanguageQCStateResponse)
async def assign_reviewer(
    job_id: str,
    lang: str,
    request: AssignReviewerRequest,
    http_request: Request,
    current_user: User = Depends(require_roles(
        UserRole.REVIEWER, UserRole.PROJECT_MANAGER, UserRole.PRODUCTION, UserRole.ADMIN,
    )),
    db: AsyncIOMotorDatabase = Depends(get_database),
):
    state = await lqc.assign_reviewer(
        db, job_id, lang, request.reviewer_user_id, current_user,
        http_request=http_request, notes=request.notes, deadline=request.deadline,
    )
    await audit_logger.log_action(
        action=AuditAction.LANGUAGE_QC_REVIEWER_ASSIGN,
        description=f"Reviewer '{request.reviewer_user_id}' assigned to language '{lang}' for job {job_id}",
        user=current_user,
        request=http_request,
        resource_type="job",
        resource_id=job_id,
        details={"lang": lang, "reviewer_user_id": request.reviewer_user_id},
    )
    return LanguageQCStateResponse(lang=lang, state=state)


@router.post("/jobs/{job_id}/languages/{lang}/reassign-reviewer", response_model=LanguageQCStateResponse)
async def reassign_reviewer(
    job_id: str,
    lang: str,
    request: ReassignReviewerRequest,
    http_request: Request,
    current_user: User = Depends(require_roles(
        UserRole.REVIEWER, UserRole.PROJECT_MANAGER, UserRole.PRODUCTION, UserRole.ADMIN,
    )),
    db: AsyncIOMotorDatabase = Depends(get_database),
):
    state = await lqc.reassign_reviewer(
        db, job_id, lang, request.reviewer_user_id, current_user,
        http_request=http_request, notes=request.notes, deadline=request.deadline,
    )
    await audit_logger.log_action(
        action=AuditAction.LANGUAGE_QC_REVIEWER_REASSIGN,
        description=f"Reviewer reassigned to '{request.reviewer_user_id}' for language '{lang}', job {job_id}",
        user=current_user,
        request=http_request,
        resource_type="job",
        resource_id=job_id,
        details={"lang": lang, "reviewer_user_id": request.reviewer_user_id},
    )
    return LanguageQCStateResponse(lang=lang, state=state)


# ── Bulk assignment ───────────────────────────────────────────────────────────

@router.post("/jobs/{job_id}/languages/bulk-assign", response_model=BulkAssignResponse)
async def bulk_assign_languages(
    job_id: str,
    request: BulkAssignRequest,
    http_request: Request,
    current_user: User = Depends(require_roles(
        UserRole.REVIEWER, UserRole.PROJECT_MANAGER, UserRole.PRODUCTION, UserRole.ADMIN,
    )),
    db: AsyncIOMotorDatabase = Depends(get_database),
):
    """Assign one linguist (and optionally one reviewer) to multiple languages in one call."""
    job_doc = await db["jobs"].find_one({"_id": job_id})
    if not job_doc:
        raise HTTPException(status_code=404, detail="Job not found")

    available = list((job_doc.get("outputs") or {}).keys())
    target_langs = request.languages if request.languages else available

    assigned: list[str] = []
    skipped: list[str] = []
    errors: dict[str, str] = {}

    language_qc = job_doc.get("language_qc") or {}

    for lang in target_langs:
        if lang not in available:
            skipped.append(lang)
            continue

        lang_state = language_qc.get(lang) or {}
        already_assigned = bool(lang_state.get("assigned_linguist_id"))

        if request.only_unassigned and already_assigned:
            skipped.append(lang)
            continue

        try:
            await lqc.assign_linguist(
                db, job_id, lang, request.linguist_user_id, current_user,
                http_request=http_request, deadline=request.deadline,
            )
        except Exception as exc:
            errors[lang] = str(exc)
            continue

        if request.reviewer_user_id:
            try:
                await lqc.assign_reviewer(
                    db, job_id, lang, request.reviewer_user_id, current_user,
                    http_request=http_request, deadline=request.deadline,
                )
            except Exception as exc:
                errors[f"{lang}:reviewer"] = str(exc)

        assigned.append(lang)

    await audit_logger.log_action(
        action=AuditAction.LANGUAGE_QC_BULK_ASSIGN,
        description=f"Bulk assignment for job {job_id}: {len(assigned)} language(s) assigned to linguist '{request.linguist_user_id}'",
        user=current_user,
        request=http_request,
        resource_type="job",
        resource_id=job_id,
        details={
            "languages": assigned,
            "linguist_user_id": request.linguist_user_id,
            "reviewer_user_id": request.reviewer_user_id,
            "skipped": skipped,
            "errors": errors,
        },
    )
    return BulkAssignResponse(assigned=assigned, skipped=skipped, errors=errors)


# ── Workflow transitions ──────────────────────────────────────────────────────

@router.post("/jobs/{job_id}/languages/{lang}/start-work", response_model=LanguageQCStateResponse)
async def start_linguist_work(
    job_id: str,
    lang: str,
    http_request: Request,
    current_user: User = Depends(require_roles(
        UserRole.LINGUIST, UserRole.PRODUCTION, UserRole.ADMIN,
    )),
    db: AsyncIOMotorDatabase = Depends(get_database),
):
    """Linguist opens the language — pending → in_progress."""
    state = await lqc.start_linguist_work(db, job_id, lang, current_user)
    await audit_logger.log_action(
        action=AuditAction.LANGUAGE_QC_START_WORK,
        description=f"Linguist started work on language '{lang}' for job {job_id}",
        user=current_user,
        request=http_request,
        resource_type="job",
        resource_id=job_id,
        details={"lang": lang},
    )
    return LanguageQCStateResponse(lang=lang, state=state)


@router.post("/jobs/{job_id}/languages/{lang}/submit", response_model=LanguageQCStateResponse)
async def submit_for_review(
    job_id: str,
    lang: str,
    http_request: Request,
    current_user: User = Depends(require_roles(
        UserRole.LINGUIST, UserRole.PRODUCTION, UserRole.ADMIN,
    )),
    db: AsyncIOMotorDatabase = Depends(get_database),
):
    """Linguist submits — in_progress → pending_review. Notifies reviewer by email."""
    state = await lqc.submit_for_review(db, job_id, lang, current_user, http_request=http_request)
    await audit_logger.log_action(
        action=AuditAction.LANGUAGE_QC_SUBMIT,
        description=f"Language '{lang}' submitted for review for job {job_id}",
        user=current_user,
        request=http_request,
        resource_type="job",
        resource_id=job_id,
        details={"lang": lang},
    )
    return LanguageQCStateResponse(lang=lang, state=state)


@router.post("/jobs/{job_id}/languages/{lang}/open-review", response_model=LanguageQCStateResponse)
async def open_review(
    job_id: str,
    lang: str,
    http_request: Request,
    current_user: User = Depends(require_roles(
        UserRole.REVIEWER, UserRole.PRODUCTION, UserRole.ADMIN,
    )),
    db: AsyncIOMotorDatabase = Depends(get_database),
):
    """Reviewer opens the review — pending_review → in_review."""
    state = await lqc.open_review(db, job_id, lang, current_user, http_request=http_request)
    await audit_logger.log_action(
        action=AuditAction.LANGUAGE_QC_OPEN_REVIEW,
        description=f"Reviewer opened review for language '{lang}', job {job_id}",
        user=current_user,
        request=http_request,
        resource_type="job",
        resource_id=job_id,
        details={"lang": lang},
    )
    return LanguageQCStateResponse(lang=lang, state=state)


# ── Approve / Reject / Reopen ─────────────────────────────────────────────────

@router.post("/jobs/{job_id}/languages/{lang}/approve", response_model=LanguageQCStateResponse)
async def approve_language(
    job_id: str,
    lang: str,
    request: ApproveLanguageRequest,
    http_request: Request,
    current_user: User = Depends(require_roles(
        UserRole.REVIEWER, UserRole.PRODUCTION, UserRole.ADMIN,
    )),
    db: AsyncIOMotorDatabase = Depends(get_database),
):
    state = await lqc.approve_language(
        db, job_id, lang, current_user, http_request=http_request, notes=request.notes,
    )
    await audit_logger.log_action(
        action=AuditAction.LANGUAGE_QC_APPROVE,
        description=f"Language '{lang}' approved for job {job_id}",
        user=current_user,
        request=http_request,
        resource_type="job",
        resource_id=job_id,
        details={"lang": lang, "notes": request.notes},
    )
    return LanguageQCStateResponse(lang=lang, state=state)


@router.post("/jobs/{job_id}/languages/{lang}/reject", response_model=LanguageQCStateResponse)
async def reject_language(
    job_id: str,
    lang: str,
    request: RejectLanguageRequest,
    http_request: Request,
    current_user: User = Depends(require_roles(
        UserRole.REVIEWER, UserRole.PRODUCTION, UserRole.ADMIN,
    )),
    db: AsyncIOMotorDatabase = Depends(get_database),
):
    state = await lqc.reject_language(
        db, job_id, lang, current_user, request.notes, category=request.category, http_request=http_request,
    )
    await audit_logger.log_action(
        action=AuditAction.LANGUAGE_QC_REJECT,
        description=f"Language '{lang}' rejected for job {job_id}",
        user=current_user,
        request=http_request,
        resource_type="job",
        resource_id=job_id,
        details={"lang": lang, "notes": request.notes, "category": request.category},
    )
    return LanguageQCStateResponse(lang=lang, state=state)


class MarkCueReviewedRequest(BaseModel):
    total_cues: int | None = None   # client sends on first call to set total


@router.post("/jobs/{job_id}/languages/{lang}/mark-cue-reviewed", response_model=LanguageQCStateResponse)
async def mark_cue_reviewed(
    job_id: str,
    lang: str,
    request: MarkCueReviewedRequest,
    http_request: Request,
    current_user: User = Depends(require_roles(UserRole.REVIEWER, UserRole.ADMIN)),
    db: AsyncIOMotorDatabase = Depends(get_database),
):
    """Increment reviewed_cues counter; optionally set total_cues on first call."""
    job_doc = await db.jobs.find_one({"_id": job_id})
    if not job_doc:
        raise HTTPException(status_code=404, detail="Job not found")

    inc_op: dict = {f"language_qc.{lang}.reviewed_cues": 1}
    set_op: dict = {"updated_at": datetime.utcnow()}

    if request.total_cues is not None:
        set_op[f"language_qc.{lang}.total_cues"] = request.total_cues

    await db.jobs.update_one({"_id": job_id}, {"$inc": inc_op, "$set": set_op})

    updated_doc = await db.jobs.find_one({"_id": job_id})
    state_dict = (updated_doc.get("language_qc") or {}).get(lang, {})
    from ...models.job import LanguageQCState
    state = LanguageQCState(**state_dict) if isinstance(state_dict, dict) else LanguageQCState()
    return LanguageQCStateResponse(lang=lang, state=state)


@router.post("/jobs/{job_id}/languages/{lang}/reopen", response_model=LanguageQCStateResponse)
async def reopen_language(
    job_id: str,
    lang: str,
    request: ReopenLanguageRequest,
    http_request: Request,
    current_user: User = Depends(require_roles(UserRole.PRODUCTION, UserRole.ADMIN)),
    db: AsyncIOMotorDatabase = Depends(get_database),
):
    state = await lqc.reopen_language(
        db, job_id, lang, current_user, http_request=http_request, notes=request.notes,
    )
    await audit_logger.log_action(
        action=AuditAction.LANGUAGE_QC_REOPEN,
        description=f"Language '{lang}' reopened for job {job_id}",
        user=current_user,
        request=http_request,
        resource_type="job",
        resource_id=job_id,
        details={"lang": lang, "notes": request.notes},
    )
    return LanguageQCStateResponse(lang=lang, state=state)


# ── Comments ──────────────────────────────────────────────────────────────────

@router.post("/jobs/{job_id}/languages/{lang}/comments", response_model=LanguageQCComment, status_code=201)
async def add_comment(
    job_id: str,
    lang: str,
    request: AddCommentRequest,
    http_request: Request,
    current_user: User = Depends(require_roles(
        UserRole.LINGUIST, UserRole.REVIEWER, UserRole.PROJECT_MANAGER,
        UserRole.PRODUCTION, UserRole.ADMIN,
    )),
    db: AsyncIOMotorDatabase = Depends(get_database),
):
    comment = await lqc.add_comment(
        db, job_id, lang, current_user, request.body, http_request=http_request,
    )
    await audit_logger.log_action(
        action=AuditAction.LANGUAGE_QC_COMMENT,
        description=f"Comment added to language '{lang}' for job {job_id}",
        user=current_user,
        request=http_request,
        resource_type="job",
        resource_id=job_id,
        details={"lang": lang, "comment_id": str(comment.id) if hasattr(comment, "id") else None},
    )
    return comment


@router.get("/jobs/{job_id}/languages/{lang}/comments", response_model=list[LanguageQCComment])
async def list_comments(
    job_id: str,
    lang: str,
    current_user: User = Depends(require_roles(
        UserRole.LINGUIST, UserRole.REVIEWER, UserRole.PROJECT_MANAGER,
        UserRole.PRODUCTION, UserRole.ADMIN,
    )),
    db: AsyncIOMotorDatabase = Depends(get_database),
):
    state = await lqc.get_state(db, job_id, lang)
    if state is None:
        return []
    return state.comments


# ── Queues ─────────────────────────────────────────────────────────────────────

@router.get("/me/language-qc-queue", response_model=QueueResponse)
async def my_language_qc_queue(
    role: str = Query("linguist", description="'linguist' or 'reviewer'"),
    qc_status: str | None = Query(None, description="Filter by status"),
    skip: int = Query(0, ge=0),
    limit: int = Query(50, ge=1, le=200),
    current_user: User = Depends(require_roles(
        UserRole.LINGUIST, UserRole.REVIEWER, UserRole.PRODUCTION, UserRole.ADMIN,
    )),
    db: AsyncIOMotorDatabase = Depends(get_database),
):
    """List jobs and languages assigned to the current user as linguist or reviewer."""
    # ADMIN sees all orgs; staff scoped to their orgs from JWT claim (MT-18)
    org_ids: list[str] | None = None if current_user.role == UserRole.ADMIN else getattr(current_user, "org_ids", None)
    if role == "reviewer":
        jobs = await lqc.list_for_reviewer(
            db, str(current_user.id), accessible_org_ids=org_ids,
            status_filter=qc_status, skip=skip, limit=limit,
        )
    else:
        jobs = await lqc.list_for_linguist(
            db, str(current_user.id), accessible_org_ids=org_ids,
            status_filter=qc_status, skip=skip, limit=limit,
        )

    items: list[QueueItem] = []
    for job in jobs:
        job_id = str(job["_id"])
        for assignment in job.get("_my_assignments", []):
            lang = assignment["lang"]
            state_raw = (job.get("language_qc") or {}).get(lang, {})
            items.append(QueueItem(
                job_id=job_id,
                job_title=job.get("title", ""),
                job_status=job.get("status", ""),
                lang=lang,
                lang_qc_status=assignment.get("status", "pending"),
                assigned_at=state_raw.get("assigned_at").isoformat() if isinstance(state_raw, dict) and state_raw.get("assigned_at") else None,
                reviewed_at=state_raw.get("reviewed_at").isoformat() if isinstance(state_raw, dict) and state_raw.get("reviewed_at") else None,
            ))

    return QueueResponse(items=items, total=len(items))
