/**
 * Test rò rỉ phạm vi dữ liệu — lớp lỗi THẬT xuất hiện nhiều lần nhất trong dự án này
 * (KPI lộ cả công ty, bộ lọc lộ tên ngoài nhánh, trang sự kiện lộ phạm vi…), nên đáng
 * được kiểm tự động hơn bất kỳ thứ gì khác. Chạy trên PGlite thật (xem tests/helpers/db.ts),
 * không mock — phạm vi dữ liệu được quyết định bởi câu SQL sinh ra, mock sẽ bỏ lọt đúng loại lỗi này.
 */
import { beforeAll, describe, expect, it } from "vitest";
import { eq } from "drizzle-orm";
import { customers } from "@/db/schema";
import { scopeFilter } from "@/lib/rbac";
import { parseCustomerListQuery, loadCustomerList } from "@/lib/customer-list";
import { parseOrderListQuery, loadOrderList } from "@/lib/order-list";
import { seedMinimalFixture, type Fixture } from "./helpers/db";

let fx: Fixture;

beforeAll(async () => {
  fx = await seedMinimalFixture();
});

describe("scopeFilter — nguyên liệu gốc của mọi truy vấn có phạm vi", () => {
  it("DIRECTOR: chỉ lọc theo tenant, thấy khách của cả hai nhánh trong công ty mình", async () => {
    const scope = await scopeFilter(fx.director, { tenantId: customers.tenantId, ownerId: customers.ownerId, territoryId: customers.territoryId });
    const { db } = await import("@/db");
    const rows = await db.select({ id: customers.id }).from(customers).where(scope);
    const ids = rows.map((r) => r.id);
    expect(ids).toContain(fx.custBac);
    expect(ids).toContain(fx.custNam);
  });

  it("SALES: chỉ thấy khách của chính mình, không thấy khách của NVKD khác cùng công ty", async () => {
    const scope = await scopeFilter(fx.salesBacSession, { tenantId: customers.tenantId, ownerId: customers.ownerId, territoryId: customers.territoryId });
    const { db } = await import("@/db");
    const rows = await db.select({ id: customers.id }).from(customers).where(scope);
    const ids = rows.map((r) => r.id);
    expect(ids).toContain(fx.custBac);
    expect(ids).not.toContain(fx.custNam);
  });

  it("MANAGER: thấy khách của nhánh phòng ban mình, KHÔNG thấy khách của phòng ban khác", async () => {
    const scope = await scopeFilter(fx.managerBac, { tenantId: customers.tenantId, ownerId: customers.ownerId, territoryId: customers.territoryId });
    const { db } = await import("@/db");
    const rows = await db.select({ id: customers.id }).from(customers).where(scope);
    const ids = rows.map((r) => r.id);
    expect(ids).toContain(fx.custBac);
    expect(ids).not.toContain(fx.custNam);
  });
});

describe("loadCustomerList — cùng nguyên tắc ở tầng truy vấn dùng thật trên màn hình", () => {
  it("MANAGER không thấy khách ngoài nhánh, kể cả khi không lọc gì thêm", async () => {
    const query = parseCustomerListQuery(() => undefined);
    const { rows } = await loadCustomerList(fx.managerBac, query);
    expect(rows.map((r) => r.id)).toContain(fx.custBac);
    expect(rows.map((r) => r.id)).not.toContain(fx.custNam);
  });

  it("DIRECTOR của tenant A không bao giờ thấy khách của tenant B, kể cả khi tìm đúng tên", async () => {
    const query = parseCustomerListQuery((key) => (key === "q" ? "Khách công ty B" : undefined));
    const { rows } = await loadCustomerList(fx.director, query);
    expect(rows).toHaveLength(0);
  });
});

// `parseOrderListQuery` mặc định lọc theo THÁNG HIỆN TẠI (không truyền `tu`/`den` thì dùng `today()`) — đơn mẫu
// trong fixture cố định ngày 2026-08-05, nên phải truyền rõ khoảng ngày, nếu không mọi test sẽ ra 0 dòng bất kể phạm vi.
const wideRange = (key: string) => (key === "tu" ? "2026-01-01" : key === "den" ? "2026-12-31" : undefined);

describe("loadOrderList — cách tenant nhau tuyệt đối, dù không đặt điều kiện gì", () => {
  it("DIRECTOR của tenant A chỉ thấy đơn của tenant A, kể cả xem 'toàn công ty'", async () => {
    const query = parseOrderListQuery(wideRange);
    const { rows } = await loadOrderList(fx.director, query, { all: true });
    expect(rows.length).toBeGreaterThan(0);
    expect(rows.every((r) => r.customerCode !== "KHX01")).toBe(true);
  });

  it("DIRECTOR của tenant B (dù dùng đúng vai trò cao nhất) không thấy đơn của tenant A", async () => {
    const query = parseOrderListQuery(wideRange);
    const { rows } = await loadOrderList(fx.directorTenantB, query, { all: true });
    expect(rows.every((r) => r.customerCode !== "KHB01" && r.customerCode !== "KHN01")).toBe(true);
  });

  it("SALES chỉ thấy đơn của chính mình", async () => {
    const query = parseOrderListQuery(wideRange);
    const { rows } = await loadOrderList(fx.salesBacSession, query, { all: true });
    expect(rows.length).toBeGreaterThan(0);
    expect(rows.every((r) => r.customerCode === "KHB01")).toBe(true);
  });
});

// Không lọt qua kiểm phạm vi khi chỉnh sửa trực tiếp câu điều kiện — nhắc lại vì đây là điểm đã có
// PR thật sửa lỗi tương tự (department toggle dùng `= any(mảng JS)` gây lỗi cú pháp thay vì lọc sai lặng lẽ);
// nhưng vẫn quan trọng nhất là các test rò rỉ ở trên, không phải test này.
describe("eq(customers.tenantId, ...) không tự động kèm điều kiện gì khác — nhắc lại kỷ luật viết mã", () => {
  it("chỉ dùng eq(tenantId) mà KHÔNG qua scopeFilter thì thấy MỌI khách trong tenant — đúng như cảnh báo trong CLAUDE.md", async () => {
    const { db } = await import("@/db");
    const rows = await db.select({ id: customers.id }).from(customers).where(eq(customers.tenantId, fx.tenantA));
    // Nhắc để không quên: đây LÀ hành vi đúng của chính `eq(tenantId)` một mình — nó không lọc theo vai trò.
    // Mọi màn hình PHẢI gọi scopeFilter(), không được tự viết eq(tenantId) rồi coi là đủ.
    expect(rows.map((r) => r.id).sort()).toEqual([fx.custBac, fx.custNam].sort());
  });
});
