"""Admin-only: daily report + per-person day drill-down.

For a selected date D, per person (the lead plus their team):
  - assigned    : tasks with due_date == D
  - completed   : those tasks now in 'completed' status
  - spilled_over: tasks due *before* D that were still open as of D
                  (carried in from earlier days). These keep showing on
                  every day's report until they're completed.
"""
from datetime import date, timedelta

from flask import Blueprint, abort, render_template, request
from flask_login import current_user

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

reports_bp = Blueprint("reports", __name__, url_prefix="/admin")


def _people(lead_id):
    """The lead (first) plus their team, for reporting."""
    me = storage.get_user(lead_id)
    team = storage.get_subordinates(lead_id=lead_id, include_inactive=True)
    people = ([me] if me else []) + team
    for p in people:
        p = dict(p)
    return people


def _shift(iso_date, days):
    try:
        return (date.fromisoformat(iso_date) + timedelta(days=days)).isoformat()
    except ValueError:
        return iso_date


def _days_late(due_date, as_of):
    """How many days past its due date a task is, relative to `as_of`."""
    try:
        return (date.fromisoformat(as_of) - date.fromisoformat(due_date)).days
    except (ValueError, TypeError):
        return 0


def _ist_date(iso_ts):
    """IST date (YYYY-MM-DD) of a stored UTC timestamp, or None."""
    dt = to_ist(iso_ts)
    return dt.date().isoformat() if dt else None


def _existed_by(task, as_of):
    """True if the task had been created on or before `as_of`."""
    created = _ist_date(task.get("created_at"))
    return created is None or created <= as_of


def _completed_by(task, as_of):
    """True if the task was already completed on or before `as_of`."""
    if task.get("status") != "completed":
        return False
    done = _ist_date(task.get("completed_at"))
    return done is None or done <= as_of


def _is_spillover(task, as_of):
    """A task counts as spilled over *into* `as_of` when it was due earlier,
    already existed by then, and was still open as of that day."""
    due = task.get("due_date")
    return (
        bool(due)
        and due < as_of
        and _existed_by(task, as_of)
        and not _completed_by(task, as_of)
    )


@reports_bp.route("/report")
@admin_required
def report():
    selected = (request.args.get("date") or "").strip()
    if not selected:
        selected = today_ist()

    all_tasks = storage.get_tasks()

    rows = []
    totals = {"assigned": 0, "completed": 0, "spilled_over": 0}
    for person in _people(current_user.id):
        mine = [t for t in all_tasks if t["assignee_id"] == person["id"]]
        assigned = [t for t in mine if t.get("due_date") == selected]
        completed = [t for t in assigned if t["status"] == "completed"]
        spilled = [t for t in mine if _is_spillover(t, selected)]
        if not assigned and not spilled:
            continue
        rows.append({
            "person": person,
            "is_self": person["id"] == current_user.id,
            "assigned": len(assigned),
            "completed": len(completed),
            "spilled_over": len(spilled),
        })
        totals["assigned"] += len(assigned)
        totals["completed"] += len(completed)
        totals["spilled_over"] += len(spilled)

    return render_template(
        "admin/report.html",
        selected=selected,
        rows=rows,
        totals=totals,
        prev_date=_shift(selected, -1),
        next_date=_shift(selected, 1),
    )


@reports_bp.route("/report/<date_str>/<int:user_id>")
@admin_required
def report_day(date_str, user_id):
    person = storage.get_user(user_id)
    is_self = user_id == current_user.id
    allowed = is_self or (person is not None and person.get("lead_id") == current_user.id)
    if person is None or not allowed:
        abort(403)

    from .tasks import enrich  # local import to avoid a cycle

    users_by_id = {u["id"]: u for u in storage.get_users()}
    mine = [t for t in storage.get_tasks() if t["assignee_id"] == user_id]

    # Tasks due on the selected day.
    day_tasks = [t for t in mine if t.get("due_date") == date_str]
    completed = [enrich(t, users_by_id) for t in day_tasks if t["status"] == "completed"]
    open_tasks = [enrich(t, users_by_id) for t in day_tasks if t["status"] != "completed"]

    # Tasks that spilled over into this day from earlier due dates.
    spilled = []
    for t in mine:
        if _is_spillover(t, date_str):
            item = enrich(t, users_by_id)
            item["spillover"] = True
            item["days_late"] = _days_late(t["due_date"], date_str)
            spilled.append(item)

    open_tasks.sort(key=lambda t: t["title"].lower())
    completed.sort(key=lambda t: (t.get("completed_at") or ""), reverse=True)
    spilled.sort(key=lambda t: (t.get("due_date") or ""))  # oldest first

    return render_template(
        "admin/report_day.html",
        person=person,
        is_self=is_self,
        selected=date_str,
        spilled=spilled,
        open_tasks=open_tasks,
        completed=completed,
    )
