from __future__ import annotations

from datetime import date, datetime, timedelta

from sqlalchemy import select

from ..extensions import db
from ..models import TaskInstance, TaskTemplate, User
from .app_settings import get_quick_task_config
from .badges import evaluate_task_badges
from .dates import add_months, today_local


def refresh_task_status(task: TaskInstance, reference_date: date | None = None) -> bool:
    status = task.compute_status(reference_date or today_local())
    if task.status != status:
        task.status = status
        return True
    return False


def refresh_task_statuses(tasks: list[TaskInstance]) -> None:
    dirty = any(refresh_task_status(task) for task in tasks)
    if dirty:
        db.session.commit()


def effective_points(base_points: int, assigned_user_secondary_id: int | None) -> int:
    if assigned_user_secondary_id:
        return max(1, base_points // 2)
    return base_points


def create_task_template_and_instance(form) -> TaskInstance:
    template = TaskTemplate(
        title=form.title.data.strip(),
        description=(form.description.data or "").strip(),
        default_points=form.default_points.data,
        default_assigned_user_id=form.assigned_user_id.data,
        default_assigned_user_secondary_id=form.assigned_user_secondary_id.data or None,
        recurrence_interval_value=form.recurrence_interval_value.data if form.recurrence_interval_unit.data != "none" else None,
        recurrence_interval_unit=form.recurrence_interval_unit.data,
        active=form.active.data,
    )
    db.session.add(template)
    db.session.flush()

    task = TaskInstance(
        task_template_id=template.id,
        title=template.title,
        description=template.description,
        assigned_user_id=template.default_assigned_user_id,
        assigned_user_secondary_id=template.default_assigned_user_secondary_id,
        due_date=form.due_date.data,
        points_awarded=effective_points(template.default_points, template.default_assigned_user_secondary_id),
        status="open",
    )
    refresh_task_status(task, form.due_date.data)
    db.session.add(task)
    db.session.commit()
    return task


def update_template_and_instance(task: TaskInstance, form) -> TaskInstance:
    template = task.task_template
    template.title = form.title.data.strip()
    template.description = (form.description.data or "").strip()
    template.default_points = form.default_points.data
    template.default_assigned_user_id = form.assigned_user_id.data
    template.default_assigned_user_secondary_id = form.assigned_user_secondary_id.data or None
    template.recurrence_interval_unit = form.recurrence_interval_unit.data
    template.recurrence_interval_value = (
        form.recurrence_interval_value.data if form.recurrence_interval_unit.data != "none" else None
    )
    template.active = form.active.data

    task.title = template.title
    task.description = template.description
    task.assigned_user_id = template.default_assigned_user_id
    task.assigned_user_secondary_id = template.default_assigned_user_secondary_id
    task.points_awarded = effective_points(template.default_points, template.default_assigned_user_secondary_id)
    task.due_date = form.due_date.data
    refresh_task_status(task, form.due_date.data)
    db.session.commit()
    return task


def _next_due_date(task: TaskInstance) -> date | None:
    template = task.task_template
    value = template.recurrence_interval_value
    if template.recurrence_interval_unit == "none" or not value:
        return None
    if template.recurrence_interval_unit == "days":
        return task.due_date + timedelta(days=value)
    if template.recurrence_interval_unit == "weeks":
        return task.due_date + timedelta(weeks=value)
    if template.recurrence_interval_unit == "months":
        return add_months(task.due_date, value)
    return None


def ensure_next_recurring_task(task: TaskInstance) -> TaskInstance | None:
    next_due = _next_due_date(task)
    if not next_due or not task.task_template.active:
        return None

    existing = db.session.scalar(
        select(TaskInstance).where(
            TaskInstance.task_template_id == task.task_template_id,
            TaskInstance.due_date == next_due,
        )
    )
    if existing:
        return existing

    next_task = TaskInstance(
        task_template_id=task.task_template_id,
        title=task.task_template.title,
        description=task.task_template.description,
        assigned_user_id=task.task_template.default_assigned_user_id,
        assigned_user_secondary_id=task.task_template.default_assigned_user_secondary_id,
        due_date=next_due,
        points_awarded=effective_points(
            task.task_template.default_points,
            task.task_template.default_assigned_user_secondary_id,
        ),
        status="open",
    )
    refresh_task_status(next_task, today_local())
    db.session.add(next_task)
    return next_task


def complete_task(task: TaskInstance, completed_by_user_id: int) -> TaskInstance:
    if not task.completed_at:
        task.completed_at = datetime.utcnow()
        task.completed_by_user_id = completed_by_user_id
        task.status = "completed"
        ensure_next_recurring_task(task)
        db.session.commit()
        if task.completed_by_user:
            evaluate_task_badges(task.completed_by_user, notify=True)
    return task


def create_quick_task(title: str, effort: str, creator: User, description: str = "Quick-Win") -> TaskInstance:
    config = get_quick_task_config()
    effort_config = config[effort]
    template = TaskTemplate(
        title=title.strip(),
        description=description,
        default_points=effort_config["points"],
        default_assigned_user_id=creator.id,
        recurrence_interval_value=None,
        recurrence_interval_unit="none",
        active=False,
    )
    db.session.add(template)
    db.session.flush()

    task = TaskInstance(
        task_template_id=template.id,
        title=template.title,
        description=description,
        assigned_user_id=creator.id,
        assigned_user_secondary_id=None,
        due_date=today_local(),
        points_awarded=template.default_points,
        status="open",
    )
    refresh_task_status(task, today_local())
    db.session.add(task)
    db.session.commit()
    return task


def delete_task_instance(task: TaskInstance) -> None:
    template = task.task_template
    db.session.delete(task)
    db.session.flush()

    remaining_instance = db.session.scalar(
        select(TaskInstance.id).where(TaskInstance.task_template_id == template.id).limit(1)
    )
    if remaining_instance is None:
        db.session.delete(template)

    db.session.commit()
