"""Task and Subtask router - CRUD, reorder, copy-on-write integration."""
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload

from src.backend.database import get_db
from src.backend.api.middleware.api_key import require_maker, get_current_user
from src.backend.models.orm.recipe import Recipe, RecipeVersion
from src.backend.models.orm.task import RecipeSubtask, RecipeTask
from src.backend.models.orm.user import User
from src.backend.models.api.task import (
    SubtaskCreate,
    SubtaskResponse,
    SubtaskUpdate,
    TaskCreate,
    TaskReorderRequest,
    TaskResponse,
    TaskUpdate,
)
from src.backend.services import recipe_service

router = APIRouter(tags=["tasks"])


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

async def _get_current_version_for_recipe(
    db: AsyncSession, recipe_id: int
) -> RecipeVersion:
    """Return the current version of a recipe or raise 404."""
    result = await db.execute(
        select(RecipeVersion)
        .where(
            RecipeVersion.recipe_id == recipe_id,
            RecipeVersion.is_current == True,  # noqa: E712
        )
        .options(
            selectinload(RecipeVersion.tasks).selectinload(RecipeTask.subtasks)
        )
    )
    version = result.scalar_one_or_none()
    if version is None:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="No current version found for this recipe",
        )
    return version


async def _get_task_or_404(db: AsyncSession, task_id: int) -> RecipeTask:
    """Return a task with its subtasks or raise 404."""
    result = await db.execute(
        select(RecipeTask)
        .where(RecipeTask.id == task_id)
        .options(
            selectinload(RecipeTask.subtasks),
            selectinload(RecipeTask.version),
        )
    )
    task = result.scalar_one_or_none()
    if task is None:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND, detail="Task not found"
        )
    return task


async def _get_subtask_or_404(db: AsyncSession, subtask_id: int) -> RecipeSubtask:
    """Return a subtask or raise 404."""
    result = await db.execute(
        select(RecipeSubtask).where(RecipeSubtask.id == subtask_id)
    )
    subtask = result.scalar_one_or_none()
    if subtask is None:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND, detail="Subtask not found"
        )
    return subtask


async def _ensure_version_is_current(db: AsyncSession, version_id: int) -> RecipeVersion:
    """Verify a version is the current one (editable). Raise 409 otherwise."""
    result = await db.execute(
        select(RecipeVersion).where(RecipeVersion.id == version_id)
    )
    version = result.scalar_one_or_none()
    if version is None:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND, detail="Version not found"
        )
    if not version.is_current:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail="Cannot modify a non-current version. Create a new version first.",
        )
    return version


# ---------------------------------------------------------------------------
# Task endpoints
# ---------------------------------------------------------------------------

@router.get("/api/recipes/{recipe_id}/tasks", response_model=list[TaskResponse])
async def list_tasks(
    recipe_id: int,
    _user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """List all tasks for the current version of a recipe. All authenticated users."""
    version = await _get_current_version_for_recipe(db, recipe_id)
    tasks = sorted(version.tasks, key=lambda t: t.order_index)
    return [TaskResponse.model_validate(t) for t in tasks]


@router.get("/api/tasks/{task_id}", response_model=TaskResponse)
async def get_task(
    task_id: int,
    _user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """Get a single task with its subtasks. All authenticated users."""
    task = await _get_task_or_404(db, task_id)
    return TaskResponse.model_validate(task)


@router.post(
    "/api/recipes/{recipe_id}/tasks",
    response_model=TaskResponse,
    status_code=201,
)
async def create_task(
    recipe_id: int,
    data: TaskCreate,
    user: User = Depends(require_maker),
    db: AsyncSession = Depends(get_db),
):
    """Add a task to the current version of a recipe.

    This creates a NEW version via copy-on-write, then appends the task to
    the new version. Requires Maker role.
    """
    # Ensure recipe exists and is active
    result = await db.execute(select(Recipe).where(Recipe.id == recipe_id))
    recipe = result.scalar_one_or_none()
    if recipe is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Recipe not found")
    if not recipe.active:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail="Cannot modify an inactive recipe",
        )

    # Check if current version has measurements
    current = await recipe_service.get_current_version(db, recipe_id)
    has_measurements = await recipe_service.version_has_measurements(db, current.id)

    if has_measurements:
        # Copy-on-write: create new version preserving measurement data
        from src.backend.models.api.recipe import RecipeUpdate
        new_version = await recipe_service.create_new_version(
            db, recipe_id, RecipeUpdate(change_notes=f"Added task: {data.title}"), user
        )
    else:
        # No measurements: use current version directly
        new_version = current

    # Determine order_index (append at end)
    max_order = max((t.order_index for t in new_version.tasks), default=-1)
    new_task = RecipeTask(
        version_id=new_version.id,
        order_index=max_order + 1,
        title=data.title,
        directive=data.directive,
        description=data.description,
        file_path=data.file_path,
        file_type=data.file_type,
        annotations_json=data.annotations_json,
    )
    db.add(new_task)
    await db.flush()

    # Create subtasks if provided
    for sub_data in data.subtasks:
        sub = RecipeSubtask(
            task_id=new_task.id,
            marker_number=sub_data.marker_number,
            description=sub_data.description,
            measurement_type=sub_data.measurement_type,
            nominal=sub_data.nominal,
            utl=sub_data.utl,
            uwl=sub_data.uwl,
            lwl=sub_data.lwl,
            ltl=sub_data.ltl,
            unit=sub_data.unit,
            image_path=sub_data.image_path,
        )
        db.add(sub)

    await db.flush()
    await db.refresh(new_task, attribute_names=["subtasks"])
    return TaskResponse.model_validate(new_task)


@router.put("/api/tasks/reorder", response_model=list[TaskResponse])
async def reorder_tasks(
    data: TaskReorderRequest,
    _user: User = Depends(require_maker),
    db: AsyncSession = Depends(get_db),
):
    """Reorder tasks by providing ordered task IDs. All tasks must belong to the
    same current version. Requires Maker role.
    """
    if not data.task_ids:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="task_ids must not be empty",
        )

    # Fetch all tasks
    result = await db.execute(
        select(RecipeTask)
        .where(RecipeTask.id.in_(data.task_ids))
        .options(
            selectinload(RecipeTask.subtasks),
            selectinload(RecipeTask.version),
        )
    )
    tasks_map = {t.id: t for t in result.scalars().all()}

    # Validate all ids found
    for tid in data.task_ids:
        if tid not in tasks_map:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Task {tid} not found",
            )

    # Validate all belong to the same version and version is current
    version_ids = {t.version_id for t in tasks_map.values()}
    if len(version_ids) != 1:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="All tasks must belong to the same version",
        )
    await _ensure_version_is_current(db, version_ids.pop())

    # Apply new order
    for idx, tid in enumerate(data.task_ids):
        tasks_map[tid].order_index = idx

    await db.flush()

    ordered = [tasks_map[tid] for tid in data.task_ids]
    return [TaskResponse.model_validate(t) for t in ordered]


@router.put("/api/tasks/{task_id}", response_model=TaskResponse)
async def update_task(
    task_id: int,
    data: TaskUpdate,
    _user: User = Depends(require_maker),
    db: AsyncSession = Depends(get_db),
):
    """Update a task. The task must belong to the current version. Requires Maker role."""
    task = await _get_task_or_404(db, task_id)
    await _ensure_version_is_current(db, task.version_id)

    update_data = data.model_dump(exclude_unset=True)
    if not update_data:
        return TaskResponse.model_validate(task)

    for field, value in update_data.items():
        setattr(task, field, value)

    await db.flush()
    await db.refresh(task, attribute_names=["subtasks"])
    return TaskResponse.model_validate(task)


@router.delete("/api/tasks/{task_id}", status_code=204)
async def delete_task(
    task_id: int,
    _user: User = Depends(require_maker),
    db: AsyncSession = Depends(get_db),
):
    """Delete a task and its subtasks. The task must belong to the current version.

    Requires Maker role.
    """
    task = await _get_task_or_404(db, task_id)
    await _ensure_version_is_current(db, task.version_id)

    await db.delete(task)
    await db.flush()


# ---------------------------------------------------------------------------
# Subtask endpoints
# ---------------------------------------------------------------------------

@router.post(
    "/api/tasks/{task_id}/subtasks",
    response_model=SubtaskResponse,
    status_code=201,
)
async def create_subtask(
    task_id: int,
    data: SubtaskCreate,
    _user: User = Depends(require_maker),
    db: AsyncSession = Depends(get_db),
):
    """Add a subtask to a task. The task must belong to the current version.

    Requires Maker role.
    """
    task = await _get_task_or_404(db, task_id)
    await _ensure_version_is_current(db, task.version_id)

    # Check marker_number uniqueness within the task
    existing_markers = {s.marker_number for s in task.subtasks}
    if data.marker_number in existing_markers:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail=f"Marker number {data.marker_number} already exists on this task",
        )

    subtask = RecipeSubtask(
        task_id=task_id,
        marker_number=data.marker_number,
        description=data.description,
        measurement_type=data.measurement_type,
        nominal=data.nominal,
        utl=data.utl,
        uwl=data.uwl,
        lwl=data.lwl,
        ltl=data.ltl,
        unit=data.unit,
        image_path=data.image_path,
    )
    db.add(subtask)
    await db.flush()
    await db.refresh(subtask)
    return SubtaskResponse.model_validate(subtask)


@router.put("/api/subtasks/{subtask_id}", response_model=SubtaskResponse)
async def update_subtask(
    subtask_id: int,
    data: SubtaskUpdate,
    _user: User = Depends(require_maker),
    db: AsyncSession = Depends(get_db),
):
    """Update a subtask. Its parent task must belong to the current version.

    Requires Maker role.
    """
    subtask = await _get_subtask_or_404(db, subtask_id)

    # Validate task belongs to current version
    task = await _get_task_or_404(db, subtask.task_id)
    await _ensure_version_is_current(db, task.version_id)

    update_data = data.model_dump(exclude_unset=True)
    if not update_data:
        return SubtaskResponse.model_validate(subtask)

    for field, value in update_data.items():
        setattr(subtask, field, value)

    await db.flush()
    await db.refresh(subtask)
    return SubtaskResponse.model_validate(subtask)


@router.delete("/api/subtasks/{subtask_id}", status_code=204)
async def delete_subtask(
    subtask_id: int,
    _user: User = Depends(require_maker),
    db: AsyncSession = Depends(get_db),
):
    """Delete a subtask. Its parent task must belong to the current version.

    Requires Maker role.
    """
    subtask = await _get_subtask_or_404(db, subtask_id)

    # Validate task belongs to current version
    task = await _get_task_or_404(db, subtask.task_id)
    await _ensure_version_is_current(db, task.version_id)

    await db.delete(subtask)
    await db.flush()
