"use server";

import { revalidatePath } from "next/cache";
import { and, eq, inArray, sql } from "drizzle-orm";
import { z } from "zod";
import { db } from "@/db";
import { departments, users } from "@/db/schema";
import { requireSession } from "@/lib/auth";
import { canManageSettings, departmentSubtree } from "@/lib/rbac";
import { diffFields, writeAudit } from "@/lib/audit";
import { isUuid } from "@/lib/route-params";

const departmentSchema = z.object({
  name: z.string().trim().min(2, "Tên phòng ban quá ngắn."),
  code: z.string().trim().min(1, "Mã phòng ban không được để trống.").max(20),
  parentId: z.string().uuid().optional().or(z.literal("")),
  headUserId: z.string().uuid().optional().or(z.literal("")),
});

export type DepartmentState = { error?: string; ok?: string };

async function requireAdmin() {
  const session = await requireSession();
  if (!canManageSettings(session)) {
    throw new Error("Chỉ Ban giám đốc mới được sửa cơ cấu phòng ban.");
  }
  return session;
}

/** Trưởng phòng phải là nhân viên đang hoạt động CÙNG công ty — không tin mã người dùng do trình duyệt gửi lên. */
async function headUserError(tenantId: string, headUserId: string): Promise<string | null> {
  if (!headUserId) return null;
  const [head] = await db
    .select({ id: users.id })
    .from(users)
    .where(and(eq(users.tenantId, tenantId), eq(users.id, headUserId), eq(users.isActive, true)))
    .limit(1);
  return head ? null : "Trưởng phòng phải là nhân viên đang hoạt động của công ty.";
}

export async function createDepartmentAction(
  _prev: DepartmentState,
  formData: FormData,
): Promise<DepartmentState> {
  let session;
  try {
    session = await requireAdmin();
  } catch (error) {
    return { error: error instanceof Error ? error.message : "Không có quyền." };
  }

  const parsed = departmentSchema.safeParse({
    name: formData.get("name"),
    code: formData.get("code"),
    parentId: formData.get("parentId") || "",
    headUserId: formData.get("headUserId") || "",
  });

  if (!parsed.success) return { error: parsed.error.issues[0].message };
  const data = parsed.data;

  const [duplicate] = await db
    .select({ id: departments.id })
    .from(departments)
    .where(and(eq(departments.tenantId, session.tenantId), eq(departments.code, data.code)))
    .limit(1);

  if (duplicate) return { error: `Mã phòng ban ${data.code} đã tồn tại.` };

  if (data.parentId) {
    const [parent] = await db
      .select({ id: departments.id })
      .from(departments)
      .where(and(eq(departments.tenantId, session.tenantId), eq(departments.id, data.parentId)))
      .limit(1);
    if (!parent) return { error: "Không tìm thấy phòng ban cấp trên." };
  }
  const headError = await headUserError(session.tenantId, data.headUserId ?? "");
  if (headError) return { error: headError };

  const [created] = await db
    .insert(departments)
    .values({
      tenantId: session.tenantId,
      code: data.code,
      name: data.name,
      parentId: data.parentId || null,
      headUserId: data.headUserId || null,
    })
    .returning();

  await writeAudit(session, "CREATE", "department", created.id, { code: data.code, name: data.name, parentId: data.parentId || null, headUserId: data.headUserId || null });

  revalidatePath("/danh-muc/phong-ban");
  return { ok: `Đã thêm phòng ban "${data.name}".` };
}

export async function updateDepartmentAction(formData: FormData): Promise<{ ok?: string; error?: string }> {
  const session = await requireSession();
  if (!canManageSettings(session)) return { error: "Chỉ Ban giám đốc mới được sửa cơ cấu phòng ban." };
  const id = String(formData.get("id") ?? "");
  const name = String(formData.get("name") ?? "").trim();
  const headUserId = String(formData.get("headUserId") ?? "");

  if (!isUuid(id)) return { error: "Không tìm thấy phòng ban." };
  if (name.length < 2) return { error: "Tên phòng ban quá ngắn." };
  if (name.length > 120) return { error: "Tên phòng ban tối đa 120 ký tự." };
  if (headUserId && !isUuid(headUserId)) return { error: "Trưởng phòng không hợp lệ." };

  const [current] = await db
    .select({ name: departments.name, headUserId: departments.headUserId })
    .from(departments)
    .where(and(eq(departments.tenantId, session.tenantId), eq(departments.id, id)))
    .limit(1);
  if (!current) return { error: "Không tìm thấy phòng ban." };
  const headError = await headUserError(session.tenantId, headUserId);
  if (headError) return { error: headError };

  const next = { name, headUserId: headUserId || null };
  const changes = diffFields({ name: current.name, headUserId: current.headUserId }, next);
  if (Object.keys(changes).length === 0) return { ok: "Không có thay đổi nào để lưu." };

  await db
    .update(departments)
    .set(next)
    .where(and(eq(departments.tenantId, session.tenantId), eq(departments.id, id)));
  await writeAudit(session, "UPDATE", "department", id, { name, changes });

  revalidatePath("/danh-muc/phong-ban");
  return { ok: "Đã lưu thay đổi." };
}

/**
 * Ngừng sử dụng một phòng ban.
 *
 * Không xoá hẳn: đơn hàng cũ đã sao chép `departmentId`, xoá đi thì báo cáo lịch
 * sử mất tên phòng ban. Chặn khi còn nhân sự đang thuộc nhánh này — nếu không,
 * những người đó sẽ mất phạm vi dữ liệu mà không ai hay.
 */
export async function toggleDepartmentActiveAction(formData: FormData): Promise<{ ok?: string; error?: string }> {
  const session = await requireSession();
  if (!canManageSettings(session)) return { error: "Chỉ Ban giám đốc mới được sửa cơ cấu phòng ban." };
  const id = String(formData.get("id") ?? "");
  if (!isUuid(id)) return { error: "Không tìm thấy phòng ban." };

  const [target] = await db
    .select({ isActive: departments.isActive, name: departments.name })
    .from(departments)
    .where(and(eq(departments.tenantId, session.tenantId), eq(departments.id, id)))
    .limit(1);

  if (!target) return { error: "Không tìm thấy phòng ban." };

  if (target.isActive) {
    const subtree = await departmentSubtree(session.tenantId, [id]);
    const [{ count }] = await db
      .select({ count: sql<number>`count(*)::int` })
      .from(users)
      .where(
        and(
          eq(users.tenantId, session.tenantId),
          eq(users.isActive, true),
          // Mảng JS đưa vào `sql` bị Drizzle bung thành danh sách tham số nên `= any($1,$2)` báo lỗi cú pháp — phải dùng inArray.
          inArray(users.departmentId, subtree),
        ),
      );

    if (Number(count) > 0) {
      return {
        error:
          `${target.name} còn ${count} nhân sự đang thuộc phòng ban này hoặc nhánh con. ` +
          `Chuyển họ sang phòng ban khác trước khi ngừng sử dụng.`,
      };
    }
  }

  await db
    .update(departments)
    .set({ isActive: !target.isActive })
    .where(and(eq(departments.tenantId, session.tenantId), eq(departments.id, id)));
  await writeAudit(session, "UPDATE", "department", id, { name: target.name, changes: { isActive: { from: target.isActive, to: !target.isActive } } });

  revalidatePath("/danh-muc/phong-ban");
  return { ok: target.isActive ? "Đã ngừng sử dụng phòng ban." : "Đã dùng lại phòng ban." };
}
