/**
 * Đợt 2 — "Thông tin cần duyệt không được sửa/xoá, ngoại trừ Admin" (yêu cầu bổ sung của khách).
 * Gọi thẳng server action qua HTTP như trình duyệt thật (xem tests/e2e/helpers/server.ts) trên một
 * bản build + cơ sở dữ liệu riêng cho lần chạy này — không mock quyền, không mock `next/headers`.
 */
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { callAction, startE2eServer, type E2eContext } from "./helpers/server";

let ctx: E2eContext;

const yesterday = () => new Date(Date.now() - 24 * 3600 * 1000).toISOString().slice(0, 10);
const line = (productId: string, qtySold = 2, unitPrice = 100_000) => JSON.stringify({ productId, qtySold, qtyPromo: 0, unitPrice });
const orderIdFromRedirect = (redirect: string | null) => redirect?.match(/\/don-hang\/([0-9a-f-]{36})/)?.[1];

beforeAll(async () => {
  ctx = await startE2eServer();
}, 120_000);

afterAll(() => {
  ctx?.stop();
});

describe("Đơn hàng đã duyệt: không sửa/huỷ/xoá, trừ Ban giám đốc", () => {
  let orderId: string;

  it("NVKD lập đơn mới → ở trạng thái chờ duyệt", async () => {
    const fields = { customerId: ctx.fixture.customer, orderDate: yesterday(), line: line(ctx.fixture.product) };
    const r = await callAction(ctx.baseUrl, "(app)/don-hang/actions.ts#createOrderAction", ctx.cookies.sales, fields, "/don-hang/moi", { prev: true });
    orderId = orderIdFromRedirect(r.redirect) ?? "";
    expect(orderId).toMatch(/^[0-9a-f-]{36}$/);
  });

  it("NVKD sửa được đơn của chính mình khi còn chờ duyệt", async () => {
    const fields = { id: orderId, customerId: ctx.fixture.customer, orderDate: yesterday(), line: line(ctx.fixture.product, 3), note: "Sửa khi chờ duyệt" };
    const r = await callAction(ctx.baseUrl, "(app)/don-hang/actions.ts#updateOrderAction", ctx.cookies.sales, fields, `/don-hang/${orderId}/sua`, { prev: true });
    expect((r.value as { error?: string } | null)?.error).toBeFalsy();
  });

  it("NVKD không tự duyệt được đơn của mình", async () => {
    const r = await callAction(ctx.baseUrl, "(app)/don-hang/actions.ts#approveOrderAction", ctx.cookies.sales, { id: orderId }, `/don-hang/${orderId}`);
    expect((r.value as { error: string }).error).toMatch(/Chỉ Giám đốc/);
  });

  it("Quản lý (MANAGER) không duyệt được — chỉ GĐKV hoặc Ban giám đốc", async () => {
    const r = await callAction(ctx.baseUrl, "(app)/don-hang/actions.ts#approveOrderAction", ctx.cookies.manager, { id: orderId }, `/don-hang/${orderId}`);
    expect((r.value as { error: string }).error).toMatch(/Chỉ Giám đốc/);
  });

  it("GĐKV duyệt được → đơn chuyển Đã duyệt", async () => {
    const r = await callAction(ctx.baseUrl, "(app)/don-hang/actions.ts#approveOrderAction", ctx.cookies.gdkv, { id: orderId }, `/don-hang/${orderId}`);
    expect((r.value as { ok?: string }).ok).toBeTruthy();
  });

  it("Sau khi duyệt: NVKD (người lập) không sửa được nữa", async () => {
    const fields = { id: orderId, customerId: ctx.fixture.customer, orderDate: yesterday(), line: line(ctx.fixture.product), note: "hack" };
    const r = await callAction(ctx.baseUrl, "(app)/don-hang/actions.ts#updateOrderAction", ctx.cookies.sales, fields, `/don-hang/${orderId}/sua`, { prev: true });
    expect((r.value as { error?: string } | null)?.error).toMatch(/đã được duyệt/);
  });

  it("Sau khi duyệt: Quản lý không sửa được", async () => {
    const fields = { id: orderId, customerId: ctx.fixture.customer, orderDate: yesterday(), line: line(ctx.fixture.product), note: "hack" };
    const r = await callAction(ctx.baseUrl, "(app)/don-hang/actions.ts#updateOrderAction", ctx.cookies.manager, fields, `/don-hang/${orderId}/sua`, { prev: true });
    expect((r.value as { error?: string } | null)?.error).toMatch(/đã được duyệt/);
  });

  it("Sau khi duyệt: GĐKV — kể cả người vừa duyệt chính đơn này — cũng không sửa được", async () => {
    const fields = { id: orderId, customerId: ctx.fixture.customer, orderDate: yesterday(), line: line(ctx.fixture.product), note: "hack" };
    const r = await callAction(ctx.baseUrl, "(app)/don-hang/actions.ts#updateOrderAction", ctx.cookies.gdkv, fields, `/don-hang/${orderId}/sua`, { prev: true });
    expect((r.value as { error?: string } | null)?.error).toMatch(/đã được duyệt/);
  });

  const cancelCases: [string, () => string, RegExp][] = [
    ["NVKD", () => ctx.cookies.sales, /Chỉ quản lý/],
    ["Quản lý", () => ctx.cookies.manager, /Ban giám đốc/],
    ["GĐKV", () => ctx.cookies.gdkv, /Ban giám đốc/],
  ];
  it.each(cancelCases)("Sau khi duyệt: %s không huỷ được đơn", async (_label, cookie, expected) => {
    const r = await callAction(ctx.baseUrl, "(app)/don-hang/actions.ts#cancelOrderAction", cookie(), { id: orderId }, `/don-hang/${orderId}`);
    expect((r.value as { error: string }).error).toMatch(expected);
  });

  const deleteCases: [string, () => string][] = [
    ["NVKD", () => ctx.cookies.sales],
    ["Quản lý", () => ctx.cookies.manager],
    ["GĐKV", () => ctx.cookies.gdkv],
  ];
  it.each(deleteCases)("Sau khi duyệt: %s không xoá hẳn được đơn", async (_label, cookie) => {
    const r = await callAction(ctx.baseUrl, "(app)/don-hang/actions.ts#deleteOrderAction", cookie(), { id: orderId }, `/don-hang/${orderId}`);
    expect((r.value as { error: string }).error).toMatch(/Ban giám đốc/);
  });

  it("Ban giám đốc (Admin) sửa được đơn đã duyệt", async () => {
    const fields = { id: orderId, customerId: ctx.fixture.customer, orderDate: yesterday(), line: line(ctx.fixture.product), note: "Ban giám đốc điều chỉnh" };
    const r = await callAction(ctx.baseUrl, "(app)/don-hang/actions.ts#updateOrderAction", ctx.cookies.director, fields, `/don-hang/${orderId}/sua`, { prev: true });
    expect((r.value as { error?: string } | null)?.error).toBeFalsy();
  });

  it("Ban giám đốc (Admin) huỷ được đơn đã duyệt", async () => {
    const r = await callAction(ctx.baseUrl, "(app)/don-hang/actions.ts#cancelOrderAction", ctx.cookies.director, { id: orderId }, `/don-hang/${orderId}`);
    expect((r.value as { ok?: string }).ok).toBeTruthy();
  });
});

describe("Phiếu đề xuất đã duyệt hoặc bị từ chối: không sửa/xoá, trừ Ban giám đốc", () => {
  const proposalKind = { title: "Phiếu test khoá quyền", content: "Nội dung đủ dài để qua kiểm tối thiểu.", type: "OTHER" };

  async function createAndDecide(decision: "APPROVED" | "REJECTED") {
    const created = await callAction(
      ctx.baseUrl,
      "(app)/cong-viec/de-xuat/actions.ts#createProposalAction",
      ctx.cookies.sales,
      { ...proposalKind, directorId: ctx.fixture.director },
      "/cong-viec/de-xuat",
    );
    // createProposalAction luôn trả { ok } hoặc { error } — không redirect — nên lấy mã phiếu qua trang danh sách.
    // Danh sách xếp mới nhất trước (`orderBy(desc(proposals.createdAt))`) và các it() trong file này chạy tuần tự
    // (không concurrent), nên gọi NGAY sau khi tạo thì phiếu vừa tạo luôn là phiếu đầu tiên trong danh sách.
    expect((created.value as { ok?: string; error?: string })?.error).toBeFalsy();
    const list = await fetch(`${ctx.baseUrl}/cong-viec/de-xuat?xem=toi`, { headers: { cookie: ctx.cookies.sales } });
    const html = await list.text();
    const proposalId = [...html.matchAll(/name="proposalId" value="([0-9a-f-]{36})"/g)].map((m) => m[1])[0];
    expect(proposalId).toMatch(/^[0-9a-f-]{36}$/);

    const decided = await callAction(
      ctx.baseUrl,
      "(app)/cong-viec/de-xuat/actions.ts#decideProposalAction",
      ctx.cookies.director,
      { proposalId, decision, ...(decision === "REJECTED" ? { note: "Không phù hợp lúc này" } : {}) },
      "/cong-viec/de-xuat",
    );
    expect((decided.value as { ok?: string })?.ok).toBeTruthy();
    return proposalId;
  }

  it("Ai khác ngoài Ban giám đốc không sửa được phiếu ĐÃ DUYỆT", async () => {
    const proposalId = await createAndDecide("APPROVED");
    for (const cookie of [ctx.cookies.sales, ctx.cookies.manager, ctx.cookies.gdkv]) {
      const r = await callAction(ctx.baseUrl, "(app)/cong-viec/de-xuat/actions.ts#updateProposalAction", cookie, { proposalId, title: "Hack tiêu đề", content: "Nội dung mới đủ dài." }, "/cong-viec/de-xuat");
      expect((r.value as { error: string }).error).toMatch(/Chỉ Ban giám đốc/);
    }
  });

  it("Ai khác ngoài Ban giám đốc không xoá được phiếu ĐÃ DUYỆT", async () => {
    const proposalId = await createAndDecide("APPROVED");
    const r = await callAction(ctx.baseUrl, "(app)/cong-viec/de-xuat/actions.ts#deleteProposalAction", ctx.cookies.manager, { proposalId }, "/cong-viec/de-xuat");
    expect((r.value as { error: string }).error).toMatch(/Chỉ Ban giám đốc/);
  });

  it("Ban giám đốc (Admin) sửa được phiếu đã duyệt — chuỗi duyệt và kết quả giữ nguyên", async () => {
    const proposalId = await createAndDecide("APPROVED");
    const r = await callAction(
      ctx.baseUrl,
      "(app)/cong-viec/de-xuat/actions.ts#updateProposalAction",
      ctx.cookies.director,
      { proposalId, title: "Tiêu đề đã sửa bởi Ban giám đốc", content: "Nội dung đã điều chỉnh, vẫn đủ dài." },
      "/cong-viec/de-xuat",
    );
    expect((r.value as { ok?: string })?.ok).toBeTruthy();
  });

  it("Ban giám đốc (Admin) xoá được phiếu đã bị từ chối", async () => {
    const proposalId = await createAndDecide("REJECTED");
    const r = await callAction(ctx.baseUrl, "(app)/cong-viec/de-xuat/actions.ts#deleteProposalAction", ctx.cookies.director, { proposalId }, "/cong-viec/de-xuat");
    expect((r.value as { ok?: string })?.ok).toBeTruthy();
  });

  it("Người đề xuất thu hồi được phiếu khi CHƯA ai quyết định", async () => {
    const created = await callAction(
      ctx.baseUrl,
      "(app)/cong-viec/de-xuat/actions.ts#createProposalAction",
      ctx.cookies.sales,
      { ...proposalKind, title: "Phiếu chờ thu hồi", directorId: ctx.fixture.director },
      "/cong-viec/de-xuat",
    );
    expect((created.value as { error?: string })?.error).toBeFalsy();
    const list = await fetch(`${ctx.baseUrl}/cong-viec/de-xuat?xem=toi`, { headers: { cookie: ctx.cookies.sales } });
    const html = await list.text();
    const proposalId = [...html.matchAll(/name="proposalId" value="([0-9a-f-]{36})"/g)].map((m) => m[1])[0];

    const r = await callAction(ctx.baseUrl, "(app)/cong-viec/de-xuat/actions.ts#withdrawProposalAction", ctx.cookies.sales, { proposalId }, "/cong-viec/de-xuat");
    expect((r.value as { ok?: string })?.ok).toBeTruthy();
  });

  it("Người đề xuất KHÔNG thu hồi được phiếu đã có người quyết định", async () => {
    const proposalId = await createAndDecide("APPROVED");
    const r = await callAction(ctx.baseUrl, "(app)/cong-viec/de-xuat/actions.ts#withdrawProposalAction", ctx.cookies.sales, { proposalId }, "/cong-viec/de-xuat");
    expect((r.value as { error: string }).error).toMatch(/không thu hồi được/);
  });
});
