from __future__ import annotations

from datetime import date, timedelta
from typing import Any

from sqlalchemy import select
from sqlalchemy.orm import Session, selectinload

from app.modules.clients.models import Client
from app.modules.core.rbac.models import Role, UserRole
from app.modules.employees.service import build_employee_scope, get_employee_for_user
from app.modules.services.execution import CLOSED_TASK_STATUSES
from app.modules.services.models import ClientServiceSubscription, ClientServiceTaskInstance, ServiceCatalogue, ServiceTaskComment

STAFF_ROLES = {"Staff", "Branch Manager", "Manager", "System Admin", "Firm Admin", "Partner"}
STAFF_PERMISSIONS = {"employees.work.view_self", "employees.ess.view", "services.tasks.update_self"}
CLIENT_PENDING_STATUSES = {"blocked", "client_pending", "waiting_client", "documents_pending"}
RETURNED_STATUSES = {"returned", "correction_required", "rework", "rejected"}
REVIEW_STATUSES = {"completed", "ready_review", "review_pending", "pending_review", "manager_review"}


def get_user_role_names(db: Session, user_id: int) -> list[str]:
    return list(
        db.execute(
            select(Role.name)
            .join(UserRole, UserRole.role_id == Role.id)
            .where(UserRole.user_id == int(user_id), Role.is_active.is_(True))
            .order_by(Role.name.asc())
        ).scalars().all()
    )


def get_user_permission_names(db: Session, user_id: int) -> set[str]:
    rows = db.execute(
        select(Role.permissions)
        .join(UserRole, UserRole.role_id == Role.id)
        .where(UserRole.user_id == int(user_id), Role.is_active.is_(True))
    ).scalars().all()
    permissions: set[str] = set()
    for value in rows:
        if isinstance(value, (list, tuple, set)):
            permissions.update(str(v) for v in value if v)
        elif isinstance(value, str):
            cleaned = value.replace("[", "").replace("]", "").replace('"', "").replace("'", "")
            permissions.update(v.strip() for v in cleaned.split(",") if v.strip())
    return permissions


def can_access_staff_dashboard(db: Session, current_user) -> bool:
    roles = set(get_user_role_names(db, current_user.id))
    if roles.intersection(STAFF_ROLES):
        return True
    if get_employee_for_user(db, current_user):
        return True
    return bool(get_user_permission_names(db, current_user.id).intersection(STAFF_PERMISSIONS))


def _active_financial_year(request) -> str | None:
    value = request.session.get("active_financial_year") or getattr(request.state, "year_code", None)
    value = (value or "").strip()
    return value or None


def _scope(db: Session, request, current_user):
    return build_employee_scope(
        db,
        current_user,
        tenant_id=request.session.get("active_tenant_id") or getattr(current_user, "tenant_id", None),
        branch_id=request.session.get("active_branch_id"),
    )


def _is_open(status: str | None) -> bool:
    return (status or "pending").strip().lower() not in CLOSED_TASK_STATUSES


def _status_label(status: str | None) -> str:
    return (status or "pending").replace("_", " ").replace("-", " ").title()


def _days_overdue(due_date) -> int:
    if not due_date:
        return 0
    delta = (date.today() - due_date).days
    return delta if delta > 0 else 0


def _task_href(task: ClientServiceTaskInstance) -> str:
    task_id = getattr(task, "id", None)
    if task_id:
        return f"/employees/work/tasks/{task_id}/communication"
    return "/employees/work"


def _service_name(task: ClientServiceTaskInstance) -> str:
    catalogue = getattr(task, "catalogue", None)
    service_name = getattr(catalogue, "service_name", None) or getattr(catalogue, "name", None)
    if not service_name:
        subscription = getattr(task, "subscription", None)
        sub_catalogue = getattr(subscription, "catalogue", None) if subscription else None
        service_name = getattr(sub_catalogue, "service_name", None) or getattr(sub_catalogue, "name", None)
    return service_name or "Service"


def _task_row(task: ClientServiceTaskInstance) -> dict[str, Any]:
    today = date.today()
    client = getattr(task, "client", None)
    status = (getattr(task, "status", None) or "pending").strip().lower()
    due_date = getattr(task, "internal_target_date", None)
    is_open = _is_open(status)
    return {
        "id": getattr(task, "id", None),
        "client_name": getattr(client, "client_name", None) or "Unlinked Client",
        "client_code": getattr(client, "client_code", None) or "",
        "service_name": _service_name(task),
        "task_name": getattr(task, "task_name", None) or "Task",
        "period": getattr(task, "financial_year", None) or "-",
        "due_date": due_date,
        "status": status,
        "status_label": _status_label(status),
        "priority": getattr(task, "priority", None) or "normal",
        "remarks": getattr(task, "remarks", None) or "",
        "comment_count": len([c for c in getattr(task, "comments", []) if not getattr(c, "is_deleted", False)]),
        "document_count": len(getattr(task, "documents", []) or []),
        "is_open": is_open,
        "is_overdue": bool(due_date and due_date < today and is_open),
        "is_due_today": bool(due_date and due_date == today and is_open),
        "is_due_week": bool(due_date and today <= due_date <= today + timedelta(days=7) and is_open),
        "days_overdue": _days_overdue(due_date) if is_open else 0,
        "href": _task_href(task),
    }


def _load_my_tasks(db: Session, scope, fy: str | None) -> list[ClientServiceTaskInstance]:
    stmt = (
        select(ClientServiceTaskInstance)
        .options(
            selectinload(ClientServiceTaskInstance.client),
            selectinload(ClientServiceTaskInstance.catalogue),
            selectinload(ClientServiceTaskInstance.assigned_to),
            selectinload(ClientServiceTaskInstance.comments).selectinload(ServiceTaskComment.created_by),
            selectinload(ClientServiceTaskInstance.subscription).selectinload(ClientServiceSubscription.catalogue),
            selectinload(ClientServiceTaskInstance.documents),
        )
        .where(
            ClientServiceTaskInstance.tenant_id == scope.tenant_id,
            ClientServiceTaskInstance.assigned_to_user_id == scope.own_user_id,
            ClientServiceTaskInstance.is_active.is_(True),
        )
    )
    if getattr(scope, "branch_id", None) is not None:
        stmt = stmt.where(ClientServiceTaskInstance.branch_id == scope.branch_id)
    if fy:
        stmt = stmt.where(ClientServiceTaskInstance.financial_year == fy)
    stmt = stmt.order_by(
        ClientServiceTaskInstance.internal_target_date.is_(None),
        ClientServiceTaskInstance.internal_target_date.asc(),
        ClientServiceTaskInstance.priority.desc(),
        ClientServiceTaskInstance.id.desc(),
    ).limit(250)
    return list(db.execute(stmt).scalars().all())


def _report_cards() -> list[dict[str, str]]:
    return [
        {"group": "My Work", "title": "My Pending Tasks", "desc": "All open work assigned to me.", "href": "/staff/dashboard?tab=my-tasks"},
        {"group": "Due Control", "title": "Due Today", "desc": "Tasks that must be acted on today.", "href": "/staff/dashboard?tab=due-today"},
        {"group": "Due Control", "title": "Overdue Tasks", "desc": "Assigned open work past internal target date.", "href": "/staff/dashboard?tab=overdue"},
        {"group": "Client Follow-up", "title": "Client Pending", "desc": "Tasks blocked due to client data, documents or clarification pending.", "href": "/staff/dashboard?tab=client-pending"},
        {"group": "Documents", "title": "Document Pending", "desc": "Assigned work requiring document upload or verification follow-up.", "href": "/staff/dashboard?tab=documents"},
        {"group": "Correction", "title": "Returned Work", "desc": "Work returned for correction or rework.", "href": "/staff/dashboard?tab=returned-work"},
    ]


def _wizard_cards() -> list[dict[str, str]]:
    return [
        {"title": "My Task Work Wizard", "desc": "Open assigned task, read instructions, update status and add work notes.", "href": "/employees/work"},
        {"title": "Document Upload Wizard", "desc": "Open document area for uploading supporting records and work papers.", "href": "/documents"},
        {"title": "Mark Client Pending Wizard", "desc": "Use task communication to record document or clarification pending from client.", "href": "/staff/dashboard?tab=client-pending"},
        {"title": "Submit for Review Wizard", "desc": "Open completed tasks and submit them through existing task communication workflow.", "href": "/staff/dashboard?tab=my-tasks"},
        {"title": "Time / Remarks Wizard", "desc": "Update remarks and progress on current assigned task using existing work screen.", "href": "/employees/work"},
        {"title": "Correction / Returned Work Wizard", "desc": "Open returned tasks and complete rework before resubmission.", "href": "/staff/dashboard?tab=returned-work"},
    ]


def build_staff_dashboard_payload(db: Session, request, current_user) -> dict[str, Any]:
    scope = _scope(db, request, current_user)
    fy = _active_financial_year(request)
    today = date.today()
    tasks = _load_my_tasks(db, scope, fy)
    task_rows = [_task_row(t) for t in tasks]
    open_rows = [r for r in task_rows if r["is_open"]]
    completed_rows = [r for r in task_rows if not r["is_open"]]
    due_today_rows = [r for r in open_rows if r["is_due_today"]]
    overdue_rows = [r for r in open_rows if r["is_overdue"]]
    due_week_rows = [r for r in open_rows if r["is_due_week"]]
    client_pending_rows = [r for r in open_rows if r["status"] in CLIENT_PENDING_STATUSES]
    returned_rows = [r for r in open_rows if r["status"] in RETURNED_STATUSES]
    documents_rows = [r for r in open_rows if r["document_count"] > 0 or r["status"] in CLIENT_PENDING_STATUSES]
    review_rows = [r for r in task_rows if r["status"] in REVIEW_STATUSES]
    in_progress_rows = [r for r in open_rows if r["status"] == "in_progress"]

    status_summary: dict[str, int] = {}
    service_summary: dict[str, int] = {}
    for row in open_rows:
        status_summary[row["status_label"]] = status_summary.get(row["status_label"], 0) + 1
        service_summary[row["service_name"]] = service_summary.get(row["service_name"], 0) + 1

    employee = get_employee_for_user(db, current_user)
    overview = {
        "tenant_id": getattr(scope, "tenant_id", None),
        "branch_id": getattr(scope, "branch_id", None),
        "financial_year": fy,
        "employee": employee,
        "today": today,
        "total_tasks": len(task_rows),
        "open_count": len(open_rows),
        "in_progress_count": len(in_progress_rows),
        "due_today_count": len(due_today_rows),
        "due_week_count": len(due_week_rows),
        "overdue_count": len(overdue_rows),
        "client_pending_count": len(client_pending_rows),
        "documents_count": len(documents_rows),
        "returned_count": len(returned_rows),
        "review_count": len(review_rows),
        "completed_count": len(completed_rows),
    }

    return {
        "roles": sorted(get_user_role_names(db, current_user.id)),
        "overview": overview,
        "my_tasks": open_rows[:100],
        "due_today": due_today_rows[:50],
        "due_week": due_week_rows[:50],
        "overdue": overdue_rows[:50],
        "client_pending": client_pending_rows[:50],
        "documents_pending": documents_rows[:50],
        "returned_work": returned_rows[:50],
        "completed": completed_rows[:50],
        "review_ready": review_rows[:50],
        "status_summary": sorted([{"label": k, "count": v} for k, v in status_summary.items()], key=lambda x: x["count"], reverse=True)[:10],
        "service_summary": sorted([{"label": k, "count": v} for k, v in service_summary.items()], key=lambda x: x["count"], reverse=True)[:10],
        "reports": _report_cards(),
        "wizards": _wizard_cards(),
    }
