"""بخش «کارها» — تسک‌بورد شبیه تیک‌تیک: پوشه، تسک، تیک زدن، تقویم شمسی و افزودن گروهی مشتری.

این بخش جداست از صفحه‌ی «تسک» قدیمی (دسته‌بندی‌های آماده‌ی مشتری در blueprint به نام tasks)؛
دکمه‌ی منوی پایین به اینجا میاد و دکمه‌ی هدر صفحه‌ی اصلی همون صفحه‌ی قبلی می‌مونه.
"""
from flask import Blueprint, abort, flash, jsonify, redirect, render_template, request, url_for
from flask_login import current_user, login_required

from app.extensions import db
from app.models.task_board import TaskFolder, TaskItem, TaskSubitem
from app.models.user import User
from app.services import customer_service, listing, task_board
from app.services.activity_logger import log_action
from app.services.jalali import days_ahead, iran_today, to_jalali_str
from app.services.permissions import can_access, require

bp = Blueprint("tasks_board", __name__, url_prefix="/todo")


# ---------------------------------------------------------------------------
# کمک‌توابع
# ---------------------------------------------------------------------------

def _wants_json():
    """درخواست از todo.js اومده یا از یک فرم معمولی؟ (هدر Accept قابل اتکا نیست)."""
    return request.headers.get("X-Requested-With") == "fetch"


def _safe_back(default_url):
    """بازگشت به همون صفحه‌ای که کاربر ازش اومده — فقط آدرس داخل همین سایت."""
    target = (request.values.get("back") or "").strip()
    if target.startswith("/") and not target.startswith("//") and "\\" not in target:
        return target
    return default_url


def _assignee_filter():
    raw = (request.args.get("assignee") or "").strip()
    if raw == "me":
        return current_user.id, "me"
    if raw.isdigit():
        return int(raw), raw
    return None, ""


def _board_context():
    """چیزهایی که همه‌ی صفحه‌های این بخش (و فرم تسک) لازم دارن."""
    return {
        "folders": TaskFolder.query.filter(TaskFolder.is_archived.is_(False))
                                   .order_by(TaskFolder.name, TaskFolder.id).all(),
        "users": User.query.filter(User.is_active_flag.is_(True)).order_by(User.full_name).all(),
        "priorities": task_board.PRIORITIES,
        "repeat_choices": task_board.REPEAT_CHOICES,
        "repeat_labels": task_board.REPEAT_LABELS,
        "folder_colors": task_board.FOLDER_COLORS,
        "views": task_board.VIEWS,
        "today": iran_today(),
        "quick_dates": {
            "today": to_jalali_str(iran_today()),
            "tomorrow": to_jalali_str(days_ahead(1)),
            "next_week": to_jalali_str(days_ahead(7)),
        },
    }


def _item_or_404(item_id):
    return db.session.get(TaskItem, item_id) or abort(404)


def _folder_or_404(folder_id):
    return db.session.get(TaskFolder, folder_id) or abort(404)


# ---------------------------------------------------------------------------
# صفحه‌ها
# ---------------------------------------------------------------------------

@bp.route("/")
@login_required
@require("tasks")
def index():
    task_board.ensure_default_folder(current_user.id)
    assignee_id, assignee_value = _assignee_filter()
    search = (request.args.get("q") or "").strip()

    rows = task_board.folder_rows()
    return render_template(
        "tasks_board/index.html",
        counts=task_board.view_counts(assignee_id),
        active_folders=[row for row in rows if not row["is_complete"]],
        done_folders=[row for row in rows if row["is_complete"]],
        archived_folders=[row for row in task_board.folder_rows(include_archived=True)
                          if row["folder"].is_archived],
        assignee_value=assignee_value,
        q=search,
        **_board_context(),
    )


@bp.route("/view/<key>")
@login_required
@require("tasks")
def view(key):
    if key != "all" and key not in task_board.VIEW_MAP:
        abort(404)
    meta = task_board.VIEW_MAP.get(key, {"key": "all", "title": "همه‌ی کارها", "color": "gold", "hint": ""})
    assignee_id, assignee_value = _assignee_filter()
    search = (request.args.get("q") or "").strip()

    rows, total = task_board.list_items(
        limit=listing.page_limit(), view=None if key == "all" else key,
        assignee_id=assignee_id, search=search or None,
    )
    return render_template(
        "tasks_board/list.html",
        page_title=meta["title"], page_hint=meta.get("hint", ""), page_color=meta["color"],
        view_key=key, folder=None, rows=rows, total=total,
        done_rows=None, done_total=0,
        assignee_value=assignee_value, q=search,
        **_board_context(),
    )


@bp.route("/folder/<int:folder_id>")
@login_required
@require("tasks")
def folder(folder_id):
    folder_row = _folder_or_404(folder_id)
    assignee_id, assignee_value = _assignee_filter()
    search = (request.args.get("q") or "").strip()

    rows, total = task_board.list_items(
        limit=listing.page_limit(), folder_id=folder_id, include_done=False,
        assignee_id=assignee_id, search=search or None,
    )
    done_rows, done_total = task_board.list_items(
        limit=listing.page_limit(), folder_id=folder_id, view="done",
        assignee_id=assignee_id, search=search or None,
    )

    # پنل «افزودن گروهی مشتری» — جستجوی سمت سرور، بدون نیاز به جاوااسکریپت
    add_query = (request.args.get("add") or "").strip()
    add_results = customer_service.search_by_name(add_query, limit=30) if len(add_query) >= 2 else []

    return render_template(
        "tasks_board/list.html",
        page_title=folder_row.name, page_hint="", page_color=folder_row.color,
        view_key=None, folder=folder_row, rows=rows, total=total,
        done_rows=done_rows, done_total=done_total,
        progress=task_board.folder_progress(folder_id),
        add_query=add_query, add_results=add_results,
        assignee_value=assignee_value, q=search,
        **_board_context(),
    )


# ---------------------------------------------------------------------------
# پوشه‌ها
# ---------------------------------------------------------------------------

@bp.route("/folders", methods=["POST"])
@login_required
@require("tasks")
def create_folder():
    name = (request.form.get("name") or "").strip()[:120]
    if not name:
        flash("نام پوشه را بنویسید", "error")
        return redirect(_safe_back(url_for("tasks_board.index")))
    if TaskFolder.query.filter_by(name=name).first():
        flash(f"پوشه‌ای با نام «{name}» از قبل وجود دارد", "error")
        return redirect(_safe_back(url_for("tasks_board.index")))

    folder_row = TaskFolder(name=name, color=task_board.parse_color(request.form.get("color")),
                            created_by=current_user.id)
    db.session.add(folder_row)
    db.session.commit()
    log_action(current_user, "create", "task_folder", folder_row.id, {"name": name})
    flash(f"پوشه «{name}» ساخته شد", "success")
    return redirect(url_for("tasks_board.folder", folder_id=folder_row.id))


@bp.route("/folders/<int:folder_id>/update", methods=["POST"])
@login_required
@require("tasks")
def update_folder(folder_id):
    folder_row = _folder_or_404(folder_id)
    name = (request.form.get("name") or "").strip()[:120]
    if name and name != folder_row.name:
        if TaskFolder.query.filter(TaskFolder.name == name, TaskFolder.id != folder_id).first():
            flash(f"پوشه‌ای با نام «{name}» از قبل وجود دارد", "error")
            return redirect(_safe_back(url_for("tasks_board.folder", folder_id=folder_id)))
        folder_row.name = name
    if request.form.get("color"):
        folder_row.color = task_board.parse_color(request.form.get("color"))
    if "is_archived" in request.form:
        folder_row.is_archived = request.form.get("is_archived") == "1"
    db.session.commit()
    log_action(current_user, "update", "task_folder", folder_row.id,
               {"name": folder_row.name, "action": "archive" if folder_row.is_archived else "update"})
    flash("پوشه به‌روزرسانی شد", "success")
    if folder_row.is_archived:
        return redirect(url_for("tasks_board.index"))
    return redirect(_safe_back(url_for("tasks_board.folder", folder_id=folder_id)))


@bp.route("/folders/<int:folder_id>/delete", methods=["POST"])
@login_required
@require("tasks")
def delete_folder(folder_id):
    # پوشه‌ها مشترک‌اند و حذفشون برگشت‌پذیر نیست؛ فقط مدیر (بقیه می‌تونن بایگانی کنن)
    if not current_user.is_admin():
        abort(403)
    folder_row = _folder_or_404(folder_id)
    name = folder_row.name
    count = len(folder_row.items)
    db.session.delete(folder_row)  # حتماً از طریق ORM تا cascade زیرتسک‌ها هم اجرا بشه
    db.session.commit()
    log_action(current_user, "delete", "task_folder", folder_id, {"name": name, "count": count})
    flash(f"پوشه «{name}» با {count} تسک حذف شد", "success")
    return redirect(url_for("tasks_board.index"))


@bp.route("/folders/<int:folder_id>/bulk", methods=["POST"])
@login_required
@require("tasks")
def bulk_add(folder_id):
    folder_row = _folder_or_404(folder_id)
    due_date, error = task_board.parse_due_date(request.form.get("due_date"))
    if error:
        flash(error, "error")
        return redirect(_safe_back(url_for("tasks_board.folder", folder_id=folder_id)))

    created, skipped = task_board.bulk_add_customers(
        folder_row, request.form.getlist("customer_ids"), due_date,
        task_board._int_or_none(request.form.get("assignee_id")), current_user,
    )
    if created:
        log_action(current_user, "create", "task_item", None,
                   {"folder": folder_row.name, "count": created, "action": "bulk"})
        message = f"{created} مشتری به پوشه اضافه شد"
        if skipped:
            message += f" ({skipped} مورد از قبل در این پوشه باز بود)"
        flash(message, "success")
    else:
        flash("مشتری جدیدی اضافه نشد", "error")
    return redirect(url_for("tasks_board.folder", folder_id=folder_id))


# ---------------------------------------------------------------------------
# تسک‌ها
# ---------------------------------------------------------------------------

@bp.route("/items", methods=["POST"])
@login_required
@require("tasks")
def create_item():
    item, error = task_board.save_item(request.form, current_user)
    if error:
        flash(error, "error")
    else:
        flash("تسک ثبت شد", "success")
    return redirect(_safe_back(url_for("tasks_board.folder", folder_id=item.folder_id) if item
                               else url_for("tasks_board.index")))


@bp.route("/items/<int:item_id>", methods=["POST"])
@login_required
@require("tasks")
def update_item(item_id):
    item = _item_or_404(item_id)
    updated, error = task_board.save_item(request.form, current_user, item=item)
    if error:
        db.session.rollback()
        flash(error, "error")
    else:
        flash("تسک به‌روزرسانی شد", "success")
    return redirect(_safe_back(url_for("tasks_board.folder", folder_id=(updated or item).folder_id)))


@bp.route("/items/<int:item_id>/delete", methods=["POST"])
@login_required
@require("tasks")
def delete_item(item_id):
    item = _item_or_404(item_id)
    title, folder_id = item.title, item.folder_id
    db.session.delete(item)  # زیرتسک‌ها با cascade خود ORM پاک می‌شن
    db.session.commit()
    log_action(current_user, "delete", "task_item", item_id, {"title": title})
    flash(f"تسک «{title}» حذف شد", "success")
    return redirect(_safe_back(url_for("tasks_board.folder", folder_id=folder_id)))


@bp.route("/items/<int:item_id>/toggle", methods=["POST"])
@login_required
@require("tasks")
def toggle_item(item_id):
    done = request.form.get("done") == "1"
    item, spawned, changed = task_board.set_done(item_id, done, current_user)
    if item is None:
        abort(404)

    if _wants_json():
        return jsonify({
            "ok": True,
            "changed": changed,
            "item": task_board.item_payload(item),
            "folder": task_board.folder_progress(item.folder_id),
            "counts": task_board.view_counts(),
            "spawned": {"id": spawned.id, "due_date": to_jalali_str(spawned.due_date)} if spawned else None,
        })

    if spawned:
        flash(f"انجام شد — تکرار بعدی: {to_jalali_str(spawned.due_date)}", "success")
    return redirect(_safe_back(url_for("tasks_board.folder", folder_id=item.folder_id)))


@bp.route("/subitems/<int:subitem_id>/toggle", methods=["POST"])
@login_required
@require("tasks")
def toggle_subitem(subitem_id):
    sub = db.session.get(TaskSubitem, subitem_id) or abort(404)
    sub.is_done = request.form.get("done") == "1"
    db.session.commit()
    if _wants_json():
        item = db.session.get(TaskItem, sub.task_id)
        return jsonify({
            "ok": True,
            "id": sub.id,
            "is_done": sub.is_done,
            "done_count": sum(1 for row in item.subtasks if row.is_done) if item else 0,
            "total": len(item.subtasks) if item else 0,
        })
    return redirect(_safe_back(url_for("tasks_board.index")))


@bp.route("/customers.json")
@login_required
@require("tasks")
def customers_json():
    # همون داده‌ای که در لیست مشتری‌ها هم دیده می‌شه؛ برای کسی که اصلاً به مشتری‌ها دسترسی نداره باز نباشه
    if not (can_access("customer_profile") or can_access("dashboard") or can_access("customer_account")):
        abort(403)
    query = (request.args.get("q") or "").strip()
    if len(query) < 2:
        return jsonify({"results": []})
    return jsonify({"results": [
        {
            "id": customer.id,
            "name": customer.full_name,
            "city": customer.city or "",
            "phone": customer.mobile_phone or customer.landline_phone or "",
        }
        for customer in customer_service.search_by_name(query, limit=20)
    ]})
