from datetime import date, timedelta
from typing import Optional

from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select

from app.database import get_db
from app.models.recurring_task import RecurringTask
from app.models.task import Task
from app.models.user import User
from app.schemas.recurring_task import RecurringTaskCreate, RecurringTaskUpdate, RecurringTaskOut
from app.deps import get_current_user
from app.services.recurrence import get_next_due_date

router = APIRouter(prefix="/recurring-tasks", tags=["recurring-tasks"])


async def _enrich(rt: RecurringTask, db: AsyncSession) -> dict:
    assigned_user = None
    if rt.assigned_to:
        r = await db.execute(select(User).where(User.id == rt.assigned_to))
        assigned_user = r.scalar_one_or_none()
    creator_r = await db.execute(select(User).where(User.id == rt.created_by))
    creator = creator_r.scalar_one_or_none()

    try:
        after = rt.last_generated_due or (date.today() - timedelta(days=1))
        next_due = get_next_due_date(rt.recurrence_type, rt.recurrence_config, after)
    except Exception:
        next_due = None

    return {
        **{c.name: getattr(rt, c.name) for c in rt.__table__.columns},
        "assigned_username":   assigned_user.username if assigned_user else None,
        "created_by_username": creator.username if creator else "",
        "next_due_date":       next_due,
    }


async def _create_task_instance(rt: RecurringTask, due: date, db: AsyncSession):
    """Create a task instance for a recurring task and update last_generated_due."""
    existing = await db.scalar(
        select(Task).where(Task.recurring_task_id == rt.id, Task.due_date == due)
    )
    if existing:
        return None
    task = Task(
        title=rt.title,
        assigned_to=rt.assigned_to,
        created_by=rt.created_by,
        due_date=due,
        recurring_task_id=rt.id,
    )
    db.add(task)
    rt.last_generated_due = due
    return task


# ── List ───────────────────────────────────────────────────────────────────────

@router.get("", response_model=list[RecurringTaskOut])
async def list_recurring_tasks(
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    if current_user.role == "admin":
        result = await db.execute(select(RecurringTask).order_by(RecurringTask.created_at.desc()))
    else:
        result = await db.execute(
            select(RecurringTask)
            .where(RecurringTask.created_by == current_user.id)
            .order_by(RecurringTask.created_at.desc())
        )
    return [await _enrich(rt, db) for rt in result.scalars().all()]


# ── Create ─────────────────────────────────────────────────────────────────────

@router.post("", response_model=RecurringTaskOut, status_code=201)
async def create_recurring_task(
    body: RecurringTaskCreate,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    rt = RecurringTask(**body.model_dump(), created_by=current_user.id)
    db.add(rt)
    try:
        await db.flush()  # get rt.id

        # Generate first task instance immediately
        next_due = get_next_due_date(rt.recurrence_type, rt.recurrence_config,
                                     date.today() - timedelta(days=1))
        await _create_task_instance(rt, next_due, db)

        await db.commit()
        await db.refresh(rt)
    except Exception:
        await db.rollback()
        raise
    return await _enrich(rt, db)


# ── Detail ─────────────────────────────────────────────────────────────────────

@router.get("/{rt_id}", response_model=RecurringTaskOut)
async def get_recurring_task(
    rt_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    rt = await db.get(RecurringTask, rt_id)
    if not rt:
        raise HTTPException(404, "Recurring task not found")
    if rt.created_by != current_user.id and current_user.role != "admin":
        raise HTTPException(403, "Not allowed")
    return await _enrich(rt, db)


# ── Update ─────────────────────────────────────────────────────────────────────

@router.patch("/{rt_id}", response_model=RecurringTaskOut)
async def update_recurring_task(
    rt_id: int,
    body: RecurringTaskUpdate,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    rt = await db.get(RecurringTask, rt_id)
    if not rt:
        raise HTTPException(404, "Recurring task not found")
    if rt.created_by != current_user.id and current_user.role != "admin":
        raise HTTPException(403, "Not allowed")

    for k, v in body.model_dump(exclude_unset=True).items():
        setattr(rt, k, v)

    try:
        await db.commit()
        await db.refresh(rt)
    except Exception:
        await db.rollback()
        raise
    return await _enrich(rt, db)


# ── Pause / Unpause ────────────────────────────────────────────────────────────

@router.post("/{rt_id}/pause", response_model=RecurringTaskOut)
async def toggle_pause(
    rt_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    rt = await db.get(RecurringTask, rt_id)
    if not rt:
        raise HTTPException(404, "Recurring task not found")
    if rt.created_by != current_user.id and current_user.role != "admin":
        raise HTTPException(403, "Not allowed")

    rt.is_active = not rt.is_active
    try:
        await db.commit()
        await db.refresh(rt)
    except Exception:
        await db.rollback()
        raise
    return await _enrich(rt, db)


# ── Delete ─────────────────────────────────────────────────────────────────────

@router.delete("/{rt_id}", status_code=204)
async def delete_recurring_task(
    rt_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    rt = await db.get(RecurringTask, rt_id)
    if not rt:
        raise HTTPException(404, "Recurring task not found")
    if rt.created_by != current_user.id and current_user.role != "admin":
        raise HTTPException(403, "Not allowed")
    # Nullify recurring_task_id on existing task instances (ON DELETE SET NULL handles it)
    try:
        await db.delete(rt)
        await db.commit()
    except Exception:
        await db.rollback()
        raise
