"use server";

import { revalidatePath } from "next/cache";
import { and, eq } from "drizzle-orm";
import { z } from "zod";
import { db } from "@/db";
import {
  inviteEvents,
  inviteEventTerritoryQuotas,
  inviteEventDeptQuotas,
  inviteEventStaffQuotas,
  inviteEventGuests,
  departments,
  territories,
  auditLogs,
  users,
} from "@/db/schema";
import { requireSession } from "@/lib/auth";
import { regionOf, visibleDepartmentIds, visibleTerritoryIds } from "@/lib/rbac";
import { canManageInviteGuest, inviteScopeUserIds } from "@/lib/invite-events";
import { isRealDate } from "@/lib/period";
import { diffFields, writeAudit } from "@/lib/audit";
import { isUuid } from "@/lib/route-params";

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

const date = z.string().refine(isRealDate, "Ngày không hợp lệ.");

/** Sự kiện phải thuộc đúng công ty — mọi thao tác chỉ tiêu/khách mời đều đi qua đây trước khi ghi. */
async function requireEvent(tenantId: string, eventId: string) {
  const [event] = await db
    .select({ id: inviteEvents.id, name: inviteEvents.name })
    .from(inviteEvents)
    .where(and(eq(inviteEvents.tenantId, tenantId), eq(inviteEvents.id, eventId)))
    .limit(1);
  return event ?? null;
}

async function audit(
  session: Awaited<ReturnType<typeof requireSession>>,
  entityId: string,
  payload: Record<string, unknown>,
) {
  await db.insert(auditLogs).values({
    tenantId: session.tenantId,
    userId: session.userId,
    action: "UPDATE",
    entityType: "invite_event",
    entityId,
    payload,
  });
}

export async function createInviteEventAction(_prev: FormState, formData: FormData): Promise<FormState> {
  const session = await requireSession();
  if (session.role !== "DIRECTOR") return { error: "Chỉ Ban giám đốc mới được tạo sự kiện có chỉ tiêu." };

  const parsed = z
    .object({
      name: z.string().trim().min(2, "Tên sự kiện quá ngắn."),
      eventDate: date,
      inviteDeadline: date.optional().or(z.literal("")),
      note: z.string().trim().max(500).optional(),
    })
    .safeParse({
      name: formData.get("name"),
      eventDate: formData.get("eventDate"),
      inviteDeadline: formData.get("inviteDeadline") || "",
      note: formData.get("note") || undefined,
    });
  if (!parsed.success) return { error: parsed.error.issues[0].message };
  if (parsed.data.inviteDeadline && parsed.data.inviteDeadline > parsed.data.eventDate) {
    return { error: "Hạn chót mời khách không được sau ngày diễn ra sự kiện." };
  }

  const [row] = await db
    .insert(inviteEvents)
    .values({
      tenantId: session.tenantId,
      name: parsed.data.name,
      eventDate: parsed.data.eventDate,
      inviteDeadline: parsed.data.inviteDeadline || null,
      note: parsed.data.note ?? null,
      createdBy: session.userId,
    })
    .returning();

  await db.insert(auditLogs).values({
    tenantId: session.tenantId,
    userId: session.userId,
    action: "CREATE",
    entityType: "invite_event",
    entityId: row.id,
    payload: { name: parsed.data.name },
  });

  revalidatePath("/danh-muc/su-kien-moi-khach");
  return { ok: "Đã tạo sự kiện." };
}

/* -------------------------------------------------------------------------- */
/* Giao chỉ tiêu: Ban giám đốc → khu vực → phòng ban → nhân viên             */
/* -------------------------------------------------------------------------- */

const quotaNumber = z.coerce.number().int("Chỉ tiêu phải là số nguyên.").min(0, "Chỉ tiêu không được âm.").max(1_000_000);

type ActionResult = { ok?: string; error?: string };

export async function setTerritoryQuotaAction(formData: FormData): Promise<ActionResult> {
  const session = await requireSession();
  if (session.role !== "DIRECTOR") return { error: "Chỉ Ban giám đốc mới được giao chỉ tiêu theo khu vực." };

  const eventId = String(formData.get("eventId"));
  const territoryId = String(formData.get("territoryId"));
  const quotaParsed = quotaNumber.safeParse(formData.get("quota") || 0);
  if (!quotaParsed.success) return { error: quotaParsed.error.issues[0].message };
  const quota = quotaParsed.data;

  if (!(await requireEvent(session.tenantId, eventId))) return { error: "Không tìm thấy sự kiện." };
  const [region] = await db
    .select({ id: territories.id, level: territories.level })
    .from(territories)
    .where(and(eq(territories.tenantId, session.tenantId), eq(territories.id, territoryId)))
    .limit(1);
  if (!region) return { error: "Không tìm thấy khu vực." };

  await db
    .insert(inviteEventTerritoryQuotas)
    .values({ tenantId: session.tenantId, eventId, territoryId, quota })
    .onConflictDoUpdate({ target: [inviteEventTerritoryQuotas.eventId, inviteEventTerritoryQuotas.territoryId], set: { quota } });

  await audit(session, eventId, { quotaTerritory: { territoryId, quota } });
  revalidatePath(`/danh-muc/su-kien-moi-khach/${eventId}`);
  return { ok: "Đã lưu chỉ tiêu khu vực." };
}

/** Giao chỉ tiêu cho phòng ban / team: Ban giám đốc giao bất kỳ phòng nào; GĐ khu vực và Quản lý chỉ giao cho phòng nằm dưới phòng của mình. */
export async function setDeptQuotaAction(formData: FormData): Promise<ActionResult> {
  const session = await requireSession();
  if (session.role === "SALES") return { error: "Chỉ quản lý mới được giao chỉ tiêu cho phòng ban." };

  const eventId = String(formData.get("eventId"));
  const departmentId = String(formData.get("departmentId"));
  const quotaParsed = quotaNumber.safeParse(formData.get("quota") || 0);
  if (!quotaParsed.success) return { error: quotaParsed.error.issues[0].message };
  const quota = quotaParsed.data;

  if (!(await requireEvent(session.tenantId, eventId))) return { error: "Không tìm thấy sự kiện." };
  const [dept] = await db
    .select({ id: departments.id })
    .from(departments)
    .where(and(eq(departments.tenantId, session.tenantId), eq(departments.id, departmentId)))
    .limit(1);
  if (!dept) return { error: "Không tìm thấy phòng ban." };

  if (session.role !== "DIRECTOR") {
    const [me] = await db.select({ departmentId: users.departmentId }).from(users).where(eq(users.id, session.userId)).limit(1);
    const scope = (await visibleDepartmentIds(session)) ?? [];
    if (!scope.includes(departmentId) || departmentId === me?.departmentId) {
      return { error: "Bạn chỉ giao được chỉ tiêu cho phòng ban / team nằm dưới phòng của mình." };
    }
  }

  await db
    .insert(inviteEventDeptQuotas)
    .values({ tenantId: session.tenantId, eventId, departmentId, quota })
    .onConflictDoUpdate({ target: [inviteEventDeptQuotas.eventId, inviteEventDeptQuotas.departmentId], set: { quota } });

  await audit(session, eventId, { quotaDepartment: { departmentId, quota } });
  revalidatePath(`/danh-muc/su-kien-moi-khach/${eventId}`);
  return { ok: "Đã lưu chỉ tiêu phòng ban." };
}

/** Giao chỉ tiêu cho nhân viên: Ban giám đốc giao bất kỳ NVKD nào; GĐ khu vực và Quản lý giao cho NVKD trong nhánh mình. */
export async function setStaffQuotaAction(formData: FormData): Promise<ActionResult> {
  const session = await requireSession();
  if (session.role === "SALES") return { error: "Chỉ quản lý mới được giao chỉ tiêu cho nhân viên." };

  const eventId = String(formData.get("eventId"));
  const userId = String(formData.get("userId"));
  const quotaParsed = quotaNumber.safeParse(formData.get("quota") || 0);
  if (!quotaParsed.success) return { error: quotaParsed.error.issues[0].message };
  const quota = quotaParsed.data;

  if (!(await requireEvent(session.tenantId, eventId))) return { error: "Không tìm thấy sự kiện." };

  // Suy ra khu vực (miền) từ chính khu vực của nhân viên — không tin trường ẩn client gửi lên.
  const [target] = await db
    .select({ territoryId: users.territoryId, role: users.role })
    .from(users)
    .where(and(eq(users.tenantId, session.tenantId), eq(users.id, userId), eq(users.isActive, true)))
    .limit(1);
  if (!target || target.role !== "SALES") return { error: "Chỉ giao chỉ tiêu cho nhân viên kinh doanh đang hoạt động." };
  if (!target.territoryId) return { error: "Nhân viên chưa được gán khu vực." };
  const territoryId = await regionOf(session.tenantId, target.territoryId);

  if (session.role !== "DIRECTOR") {
    const scope = await inviteScopeUserIds(session);
    if (!scope || !scope.includes(userId) || userId === session.userId) {
      return { error: "Nhân viên này không thuộc nhánh bạn phụ trách." };
    }
  }

  await db
    .insert(inviteEventStaffQuotas)
    .values({ tenantId: session.tenantId, eventId, territoryId, userId, quota })
    .onConflictDoUpdate({ target: [inviteEventStaffQuotas.eventId, inviteEventStaffQuotas.userId], set: { quota, territoryId } });

  await audit(session, eventId, { quotaStaff: { userId, quota } });
  revalidatePath(`/danh-muc/su-kien-moi-khach/${eventId}`);
  return { ok: "Đã lưu chỉ tiêu nhân viên." };
}

/* -------------------------------------------------------------------------- */
/* Khách mời                                                                  */
/* -------------------------------------------------------------------------- */

const guestSchema = z.object({
  eventId: z.string().uuid(),
  territoryId: z.string().uuid(),
  guestName: z.string().trim().min(1, "Nhập tên khách.").max(120),
  guestType: z.string().trim().max(60).optional(),
  invitedDate: date,
  note: z.string().trim().max(300).optional(),
});

async function checkGuestTerritory(session: Awaited<ReturnType<typeof requireSession>>, territoryId: string) {
  // NVKD chỉ được mời cho khu vực mình phụ trách; quản lý/GĐKV/TGĐ có thể mời cho bất kỳ khu vực nào mình thấy.
  const mine = await visibleTerritoryIds(session);
  return mine === null || mine.includes(territoryId);
}

export async function addGuestAction(_prev: FormState, formData: FormData): Promise<FormState> {
  const session = await requireSession();

  const parsed = guestSchema.safeParse({
    eventId: formData.get("eventId"),
    territoryId: formData.get("territoryId"),
    guestName: formData.get("guestName"),
    guestType: formData.get("guestType") || undefined,
    invitedDate: formData.get("invitedDate"),
    note: formData.get("note") || undefined,
  });
  if (!parsed.success) return { error: parsed.error.issues[0].message };

  if (!(await requireEvent(session.tenantId, parsed.data.eventId))) return { error: "Không tìm thấy sự kiện." };
  if (!(await checkGuestTerritory(session, parsed.data.territoryId))) return { error: "Bạn không phụ trách khu vực này." };

  const [guest] = await db
    .insert(inviteEventGuests)
    .values({
      tenantId: session.tenantId,
      eventId: parsed.data.eventId,
      territoryId: parsed.data.territoryId,
      invitedBy: session.userId,
      guestName: parsed.data.guestName,
      guestType: parsed.data.guestType ?? null,
      invitedDate: parsed.data.invitedDate,
      note: parsed.data.note ?? null,
    })
    .returning();
  await writeAudit(session, "CREATE", "invite_guest", guest.id, { name: parsed.data.guestName, guestType: parsed.data.guestType ?? null, eventId: parsed.data.eventId, invitedDate: parsed.data.invitedDate });

  revalidatePath(`/danh-muc/su-kien-moi-khach/${parsed.data.eventId}`);
  return { ok: "Đã ghi nhận khách mời." };
}

/**
 * Ghi nhanh theo số lượng — báo cáo cuối ngày kiểu "hôm nay mời 10 bác sĩ, 5 chủ spa" mà chưa có tên từng người.
 * Tạo N dòng khách "chưa ghi tên" cùng loại; tên bổ sung sau khi có (sửa từng dòng).
 */
export async function addGuestBatchAction(_prev: FormState, formData: FormData): Promise<FormState> {
  const session = await requireSession();

  const parsed = z
    .object({
      eventId: z.string().uuid(),
      territoryId: z.string().uuid(),
      guestType: z.string().trim().min(1, "Nhập loại khách (ví dụ Bác sĩ).").max(60),
      count: z.coerce.number().int("Số lượng phải là số nguyên.").min(1, "Số lượng tối thiểu là 1.").max(200, "Mỗi lần ghi tối đa 200 khách."),
      invitedDate: date,
    })
    .safeParse({
      eventId: formData.get("eventId"),
      territoryId: formData.get("territoryId"),
      guestType: formData.get("guestType"),
      count: formData.get("count"),
      invitedDate: formData.get("invitedDate"),
    });
  if (!parsed.success) return { error: parsed.error.issues[0].message };
  const data = parsed.data;

  if (!(await requireEvent(session.tenantId, data.eventId))) return { error: "Không tìm thấy sự kiện." };
  if (!(await checkGuestTerritory(session, data.territoryId))) return { error: "Bạn không phụ trách khu vực này." };

  await db.insert(inviteEventGuests).values(
    Array.from({ length: data.count }, (_, i) => ({
      tenantId: session.tenantId,
      eventId: data.eventId,
      territoryId: data.territoryId,
      invitedBy: session.userId,
      guestName: `${data.guestType} (chưa ghi tên) #${i + 1}`,
      guestType: data.guestType,
      invitedDate: data.invitedDate,
    })),
  );
  await writeAudit(session, "CREATE", "invite_guest", data.eventId, { name: `${data.count} khách ${data.guestType}`, count: data.count, guestType: data.guestType, eventId: data.eventId, invitedDate: data.invitedDate });

  revalidatePath(`/danh-muc/su-kien-moi-khach/${data.eventId}`);
  return { ok: `Đã ghi nhận ${data.count} khách ${data.guestType}.` };
}

export async function updateGuestAction(formData: FormData): Promise<ActionResult> {
  const session = await requireSession();

  const guestId = String(formData.get("guestId") ?? "");
  if (!isUuid(guestId)) return { error: "Không tìm thấy khách mời." };
  const wantsCme = formData.get("wantsCme") === "on";
  const wantsCertificate = formData.get("wantsCertificate") === "on";
  const attendedRaw = String(formData.get("attended") ?? "unknown");
  const attended = attendedRaw === "yes" ? true : attendedRaw === "no" ? false : null;
  const note = String(formData.get("note") ?? "").trim() || null;
  const guestName = String(formData.get("guestName") ?? "").trim();

  const [row] = await db
    .select()
    .from(inviteEventGuests)
    .where(and(eq(inviteEventGuests.tenantId, session.tenantId), eq(inviteEventGuests.id, guestId)))
    .limit(1);
  if (!row) return { error: "Không tìm thấy khách mời." };
  if (!(await canManageInviteGuest(session, row.invitedBy))) {
    return { error: "Bạn chỉ sửa được khách do chính mình mời hoặc do cấp dưới trong nhánh mời." };
  }

  const next = { attended, wantsCme, wantsCertificate, note, guestName: guestName ? guestName.slice(0, 120) : row.guestName };
  const changes = diffFields({ attended: row.attended, wantsCme: row.wantsCme, wantsCertificate: row.wantsCertificate, note: row.note, guestName: row.guestName }, next);

  await db
    .update(inviteEventGuests)
    .set(next)
    .where(and(eq(inviteEventGuests.tenantId, session.tenantId), eq(inviteEventGuests.id, guestId)));
  if (Object.keys(changes).length > 0) await writeAudit(session, "UPDATE", "invite_guest", guestId, { name: row.guestName, changes });

  revalidatePath(`/danh-muc/su-kien-moi-khach/${row.eventId}`);
  return { ok: "Đã cập nhật khách mời." };
}

export async function deleteGuestAction(formData: FormData): Promise<ActionResult> {
  const session = await requireSession();

  const guestId = String(formData.get("guestId") ?? "");
  if (!isUuid(guestId)) return { error: "Không tìm thấy khách mời." };

  const [row] = await db
    .select()
    .from(inviteEventGuests)
    .where(and(eq(inviteEventGuests.tenantId, session.tenantId), eq(inviteEventGuests.id, guestId)))
    .limit(1);
  if (!row) return { error: "Không tìm thấy khách mời." };
  if (!(await canManageInviteGuest(session, row.invitedBy))) {
    return { error: "Bạn chỉ xoá được khách do chính mình mời hoặc do cấp dưới trong nhánh mời." };
  }

  await writeAudit(session, "DELETE", "invite_guest", guestId, { name: row.guestName, guestType: row.guestType, eventId: row.eventId, invitedDate: row.invitedDate });
  await db.delete(inviteEventGuests).where(and(eq(inviteEventGuests.tenantId, session.tenantId), eq(inviteEventGuests.id, guestId)));
  revalidatePath(`/danh-muc/su-kien-moi-khach/${row.eventId}`);
  return { ok: "Đã xoá khách mời." };
}
