"""Business logic for the Tasks plugin."""

from __future__ import annotations

import uuid
from datetime import datetime, timezone
from typing import Any

from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession

from app.plugins.builtins.tasks.models import Task


def _task_to_dict(task: Task) -> dict[str, Any]:
    """Serialize a Task model to a dict."""
    return {
        "id": str(task.id),
        "title": task.title,
        "description": task.description,
        "status": task.status,
        "priority": task.priority,
        "due_date": task.due_date.isoformat() if task.due_date else None,
        "assigned_to": str(task.assigned_to) if task.assigned_to else None,
        "contact_id": str(task.contact_id) if task.contact_id else None,
        "created_by": str(task.created_by) if task.created_by else None,
        "created_at": task.created_at.isoformat() if task.created_at else None,
        "updated_at": task.updated_at.isoformat() if task.updated_at else None,
    }


async def list_tasks(
    db: AsyncSession,
    tenant_id: uuid.UUID,
    *,
    page: int = 1,
    page_size: int = 25,
    status: str | None = None,
    priority: str | None = None,
    assigned_to: str | None = None,
    contact_id: str | None = None,
    search: str | None = None,
) -> dict[str, Any]:
    """List tasks with filtering and pagination."""
    query = select(Task).where(Task.tenant_id == tenant_id, Task.deleted_at.is_(None))

    if status:
        query = query.where(Task.status == status)
    if priority:
        query = query.where(Task.priority == priority)
    if assigned_to:
        query = query.where(Task.assigned_to == uuid.UUID(assigned_to))
    if contact_id:
        query = query.where(Task.contact_id == uuid.UUID(contact_id))
    if search:
        query = query.where(Task.title.ilike(f"%{search}%"))

    # Count total
    count_query = select(func.count()).select_from(query.subquery())
    total_result = await db.execute(count_query)
    total = total_result.scalar() or 0

    # Paginate
    query = query.order_by(Task.due_date.asc().nulls_last(), Task.created_at.desc())
    query = query.offset((page - 1) * page_size).limit(page_size)

    result = await db.execute(query)
    tasks = result.scalars().all()

    return {
        "items": [_task_to_dict(t) for t in tasks],
        "total": total,
        "page": page,
        "page_size": page_size,
    }


async def get_task(db: AsyncSession, tenant_id: uuid.UUID, task_id: uuid.UUID) -> dict[str, Any] | None:
    """Get a single task by ID."""
    result = await db.execute(
        select(Task).where(Task.id == task_id, Task.tenant_id == tenant_id, Task.deleted_at.is_(None))
    )
    task = result.scalar_one_or_none()
    if task is None:
        return None
    return _task_to_dict(task)


async def create_task(
    db: AsyncSession,
    tenant_id: uuid.UUID,
    user_id: uuid.UUID,
    data: dict[str, Any],
) -> dict[str, Any]:
    """Create a new task."""
    task = Task(
        tenant_id=tenant_id,
        title=data["title"],
        description=data.get("description"),
        status=data.get("status", "open"),
        priority=data.get("priority", "medium"),
        due_date=data.get("due_date"),
        assigned_to=uuid.UUID(data["assigned_to"]) if data.get("assigned_to") else None,
        contact_id=uuid.UUID(data["contact_id"]) if data.get("contact_id") else None,
        created_by=user_id,
    )
    db.add(task)
    await db.flush()
    return _task_to_dict(task)


async def update_task(
    db: AsyncSession,
    tenant_id: uuid.UUID,
    task_id: uuid.UUID,
    data: dict[str, Any],
) -> dict[str, Any] | None:
    """Update a task."""
    result = await db.execute(
        select(Task).where(Task.id == task_id, Task.tenant_id == tenant_id, Task.deleted_at.is_(None))
    )
    task = result.scalar_one_or_none()
    if task is None:
        return None

    if "title" in data and data["title"] is not None:
        task.title = data["title"]
    if "description" in data:
        task.description = data["description"]
    if "status" in data and data["status"] is not None:
        task.status = data["status"]
    if "priority" in data and data["priority"] is not None:
        task.priority = data["priority"]
    if "due_date" in data:
        task.due_date = data["due_date"]
    if "assigned_to" in data:
        task.assigned_to = uuid.UUID(data["assigned_to"]) if data["assigned_to"] else None
    if "contact_id" in data:
        task.contact_id = uuid.UUID(data["contact_id"]) if data["contact_id"] else None

    await db.flush()
    return _task_to_dict(task)


async def delete_task(db: AsyncSession, tenant_id: uuid.UUID, task_id: uuid.UUID) -> bool:
    """Soft-delete a task."""
    result = await db.execute(
        select(Task).where(Task.id == task_id, Task.tenant_id == tenant_id, Task.deleted_at.is_(None))
    )
    task = result.scalar_one_or_none()
    if task is None:
        return False
    task.deleted_at = datetime.now(timezone.utc)
    await db.flush()
    return True


async def assign_task(
    db: AsyncSession,
    tenant_id: uuid.UUID,
    task_id: uuid.UUID,
    assigned_to: uuid.UUID,
) -> dict[str, Any] | None:
    """Assign a task to a user."""
    result = await db.execute(
        select(Task).where(Task.id == task_id, Task.tenant_id == tenant_id, Task.deleted_at.is_(None))
    )
    task = result.scalar_one_or_none()
    if task is None:
        return None
    task.assigned_to = assigned_to
    await db.flush()
    return _task_to_dict(task)


async def update_task_status(
    db: AsyncSession,
    tenant_id: uuid.UUID,
    task_id: uuid.UUID,
    new_status: str,
) -> dict[str, Any] | None:
    """Update task status."""
    result = await db.execute(
        select(Task).where(Task.id == task_id, Task.tenant_id == tenant_id, Task.deleted_at.is_(None))
    )
    task = result.scalar_one_or_none()
    if task is None:
        return None
    task.status = new_status
    await db.flush()
    return _task_to_dict(task)


async def get_due_tasks(
    db: AsyncSession,
    tenant_id: uuid.UUID,
    *,
    before: datetime | None = None,
) -> list[dict[str, Any]]:
    """Get tasks that are due (for ARQ reminder job)."""
    now = before or datetime.now(timezone.utc)
    result = await db.execute(
        select(Task).where(
            Task.tenant_id == tenant_id,
            Task.deleted_at.is_(None),
            Task.status != "done",
            Task.due_date.is_not(None),
            Task.due_date <= now,
        )
    )
    tasks = result.scalars().all()
    return [_task_to_dict(t) for t in tasks]
