/**
 * Quản lý tài khoản theo cha–con (yêu cầu mục 1–2: "cha phân quyền cho con", "Admin thêm, sửa, chặn, cấp lại mật khẩu,
 * điều chuyển").
 *
 * Nguyên tắc:
 *  - Ban giám đốc (DIRECTOR) quản lý mọi tài khoản.
 *  - Giám đốc khu vực và Quản lý quản lý được tài khoản **nằm dưới mình**: cùng nhánh phòng ban VÀ vai trò thấp hơn.
 *    Không đụng được tài khoản cùng cấp, cấp trên, hay chính mình (chính mình sửa ở "Tài khoản của tôi").
 *  - Chỉ được tạo/đổi sang vai trò thấp hơn vai trò của mình, chỉ được đặt vào phòng ban và khu vực trong phạm vi mình.
 *    Nhờ vậy cha không thể cấp cho con nhiều quyền hơn chính cha.
 *  - Cờ quyền riêng (tải văn bản, sửa thông tin nhân viên): chỉ cấp được cờ mà chính mình đang có.
 *  - Người chưa được gán phòng ban thì không có quyền quản lý ai (khác với phạm vi xem dữ liệu, ở đây KHÔNG lùi về "không giới hạn").
 */
import "server-only";
import { eq, inArray } from "drizzle-orm";
import type { PgColumn, PgTable } from "drizzle-orm/pg-core";
import { db } from "@/db";
import * as t from "@/db/schema";
import { users } from "@/db/schema";
import type { SessionUser } from "./auth";
import { visibleDepartmentIds, visibleTerritoryIds, visibleUserIds } from "./rbac";

export type Role = "DIRECTOR" | "GDKV" | "MANAGER" | "SALES";

export const ROLE_RANK: Record<Role, number> = { SALES: 1, MANAGER: 2, GDKV: 3, DIRECTOR: 4 };

export type AccountScope = {
  /** Ban giám đốc: không giới hạn. */
  all: boolean;
  /** Tài khoản nằm dưới (không gồm chính mình). Bỏ qua khi `all`. */
  userIds: string[];
  departmentIds: string[];
  territoryIds: string[];
  /** Vai trò được phép tạo / gán. */
  assignableRoles: Role[];
  /** Cờ quyền riêng mà người này đang có — chỉ được cấp lại đúng những cờ này. */
  holdsUploadDocuments: boolean;
  holdsEditStaffInfo: boolean;
  /** Đăng được thông báo trong phạm vi mình — Giám đốc khu vực / Quản lý luôn có; cha chỉ cấp lại quyền này khi bản thân có. */
  holdsPostAnnouncements: boolean;
};

export async function accountScope(session: SessionUser): Promise<AccountScope | null> {
  if (session.role === "DIRECTOR") {
    return {
      all: true,
      userIds: [],
      departmentIds: [],
      territoryIds: [],
      assignableRoles: ["DIRECTOR", "GDKV", "MANAGER", "SALES"],
      holdsUploadDocuments: true,
      holdsEditStaffInfo: true,
      holdsPostAnnouncements: true,
    };
  }
  if (session.role === "SALES") return null;

  const departmentIds = (await visibleDepartmentIds(session)) ?? [];
  if (departmentIds.length === 0) return null;

  const [userIds, territoryIds, [me]] = await Promise.all([
    visibleUserIds(session),
    visibleTerritoryIds(session),
    db
      .select({ up: users.canUploadDocuments, es: users.canEditStaffInfo })
      .from(users)
      .where(eq(users.id, session.userId))
      .limit(1),
  ]);

  const myRank = ROLE_RANK[session.role];
  return {
    all: false,
    userIds: (userIds ?? []).filter((id) => id !== session.userId),
    departmentIds,
    territoryIds: territoryIds ?? [],
    assignableRoles: (Object.keys(ROLE_RANK) as Role[]).filter((r) => ROLE_RANK[r] < myRank),
    holdsUploadDocuments: !!me?.up,
    holdsEditStaffInfo: !!me?.es,
    holdsPostAnnouncements: true,
  };
}

/** Người xem có quản lý được tài khoản này không (theo cha–con). */
export function canManageAccount(
  session: SessionUser,
  scope: AccountScope | null,
  target: { id: string; role: Role },
): boolean {
  if (!scope) return false;
  if (scope.all) return true;
  return (
    target.id !== session.userId &&
    scope.userIds.includes(target.id) &&
    ROLE_RANK[target.role] < ROLE_RANK[session.role]
  );
}

/** Kiểm tra vai trò / phòng ban / khu vực muốn đặt cho một tài khoản có nằm trong phạm vi người thao tác không. */
export function validateAssignment(
  scope: AccountScope,
  input: { role: Role; departmentId?: string | null; territoryId?: string | null; managedTerritories?: string[] },
): string | null {
  if (!scope.assignableRoles.includes(input.role)) {
    return "Bạn chỉ được gán vai trò thấp hơn vai trò của mình.";
  }
  if (scope.all) return null;

  if (!input.departmentId) return "Chọn phòng ban / team — tài khoản phải thuộc nhánh do bạn phụ trách.";
  if (!scope.departmentIds.includes(input.departmentId)) {
    return "Phòng ban này nằm ngoài nhánh bạn phụ trách.";
  }
  const territories = [...(input.managedTerritories ?? []), ...(input.territoryId ? [input.territoryId] : [])];
  if (territories.some((id) => !scope.territoryIds.includes(id))) {
    return "Khu vực này nằm ngoài phạm vi bạn phụ trách.";
  }
  return null;
}

/** Mọi cột trỏ tới tài khoản người dùng — có bất kỳ dòng nào nghĩa là tài khoản đã gắn với dữ liệu nghiệp vụ. */
const USER_REFERENCES: [PgTable, PgColumn][] = [
  [t.customers, t.customers.ownerId],
  [t.customerStageHistory, t.customerStageHistory.changedBy],
  [t.customerNotes, t.customerNotes.recordedBy],
  [t.customerAttachments, t.customerAttachments.uploadedBy],
  [t.customerCourseCompletions, t.customerCourseCompletions.recordedBy],
  [t.dealerInactiveNotes, t.dealerInactiveNotes.recordedBy],
  [t.orders, t.orders.salesUserId],
  [t.orders, t.orders.createdBy],
  [t.orderPayments, t.orderPayments.recordedBy],
  [t.orderAttachments, t.orderAttachments.uploadedBy],
  [t.dailyReports, t.dailyReports.userId],
  [t.weeklyPlans, t.weeklyPlans.userId],
  [t.checkIns, t.checkIns.userId],
  [t.kpiMonthly, t.kpiMonthly.userId],
  [t.customKpis, t.customKpis.createdBy],
  [t.customKpiEntries, t.customKpiEntries.userId],
  [t.customKpiLogs, t.customKpiLogs.userId],
  [t.announcements, t.announcements.createdBy],
  [t.companyDocuments, t.companyDocuments.uploadedBy],
  [t.proposals, t.proposals.createdBy],
  [t.proposalApprovers, t.proposalApprovers.approverId],
  [t.events, t.events.ownerId],
  [t.inviteEventStaffQuotas, t.inviteEventStaffQuotas.userId],
  [t.inviteEventGuests, t.inviteEventGuests.invitedBy],
  [t.tasks, t.tasks.assigneeId],
  [t.tasks, t.tasks.createdBy],
  [t.departments, t.departments.headUserId],
];

/**
 * Trong số các tài khoản đã cho, những tài khoản nào đã có dữ liệu nghiệp vụ (đơn hàng, khách phụ trách, báo cáo, việc...).
 * Tài khoản có dữ liệu chỉ được KHOÁ, không xoá hẳn, để lịch sử đơn hàng và báo cáo không mất người thực hiện.
 */
export async function usersWithData(ids: string[]): Promise<Set<string>> {
  const found = new Set<string>();
  if (ids.length === 0) return found;
  await Promise.all(
    USER_REFERENCES.map(async ([table, column]) => {
      const rows = await db.selectDistinct({ id: column }).from(table).where(inArray(column, ids));
      for (const row of rows) if (row.id) found.add(String(row.id));
    }),
  );
  return found;
}
