/**
 * Thông báo trang chủ — mỗi tin gắn một phạm vi hiển thị (công ty/phòng ban/khu
 * vực). Số lượng tin luôn nhỏ (vài chục), nên lọc theo phạm vi thực hiện ngay
 * trong JavaScript thay vì dựng truy vấn SQL động theo từng loại phạm vi.
 *
 * Tin có thể ghim (luôn lên đầu), có hạn hiển thị (tự ẩn sau ngày hết hạn), có tệp
 * đính kèm (ảnh/file) và có thể là tin lịch sự kiện (`eventDate`).
 */
import { eq, and, desc } from "drizzle-orm";
import { db } from "@/db";
import { announcements, users } from "@/db/schema";
import {
  departmentSubtree,
  visibleDepartmentIds,
  assignedTerritoryIds,
  expandTerritoryDescendants,
  visibleTerritoryIds,
} from "./rbac";
import { today } from "./period";
import type { SessionUser } from "./auth";

export type AnnouncementRow = {
  id: string;
  title: string;
  body: string;
  scopeType: "COMPANY" | "DEPARTMENT" | "TERRITORY";
  scopeId: string | null;
  isPinned: boolean;
  expiresAt: string | null;
  eventDate: string | null;
  attachmentName: string | null;
  attachmentPath: string | null;
  attachmentMime: string | null;
  createdBy: string | null;
  createdAt: Date;
};

/** `includeExpired`: trang quản lý cần thấy cả tin đã hết hạn để gia hạn hoặc gỡ. */
export async function visibleAnnouncements(
  session: SessionUser,
  options: { includeExpired?: boolean } = {},
): Promise<AnnouncementRow[]> {
  const raw = await db
    .select({
      id: announcements.id,
      title: announcements.title,
      body: announcements.body,
      scopeType: announcements.scopeType,
      scopeId: announcements.scopeId,
      isPinned: announcements.isPinned,
      expiresAt: announcements.expiresAt,
      eventDate: announcements.eventDate,
      attachmentName: announcements.attachmentName,
      attachmentPath: announcements.attachmentPath,
      attachmentMime: announcements.attachmentMime,
      createdBy: announcements.createdBy,
      createdAt: announcements.createdAt,
    })
    .from(announcements)
    .where(and(eq(announcements.tenantId, session.tenantId), eq(announcements.isActive, true)))
    .orderBy(desc(announcements.createdAt));

  const todayIso = today();
  const all = raw
    .filter((row) => options.includeExpired || !row.expiresAt || row.expiresAt >= todayIso)
    // Ghim lên đầu, còn lại giữ thứ tự mới nhất trước (sort ổn định).
    .sort((a, b) => Number(b.isPinned) - Number(a.isPinned));

  if (session.role === "DIRECTOR") return all;

  // Cùng phạm vi đang dùng để lọc dữ liệu nghiệp vụ (khách hàng, đơn hàng) —
  // ai quản/thấy được phòng ban hoặc khu vực nào thì cũng thấy thông báo đăng cho
  // phòng ban/khu vực đó, kể cả khi mình ở cấp cao hơn nơi đăng tin.
  const myDepartmentIds = (await visibleDepartmentIds(session)) ?? [];
  const myTerritoryIds = await assignedTerritoryIds(session);
  // Khu vực mình phụ trách đã bung xuống cấp con — để cha (GĐ khu vực, quản lý vùng) cũng thấy tin đăng cho khu vực con của mình.
  const myTerritoryTree = (await visibleTerritoryIds(session)) ?? [];

  const visible: AnnouncementRow[] = [];
  for (const row of all) {
    // Người đăng luôn thấy tin của chính mình.
    if (row.createdBy === session.userId) {
      visible.push(row);
      continue;
    }
    if (row.scopeType === "COMPANY") {
      visible.push(row);
      continue;
    }
    if (row.scopeType === "DEPARTMENT" && row.scopeId) {
      // Tin đăng cho phòng ban X hiển thị cho X và toàn bộ nhánh con của X.
      const targetSubtree = await departmentSubtree(session.tenantId, [row.scopeId]);
      if (myDepartmentIds.some((id) => targetSubtree.includes(id))) visible.push(row);
      continue;
    }
    if (row.scopeType === "TERRITORY" && row.scopeId && (myTerritoryIds.length > 0 || myTerritoryTree.length > 0)) {
      // Thấy khi khu vực của mình nằm trong khu vực đăng tin (tin cho miền → thấy ở tỉnh), hoặc khu vực đăng tin nằm dưới mình (cha thấy con).
      const targetSubtree = await expandTerritoryDescendants(session.tenantId, [row.scopeId]);
      if (myTerritoryIds.some((id) => targetSubtree.includes(id)) || myTerritoryTree.includes(row.scopeId)) visible.push(row);
    }
  }
  return visible;
}

/**
 * Phạm vi mà một người được phép đăng tin (yêu cầu mục 3):
 *  - Ban giám đốc: mọi phạm vi.
 *  - Người được cấp riêng quyền tải văn bản (thường là Hành chính nhân sự): toàn công ty.
 *  - Giám đốc khu vực / Quản lý: phòng ban trong nhánh mình phụ trách và khu vực được giao.
 *  - NVKD: không đăng được.
 * `null` ở danh sách nghĩa là không giới hạn.
 */
export type PosterScopes = {
  company: boolean;
  departmentIds: string[] | null;
  territoryIds: string[] | null;
};

export async function posterScopes(session: SessionUser): Promise<PosterScopes> {
  if (session.role === "DIRECTOR") return { company: true, departmentIds: null, territoryIds: null };

  const [me] = await db
    .select({
      canUploadDocuments: users.canUploadDocuments,
      canPostAnnouncements: users.canPostAnnouncements,
      departmentId: users.departmentId,
      territoryId: users.territoryId,
    })
    .from(users)
    .where(eq(users.id, session.userId))
    .limit(1);
  const company = !!me?.canUploadDocuments;

  if (session.role === "SALES") {
    // Người được ủy quyền (Trợ lý...) đăng cho phòng ban/khu vực mình đang thuộc, gồm cả các nhánh con.
    if (!me?.canPostAnnouncements) return { company, departmentIds: [], territoryIds: [] };
    return {
      company,
      departmentIds: me.departmentId ? await departmentSubtree(session.tenantId, [me.departmentId]) : [],
      territoryIds: me.territoryId ? await expandTerritoryDescendants(session.tenantId, [me.territoryId]) : [],
    };
  }

  return {
    company,
    departmentIds: (await visibleDepartmentIds(session)) ?? [],
    territoryIds: (await visibleTerritoryIds(session)) ?? [],
  };
}

export function canPostAnywhere(scopes: PosterScopes): boolean {
  return (
    scopes.company ||
    scopes.departmentIds === null ||
    scopes.territoryIds === null ||
    scopes.departmentIds.length > 0 ||
    scopes.territoryIds.length > 0
  );
}

export function canPostTo(
  scopes: PosterScopes,
  scopeType: "COMPANY" | "DEPARTMENT" | "TERRITORY",
  scopeId: string | null,
): boolean {
  if (scopeType === "COMPANY") return scopes.company;
  if (!scopeId) return false;
  const list = scopeType === "DEPARTMENT" ? scopes.departmentIds : scopes.territoryIds;
  return list === null || list.includes(scopeId);
}
