/**
 * Thông báo cá nhân (biểu tượng chuông) — gom mọi thứ liên quan tới tài khoản đang đăng nhập
 * theo 5 nhóm: Công ty · Khu vực · Phòng · Team · Cá nhân (Team = phòng ban nằm từ cấp thứ 3 của cây tổ chức).
 *
 * Không có bảng thông báo riêng: mọi mục đều suy ra từ dữ liệu sẵn có (tin đăng, đề xuất, đơn hàng,
 * công việc), nên không thể lệch với thực tế. Chỉ lưu mốc "lần cuối mở chuông" (users.notificationsSeenAt)
 * để đếm số mục mới. Mục "cần xử lý" (đề xuất chờ duyệt, đơn chờ duyệt...) luôn được tính là mới cho tới
 * khi xử lý xong, dù đã mở chuông.
 */
import { and, desc, eq, gte, inArray, ne, sql } from "drizzle-orm";
import { db } from "@/db";
import { orders, proposalApprovers, proposals, tasks, users, customers, alerts, customKpiEntries, customKpis } from "@/db/schema";
import type { SessionUser } from "./auth";
import { visibleAnnouncements } from "./announcements";
import { canApproveOrders, departmentPaths, visibleUserIds } from "./rbac";
import { monthKey, today } from "./period";
import { OPEN_TASK_STATUSES, TASK_STATUS_LABEL } from "./tasks";
import { formatDate } from "./format";

export type NotificationGroup = "COMPANY" | "TERRITORY" | "DEPARTMENT" | "TEAM" | "PERSONAL";

export type NotificationItem = {
  id: string;
  group: NotificationGroup;
  title: string;
  text?: string;
  href: string;
  at: Date;
  /** Việc đang chờ chính người này xử lý. */
  needsAction: boolean;
  /** Chưa xem: mới hơn lần mở chuông cuối, hoặc còn cần xử lý. */
  unread: boolean;
};

export const GROUP_LABEL: Record<NotificationGroup, string> = {
  COMPANY: "Công ty",
  TERRITORY: "Khu vực",
  DEPARTMENT: "Phòng",
  TEAM: "Team",
  PERSONAL: "Cá nhân",
};

const RECENT_DAYS = 14;

export async function getNotifications(session: SessionUser): Promise<NotificationItem[]> {
  const since = new Date(Date.now() - RECENT_DAYS * 24 * 60 * 60 * 1000);

  const [me] = await db
    .select({ seenAt: users.notificationsSeenAt })
    .from(users)
    .where(eq(users.id, session.userId))
    .limit(1);
  const seenAt = me?.seenAt ?? null;

  const items: Omit<NotificationItem, "unread">[] = [];

  // --- Tin đăng theo phạm vi ---
  // Mọi tin còn hạn đều hiện (không chỉ tin mới) để người dùng luôn xem lại được thông báo đang có hiệu lực.
  // "Mới" nghĩa là đăng sau lần cuối mở chuông; người chưa mở lần nào thì chỉ tính tin đăng trong 14 ngày gần đây.
  const news = await visibleAnnouncements(session);
  const paths = await departmentPaths(session.tenantId);
  for (const row of news) {
    // Phòng ban nằm sâu từ cấp 3 (Công ty › Phòng › Team) là Team; còn lại là Phòng.
    const depth = row.scopeId ? (paths.get(row.scopeId)?.split(" › ").length ?? 1) : 1;
    const group: NotificationGroup =
      row.scopeType === "DEPARTMENT" ? (depth >= 3 ? "TEAM" : "DEPARTMENT") : row.scopeType;
    items.push({
      id: `an-${row.id}`,
      group,
      title: row.isPinned ? `📌 ${row.title}` : row.title,
      text: row.body.length > 140 ? `${row.body.slice(0, 140)}…` : row.body,
      href: "/",
      at: row.createdAt,
      needsAction: false,
    });
  }

  // --- Đề xuất chờ tôi duyệt ---
  const waiting = await db
    .select({ id: proposals.id, title: proposals.title, createdAt: proposals.createdAt, by: users.fullName, step: proposalApprovers.stepLabel })
    .from(proposalApprovers)
    .innerJoin(proposals, eq(proposalApprovers.proposalId, proposals.id))
    .innerJoin(users, eq(proposals.createdBy, users.id))
    .where(
      and(
        eq(proposalApprovers.tenantId, session.tenantId),
        eq(proposalApprovers.approverId, session.userId),
        eq(proposalApprovers.decision, "PENDING"),
        eq(proposals.status, "PENDING"),
        // Duyệt lần lượt: chỉ báo khi ĐẾN LƯỢT (mọi bước trước đã duyệt), không báo sớm cho người ở bước sau.
        sql`not exists (
          select 1 from proposal_approvers p2
          where p2.proposal_id = proposals.id and p2.sort_order < proposal_approvers.sort_order and p2.decision <> 'APPROVED'
        )`,
      ),
    );
  for (const row of waiting) {
    items.push({
      id: `pp-${row.id}`,
      group: "PERSONAL",
      title: `Đề xuất chờ bạn duyệt: ${row.title}`,
      text: `Người đề xuất: ${row.by}${row.step ? ` · bước ${row.step}` : ""}`,
      href: "/cong-viec/de-xuat?xem=duyet",
      at: row.createdAt,
      needsAction: true,
    });
  }

  // --- Đề xuất của tôi đã có kết quả ---
  const decided = await db
    .select({ id: proposals.id, title: proposals.title, status: proposals.status, at: sql<Date | null>`coalesce(proposals.decided_at, (select max(pa.decided_at) from proposal_approvers pa where pa.proposal_id = proposals.id))` })
    .from(proposals)
    .where(
      and(
        eq(proposals.tenantId, session.tenantId),
        eq(proposals.createdBy, session.userId),
        ne(proposals.status, "PENDING"),
        // Phiếu do chính mình thu hồi không phải "kết quả duyệt".
        sql`${proposals.details}->>'withdrawnAt' is null`,
      ),
    );
  for (const row of decided) {
    const at = row.at ? new Date(row.at) : null;
    if (!at || at < since) continue;
    items.push({
      id: `pr-${row.id}`,
      group: "PERSONAL",
      title: `Đề xuất “${row.title}” ${row.status === "APPROVED" ? "đã được duyệt" : "bị từ chối"}`,
      href: "/cong-viec/de-xuat",
      at,
      needsAction: false,
    });
  }

  // --- Đơn hàng: chờ tôi duyệt (người duyệt) ---
  if (canApproveOrders(session)) {
    const visible = session.role === "DIRECTOR" ? null : await visibleUserIds(session);
    const pending = await db
      .select({ id: orders.id, code: orders.code, createdAt: orders.createdAt, customer: customers.orgName })
      .from(orders)
      .innerJoin(customers, eq(orders.customerId, customers.id))
      .where(
        and(
          eq(orders.tenantId, session.tenantId),
          eq(orders.status, "DRAFT"),
          eq(orders.needsRevision, false),
          visible ? (visible.length > 0 ? inArray(orders.salesUserId, visible) : sql`false`) : undefined,
        ),
      )
      .orderBy(desc(orders.createdAt))
      .limit(20);
    for (const row of pending) {
      items.push({
        id: `od-${row.id}`,
        group: "PERSONAL",
        title: `Đơn ${row.code} chờ duyệt`,
        text: row.customer,
        href: `/don-hang/${row.id}`,
        at: row.createdAt,
        needsAction: true,
      });
    }
  }

  // --- Đơn hàng của tôi: đã duyệt / cần điều chỉnh ---
  const mine = await db
    .select({
      id: orders.id,
      code: orders.code,
      status: orders.status,
      approvedAt: orders.approvedAt,
      needsRevision: orders.needsRevision,
      revisionNote: orders.revisionNote,
      createdAt: orders.createdAt,
    })
    .from(orders)
    .where(and(eq(orders.tenantId, session.tenantId), eq(orders.salesUserId, session.userId), gte(orders.createdAt, since)));
  for (const row of mine) {
    if (row.needsRevision) {
      items.push({
        id: `or-${row.id}`,
        group: "PERSONAL",
        title: `Đơn ${row.code} cần điều chỉnh`,
        text: row.revisionNote ?? undefined,
        href: `/don-hang/${row.id}`,
        at: row.createdAt,
        needsAction: true,
      });
    } else if (row.status === "CONFIRMED" && row.approvedAt && row.approvedAt >= since) {
      items.push({
        id: `oa-${row.id}`,
        group: "PERSONAL",
        title: `Đơn ${row.code} đã được duyệt`,
        href: `/don-hang/${row.id}`,
        at: row.approvedAt,
        needsAction: false,
      });
    }
  }

  // --- Công việc của tôi: mới được giao, đến hạn hôm nay, quá hạn ---
  // Mỗi việc chỉ hiện một dòng (ưu tiên quá hạn > hôm nay > mới giao) để chuông không bị lặp.
  const todayIso = today();
  const myTasks = await db
    .select({
      id: tasks.id,
      title: tasks.title,
      status: tasks.status,
      createdAt: tasks.createdAt,
      createdBy: tasks.createdBy,
      startDate: tasks.startDate,
      dueDate: tasks.dueDate,
      by: users.fullName,
    })
    .from(tasks)
    .innerJoin(users, eq(tasks.createdBy, users.id))
    .where(
      and(
        eq(tasks.tenantId, session.tenantId),
        eq(tasks.assigneeId, session.userId),
        inArray(tasks.status, OPEN_TASK_STATUSES),
      ),
    );
  for (const row of myTasks) {
    const assignedByOther = row.createdBy !== session.userId;
    const overdue = !!row.dueDate && row.dueDate < todayIso;
    const dueToday = row.dueDate === todayIso;
    const startsToday = row.startDate === todayIso && row.status === "NOT_STARTED";
    const isNew = assignedByOther && row.status === "NOT_STARTED";
    if (!overdue && !dueToday && !startsToday && !isNew) continue;
    const title = overdue
      ? `Công việc quá hạn: ${row.title}`
      : dueToday
        ? `Công việc đến hạn hôm nay: ${row.title}`
        : startsToday && !isNew
          ? `Hôm nay bắt đầu: ${row.title}`
          : `Công việc mới: ${row.title}`;
    items.push({
      id: `tk-${row.id}`,
      group: "PERSONAL",
      title,
      text: `${assignedByOther ? `Giao bởi ${row.by}` : "Việc của bạn"}${row.dueDate ? ` · hạn ${formatDate(row.dueDate)}` : ""}`,
      href: "/cong-viec/danh-sach",
      // Việc quá hạn/đến hạn luôn hiện ở đầu (đang cần xử lý); việc mới giao lấy giờ tạo.
      at: overdue || dueToday ? new Date() : row.createdAt,
      needsAction: true,
    });
  }

  // --- Việc tôi giao cho người khác vừa được cập nhật trạng thái ---
  const delegated = await db
    .select({
      id: tasks.id,
      title: tasks.title,
      status: tasks.status,
      reason: tasks.reason,
      updatedAt: tasks.updatedAt,
      createdAt: tasks.createdAt,
      assignee: users.fullName,
    })
    .from(tasks)
    .innerJoin(users, eq(tasks.assigneeId, users.id))
    .where(
      and(
        eq(tasks.tenantId, session.tenantId),
        eq(tasks.createdBy, session.userId),
        ne(tasks.assigneeId, session.userId),
        gte(tasks.updatedAt, since),
      ),
    );
  for (const row of delegated) {
    // updatedAt mặc định = lúc tạo: chỉ báo khi người nhận đã đổi trạng thái sau đó.
    if (row.status === "NOT_STARTED" || row.updatedAt.getTime() - row.createdAt.getTime() < 1000) continue;
    items.push({
      id: `tu-${row.id}`,
      group: "PERSONAL",
      title: `${row.assignee} cập nhật việc “${row.title}”: ${TASK_STATUS_LABEL[row.status] ?? row.status}`,
      text: row.reason ? `Lý do: ${row.reason}` : undefined,
      href: "/cong-viec/danh-sach?xem=nhom",
      at: row.updatedAt,
      needsAction: false,
    });
  }

  // --- Khách tiềm năng được giao mà chưa liên hệ ---
  const [pendingLeads] = await db
    .select({ count: sql<number>`count(*)::int`, latest: sql<string>`max(${customers.createdAt})` })
    .from(customers)
    .where(
      and(
        eq(customers.tenantId, session.tenantId),
        eq(customers.ownerId, session.userId),
        eq(customers.isActive, true),
        eq(customers.stage, "LEAD"),
        eq(customers.leadStatus, "NOT_CONTACTED"),
      ),
    );
  if (Number(pendingLeads?.count ?? 0) > 0) {
    items.push({
      id: "ld-pending",
      group: "PERSONAL",
      title: `Có ${pendingLeads.count} khách tiềm năng chưa liên hệ`,
      text: "Liên hệ rồi báo cáo kết quả để cập nhật trạng thái chuyển đổi.",
      href: "/khach-hang/tiem-nang?tt=NOT_CONTACTED",
      at: pendingLeads.latest ? new Date(pendingLeads.latest) : new Date(),
      needsAction: true,
    });
  }

  // --- Đơn của tôi bị huỷ / từ chối ---
  for (const row of mine) {
    if (row.status === "CANCELLED") {
      items.push({
        id: `oc-${row.id}`,
        group: "PERSONAL",
        title: `Đơn ${row.code} đã bị huỷ hoặc từ chối`,
        href: `/don-hang/${row.id}`,
        at: row.createdAt,
        needsAction: false,
      });
    }
  }

  // --- Cảnh báo kinh doanh về chính tôi (ví dụ không đạt chỉ tiêu 2 tháng liên tiếp) ---
  const aboutMe = await db
    .select({ id: alerts.id, title: alerts.title, createdAt: alerts.createdAt })
    .from(alerts)
    .where(
      and(
        eq(alerts.tenantId, session.tenantId),
        eq(alerts.entityType, "user"),
        eq(alerts.entityId, session.userId),
        eq(alerts.status, "OPEN"),
      ),
    );
  for (const row of aboutMe) {
    items.push({ id: `al-${row.id}`, group: "PERSONAL", title: row.title, href: "/", at: row.createdAt, needsAction: false });
  }

  // --- KPI tự tạo mới được giao trong tháng này ---
  const kpiNew = await db
    .select({ id: customKpiEntries.id, name: customKpis.name, unit: customKpis.unit, target: customKpiEntries.targetValue, at: customKpiEntries.updatedAt })
    .from(customKpiEntries)
    .innerJoin(customKpis, eq(customKpiEntries.kpiId, customKpis.id))
    .where(
      and(
        eq(customKpiEntries.tenantId, session.tenantId),
        eq(customKpiEntries.userId, session.userId),
        eq(customKpiEntries.periodKey, monthKey(today())),
        gte(customKpiEntries.updatedAt, since),
      ),
    );
  for (const row of kpiNew) {
    items.push({
      id: `kp-${row.id}`,
      group: "PERSONAL",
      title: `KPI được giao: ${row.name}`,
      text: `Chỉ tiêu ${row.target} ${row.unit} trong tháng này`,
      href: "/cong-viec",
      at: row.at,
      needsAction: false,
    });
  }

  return items
    .map((item) => ({
      ...item,
      // Tin đăng cũ chưa từng mở chuông không được tính là "mới" — chỉ tin trong 14 ngày gần đây.
      unread: item.needsAction || item.at > (seenAt ?? since),
    }))
    .sort((a, b) => Number(b.needsAction) - Number(a.needsAction) || b.at.getTime() - a.at.getTime());
}

export async function unreadNotificationCount(session: SessionUser): Promise<number> {
  return (await getNotifications(session)).filter((item) => item.unread).length;
}
