"""Task views: listing, CRUD, complete/reopen."""
from flask import (
    Blueprint,
    abort,
    flash,
    redirect,
    render_template,
    request,
    url_for,
)
from flask_login import current_user, login_required

from . import notifications, storage
from .auth import admin_required
from .timeutils import today_ist

tasks_bp = Blueprint("tasks", __name__)


def today_str():
    return today_ist()


def _team_member_ids(lead_id):
    return {s["id"] for s in storage.get_subordinates(lead_id=lead_id, include_inactive=True)}


def _admin_can_access(task, lead_id):
    """True if the task's assignee is this lead or a member of their team."""
    assignee = storage.get_user(task["assignee_id"])
    if assignee is None:
        return False
    return assignee["id"] == lead_id or assignee.get("lead_id") == lead_id


def _assignable(lead_id):
    """Users a lead may assign to: themselves first, then their team."""
    me = storage.get_user(lead_id)
    team = storage.get_subordinates(lead_id=lead_id)
    people = ([me] if me else []) + team
    return [
        {"id": p["id"], "name": p["name"], "is_self": p["id"] == lead_id}
        for p in people
    ]


def _resolve_assignee(assignee_id, lead_id):
    """Return the chosen assignee id if the lead may assign to them, else None."""
    allowed = {p["id"] for p in _assignable(lead_id)}
    try:
        chosen = int(assignee_id)
    except (TypeError, ValueError):
        return None
    return chosen if chosen in allowed else None


def is_overdue(task):
    return (
        task["status"] == "open"
        and task.get("due_date")
        and task["due_date"] < today_str()
    )


def enrich(task, users_by_id):
    """Return a copy of a task with display fields for templates."""
    assignee = users_by_id.get(task["assignee_id"])
    return {
        **task,
        "assignee_name": assignee["name"] if assignee else "Unknown",
        "overdue": is_overdue(task),
    }


def _users_by_id():
    return {u["id"]: u for u in storage.get_users()}


@tasks_bp.route("/")
@login_required
def home():
    return redirect(url_for("tasks.my_tasks"))


# --------------------------------------------------------------------------
# Admin views
# --------------------------------------------------------------------------
@tasks_bp.route("/admin/tasks")
@admin_required
def admin_open():
    users_by_id = _users_by_id()
    team_ids = _team_member_ids(current_user.id)
    open_tasks = [
        enrich(t, users_by_id)
        for t in storage.get_tasks()
        if t["status"] == "open" and t["assignee_id"] in team_ids
    ]

    groups = []
    for sub in storage.get_subordinates(lead_id=current_user.id):
        sub_tasks = [t for t in open_tasks if t["assignee_id"] == sub["id"]]
        sub_tasks.sort(key=lambda t: (t.get("due_date") or "9999-12-31"))
        groups.append({"subordinate": sub, "tasks": sub_tasks})

    total_open = len(open_tasks)
    return render_template("admin/open.html", groups=groups, total_open=total_open)


@tasks_bp.route("/admin/completed")
@admin_required
def admin_completed():
    users_by_id = _users_by_id()
    team_ids = _team_member_ids(current_user.id)
    completed = [
        enrich(t, users_by_id)
        for t in storage.get_tasks()
        if t["status"] == "completed" and t["assignee_id"] in team_ids
    ]
    completed.sort(key=lambda t: (t.get("completed_at") or ""), reverse=True)
    return render_template("admin/completed.html", tasks=completed)


@tasks_bp.route("/admin/tasks/new", methods=["GET", "POST"])
@admin_required
def create():
    assignees = _assignable(current_user.id)
    if request.method == "POST":
        title = (request.form.get("title") or "").strip()
        description = request.form.get("description") or ""
        due_date = (request.form.get("due_date") or "").strip()
        assignee_id = request.form.get("assignee_id")

        errors = []
        if not title:
            errors.append("Please enter a task title.")
        chosen = _resolve_assignee(assignee_id, current_user.id)
        if chosen is None:
            errors.append("Please choose who to assign this task to.")

        if errors:
            for e in errors:
                flash(e, "error")
            return render_template(
                "admin/task_form.html",
                assignees=assignees,
                task=None,
                form={"title": title, "description": description, "due_date": due_date,
                      "assignee_id": assignee_id},
            )

        assigned_to_self = chosen == current_user.id
        new_task = storage.create_task(title, description, due_date or "", chosen, current_user.id)
        notifications.task_assigned(new_task, actor_id=current_user.id)
        flash("Task added to your list." if assigned_to_self else "Task assigned.", "success")
        return redirect(url_for("tasks.my_tasks" if assigned_to_self else "tasks.admin_open"))

    return render_template(
        "admin/task_form.html",
        assignees=assignees,
        task=None,
        form={"title": "", "description": "", "due_date": today_str(),
              "assignee_id": current_user.id},
    )


@tasks_bp.route("/admin/tasks/<int:task_id>/edit", methods=["GET", "POST"])
@admin_required
def edit(task_id):
    task = storage.get_task(task_id)
    if task is None:
        abort(404)
    if not _admin_can_access(task, current_user.id):
        abort(403)
    assignees = _assignable(current_user.id)

    if request.method == "POST":
        title = (request.form.get("title") or "").strip()
        description = request.form.get("description") or ""
        due_date = (request.form.get("due_date") or "").strip()
        assignee_id = request.form.get("assignee_id")

        errors = []
        if not title:
            errors.append("Please enter a task title.")
        chosen = _resolve_assignee(assignee_id, current_user.id)
        if chosen is None:
            errors.append("Please choose who to assign this task to.")

        if errors:
            for e in errors:
                flash(e, "error")
            return render_template(
                "admin/task_form.html", assignees=assignees, task=task,
                form={"title": title, "description": description, "due_date": due_date,
                      "assignee_id": assignee_id},
            )

        prev_assignee = task["assignee_id"]
        updated = storage.update_task(
            task_id, title=title, description=description.strip(),
            due_date=due_date or "", assignee_id=chosen,
        )
        if chosen != prev_assignee:
            notifications.task_assigned(updated, actor_id=current_user.id)
        flash("Task updated.", "success")
        return redirect(url_for("tasks.detail", task_id=task_id))

    return render_template(
        "admin/task_form.html", assignees=assignees, task=task,
        form={"title": task["title"], "description": task["description"],
              "due_date": task.get("due_date", ""), "assignee_id": task["assignee_id"]},
    )


@tasks_bp.route("/admin/tasks/<int:task_id>/delete", methods=["POST"])
@admin_required
def delete(task_id):
    task = storage.get_task(task_id)
    if task is None:
        abort(404)
    if not _admin_can_access(task, current_user.id):
        abort(403)
    storage.delete_task(task_id)
    flash("Task deleted.", "success")
    return redirect(url_for("tasks.admin_open"))


@tasks_bp.route("/admin/tasks/<int:task_id>/reopen", methods=["POST"])
@admin_required
def reopen(task_id):
    task = storage.get_task(task_id)
    if task is None:
        abort(404)
    if not _admin_can_access(task, current_user.id):
        abort(403)
    storage.reopen_task(task_id)
    flash("Task reopened.", "success")
    return redirect(url_for("tasks.detail", task_id=task_id))


@tasks_bp.route("/admin/tasks/<int:task_id>/reassign", methods=["POST"])
@admin_required
def reassign(task_id):
    task = storage.get_task(task_id)
    if task is None:
        abort(404)
    if not _admin_can_access(task, current_user.id):
        abort(403)
    chosen = _resolve_assignee(request.form.get("assignee_id"), current_user.id)
    if chosen is None:
        flash("Please pick who to reassign this task to.", "error")
        return redirect(url_for("tasks.detail", task_id=task_id))
    prev_assignee = task["assignee_id"]
    updated = storage.update_task(task_id, assignee_id=chosen)
    target = storage.get_user(chosen)
    if chosen != prev_assignee:
        notifications.task_assigned(updated, actor_id=current_user.id)
    if chosen == current_user.id:
        flash("Task moved to your own list.", "success")
    else:
        flash(f"Reassigned to {target['name']}.", "success")
    return redirect(url_for("tasks.detail", task_id=task_id))


# --------------------------------------------------------------------------
# Subordinate views
# --------------------------------------------------------------------------
@tasks_bp.route("/my-tasks")
@login_required
def my_tasks():
    users_by_id = _users_by_id()
    tasks = [enrich(t, users_by_id)
             for t in storage.tasks_for_assignee(current_user.id, status="open")]
    tasks.sort(key=lambda t: (t.get("due_date") or "9999-12-31"))
    return render_template("sub/my_tasks.html", tasks=tasks)


@tasks_bp.route("/my-completed")
@login_required
def my_completed():
    users_by_id = _users_by_id()
    tasks = [enrich(t, users_by_id)
             for t in storage.tasks_for_assignee(current_user.id, status="completed")]
    tasks.sort(key=lambda t: (t.get("completed_at") or ""), reverse=True)
    return render_template("sub/my_completed.html", tasks=tasks)


# --------------------------------------------------------------------------
# Shared: detail + complete
# --------------------------------------------------------------------------
@tasks_bp.route("/tasks/<int:task_id>")
@login_required
def detail(task_id):
    task = storage.get_task(task_id)
    if task is None:
        abort(404)
    if current_user.is_admin:
        if not _admin_can_access(task, current_user.id):
            abort(403)
    elif task["assignee_id"] != current_user.id:
        abort(403)
    users_by_id = _users_by_id()
    assignees = _assignable(current_user.id) if current_user.is_admin else []
    return render_template(
        "task_detail.html", task=enrich(task, users_by_id), assignees=assignees
    )


@tasks_bp.route("/tasks/<int:task_id>/complete", methods=["POST"])
@login_required
def complete(task_id):
    task = storage.get_task(task_id)
    if task is None:
        abort(404)
    if current_user.is_admin:
        if not _admin_can_access(task, current_user.id):
            abort(403)
    elif task["assignee_id"] != current_user.id:
        abort(403)
    storage.complete_task(task_id)
    notifications.task_completed(task, actor_id=current_user.id)
    flash("Nice work — task marked complete!", "success")
    # Return to wherever the tick was pressed (report day view, my tasks, etc.)
    ref = request.referrer
    if ref and url_for("tasks.detail", task_id=task_id) not in ref:
        return redirect(ref)
    if current_user.is_admin and task["assignee_id"] != current_user.id:
        return redirect(url_for("tasks.admin_open"))
    return redirect(url_for("tasks.my_tasks"))
