"use server";

import { revalidatePath } from "next/cache";
import { and, eq } from "drizzle-orm";
import { z } from "zod";
import { db } from "@/db";
import { tasks, users, auditLogs } from "@/db/schema";
import { requireSession } from "@/lib/auth";
import { visibleUserIds } from "@/lib/rbac";
import { ROLE_RANK, type Role } from "@/lib/accounts";
import { diffFields } from "@/lib/audit";
import { TASK_STATUSES, TASK_STATUS_LABEL } from "@/lib/tasks";
import { isRealDate } from "@/lib/period";

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

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

/**
 * Ai được giao việc cho ai — cha giao cho con, không giao ngang hay ngược lên: tự giao cho mình thì luôn được;
 * Ban giám đốc giao cho mọi người đang hoạt động; Giám đốc khu vực / Quản lý chỉ giao cho người NẰM DƯỚI trong nhánh
 * phòng ban mình phụ trách (vai trò thấp hơn). NVKD không giao được cho ai khác.
 */
async function canAssignTo(
  session: Awaited<ReturnType<typeof requireSession>>,
  assigneeId: string,
): Promise<{ ok: true } | { ok: false; error: string }> {
  if (assigneeId === session.userId) return { ok: true };

  const [assignee] = await db
    .select({ id: users.id, role: users.role })
    .from(users)
    .where(and(eq(users.tenantId, session.tenantId), eq(users.id, assigneeId), eq(users.isActive, true)))
    .limit(1);
  if (!assignee) return { ok: false, error: "Không tìm thấy người nhận việc." };
  if (session.role === "DIRECTOR") return { ok: true };
  if (session.role === "SALES") return { ok: false, error: "Nhân viên chỉ ghi việc cho chính mình." };

  const allowed = await visibleUserIds(session); // null = chưa gán phòng ban
  const below = ROLE_RANK[assignee.role as Role] < ROLE_RANK[session.role];
  if (!allowed || !allowed.includes(assigneeId) || !below) {
    return { ok: false, error: "Bạn chỉ giao việc được cho cấp dưới trong nhánh mình phụ trách." };
  }
  return { ok: true };
}

const createSchema = z.object({
  title: z.string().trim().min(2, "Tên công việc quá ngắn.").max(200),
  description: z.string().trim().max(1000).optional(),
  assigneeId: z.string().uuid().optional().or(z.literal("")),
  startDate: date,
  dueDate: date,
});

/** Thêm việc cần làm. Ai cũng tự ghi việc cho mình; cấp trên giao được cho cấp dưới (xem `canAssignTo`). */
export async function createTaskAction(formData: FormData): Promise<TaskResult> {
  const session = await requireSession();
  const parsed = createSchema.safeParse({
    title: formData.get("title"),
    description: formData.get("description") || undefined,
    assigneeId: formData.get("assigneeId") || "",
    startDate: formData.get("startDate") || "",
    dueDate: formData.get("dueDate") || "",
  });
  if (!parsed.success) return { error: parsed.error.issues[0].message };
  const data = parsed.data;

  if (data.startDate && data.dueDate && data.dueDate < data.startDate) {
    return { error: "Hạn hoàn thành không được trước ngày bắt đầu." };
  }

  const assigneeId = data.assigneeId || session.userId;
  const permission = await canAssignTo(session, assigneeId);
  if (!permission.ok) return { error: permission.error };

  const [created] = await db
    .insert(tasks)
    .values({
      tenantId: session.tenantId,
      assigneeId,
      createdBy: session.userId,
      title: data.title,
      description: data.description ?? null,
      startDate: data.startDate || null,
      dueDate: data.dueDate || null,
    })
    .returning();

  await db.insert(auditLogs).values({
    tenantId: session.tenantId,
    userId: session.userId,
    action: "CREATE",
    entityType: "task",
    entityId: created.id,
    payload: { title: data.title, assigneeId },
  });

  revalidatePath("/cong-viec/danh-sach");
  revalidatePath("/cong-viec/lich");
  revalidatePath("/");
  return { ok: assigneeId === session.userId ? "Đã thêm công việc." : "Đã giao việc." };
}

const updateSchema = z.object({
  id: z.string().uuid(),
  title: z.string().trim().min(2, "Tên công việc quá ngắn.").max(200),
  description: z.string().trim().max(1000).optional(),
  assigneeId: z.string().uuid().optional().or(z.literal("")),
  startDate: date,
  dueDate: date,
});

/**
 * Sửa nội dung công việc (tên, ghi chú, ngày bắt đầu, hạn, người thực hiện). Người tạo sửa được mọi việc do mình tạo;
 * người thực hiện chỉ sửa được việc do chính mình tự ghi — việc được giao thì chỉ đổi trạng thái, không đổi nội dung cấp trên đã giao.
 */
export async function updateTaskAction(formData: FormData): Promise<TaskResult> {
  const session = await requireSession();
  const parsed = updateSchema.safeParse({
    id: formData.get("id"),
    title: formData.get("title"),
    description: formData.get("description") || undefined,
    assigneeId: formData.get("assigneeId") || "",
    startDate: formData.get("startDate") || "",
    dueDate: formData.get("dueDate") || "",
  });
  if (!parsed.success) return { error: parsed.error.issues[0].message };
  const data = parsed.data;

  const [task] = await db
    .select()
    .from(tasks)
    .where(and(eq(tasks.tenantId, session.tenantId), eq(tasks.id, data.id)))
    .limit(1);
  if (!task) return { error: "Không tìm thấy công việc." };
  const isCreator = task.createdBy === session.userId;
  if (!isCreator) return { error: "Chỉ người tạo công việc mới sửa được nội dung. Bạn chỉ đổi được trạng thái." };
  if (data.startDate && data.dueDate && data.dueDate < data.startDate) {
    return { error: "Hạn hoàn thành không được trước ngày bắt đầu." };
  }

  const assigneeId = data.assigneeId || task.assigneeId;
  if (assigneeId !== task.assigneeId) {
    const permission = await canAssignTo(session, assigneeId);
    if (!permission.ok) return { error: permission.error };
  }

  const next = {
    title: data.title,
    description: data.description ?? null,
    startDate: data.startDate || null,
    dueDate: data.dueDate || null,
    assigneeId,
  };
  const changes = diffFields(
    { title: task.title, description: task.description, startDate: task.startDate, dueDate: task.dueDate, assigneeId: task.assigneeId },
    next,
  );
  if (Object.keys(changes).length === 0) return { ok: "Không có thay đổi nào để lưu." };

  await db
    .update(tasks)
    .set({ ...next, updatedAt: new Date() })
    .where(and(eq(tasks.tenantId, session.tenantId), eq(tasks.id, task.id)));

  await db.insert(auditLogs).values({
    tenantId: session.tenantId,
    userId: session.userId,
    action: "UPDATE",
    entityType: "task",
    entityId: task.id,
    payload: { title: next.title, changes },
  });

  revalidatePath("/cong-viec/danh-sach");
  revalidatePath("/cong-viec/lich");
  revalidatePath("/");
  return { ok: "Đã cập nhật công việc." };
}

const statusSchema = z.object({
  id: z.string().uuid(),
  status: z.enum(TASK_STATUSES.map((s) => s.key) as [string, ...string[]]),
  reason: z.string().trim().max(500).optional(),
  newStartDate: date,
  newDueDate: date,
});

/**
 * Đổi trạng thái — người thực hiện hoặc người giao việc. Dời / huỷ / chưa hoàn thành bắt buộc kèm lý do.
 * Khi "Dời" có thể chọn luôn ngày bắt đầu (và hạn) mới, để việc hiện lại đúng ngày trên lịch.
 */
export async function updateTaskStatusAction(formData: FormData): Promise<TaskResult> {
  const session = await requireSession();
  const parsed = statusSchema.safeParse({
    id: formData.get("id"),
    status: formData.get("status"),
    reason: formData.get("reason") || undefined,
    newStartDate: formData.get("newStartDate") || "",
    newDueDate: formData.get("newDueDate") || "",
  });
  if (!parsed.success) return { error: parsed.error.issues[0].message };
  const { id, status, reason, newStartDate, newDueDate } = parsed.data;

  const meta = TASK_STATUSES.find((s) => s.key === status)!;
  if (meta.needsReason && (!reason || reason.length < 3)) {
    return { error: `Trạng thái “${meta.label}” cần ghi lý do.` };
  }

  const [task] = await db
    .select()
    .from(tasks)
    .where(and(eq(tasks.tenantId, session.tenantId), eq(tasks.id, id)))
    .limit(1);
  if (!task || (task.assigneeId !== session.userId && task.createdBy !== session.userId)) {
    return { error: "Bạn không có quyền đổi trạng thái công việc này." };
  }

  // Ngày mới chỉ áp dụng khi dời; hạn mới không được trước ngày bắt đầu (mới hoặc cũ).
  const rescheduling = status === "POSTPONED" && (newStartDate || newDueDate);
  const startAfter = rescheduling && newStartDate ? newStartDate : task.startDate;
  const dueAfter = rescheduling && newDueDate ? newDueDate : task.dueDate;
  if (startAfter && dueAfter && dueAfter < startAfter) {
    return { error: "Hạn hoàn thành không được trước ngày bắt đầu." };
  }
  if (!rescheduling && task.status === status && (task.reason ?? "") === (reason ?? "")) return { ok: "Không có thay đổi." };

  await db
    .update(tasks)
    .set({
      status,
      reason: meta.needsReason ? (reason ?? null) : null,
      completedAt: status === "DONE" ? new Date() : null,
      updatedAt: new Date(),
      ...(rescheduling ? { startDate: startAfter, dueDate: dueAfter } : {}),
    })
    .where(and(eq(tasks.tenantId, session.tenantId), eq(tasks.id, task.id)));

  await db.insert(auditLogs).values({
    tenantId: session.tenantId,
    userId: session.userId,
    action: "UPDATE",
    entityType: "task",
    entityId: task.id,
    payload: {
      title: task.title,
      changes: {
        status: { from: task.status, to: status },
        ...(reason ? { reason: { from: task.reason, to: reason } } : {}),
        ...(rescheduling && startAfter !== task.startDate ? { startDate: { from: task.startDate, to: startAfter } } : {}),
        ...(rescheduling && dueAfter !== task.dueDate ? { dueDate: { from: task.dueDate, to: dueAfter } } : {}),
      },
    },
  });

  revalidatePath("/cong-viec/danh-sach");
  revalidatePath("/cong-viec/lich");
  revalidatePath("/");
  return { ok: `Đã chuyển sang “${TASK_STATUS_LABEL[status]}”.` };
}

/** Xoá công việc — chỉ người tạo. */
export async function deleteTaskAction(formData: FormData): Promise<TaskResult> {
  const session = await requireSession();
  const id = String(formData.get("id"));
  const [task] = await db
    .select({ id: tasks.id, createdBy: tasks.createdBy, title: tasks.title })
    .from(tasks)
    .where(and(eq(tasks.tenantId, session.tenantId), eq(tasks.id, id)))
    .limit(1);
  if (!task || task.createdBy !== session.userId) return { error: "Chỉ người tạo mới xoá được công việc." };

  await db.delete(tasks).where(and(eq(tasks.tenantId, session.tenantId), eq(tasks.id, task.id)));
  await db.insert(auditLogs).values({
    tenantId: session.tenantId,
    userId: session.userId,
    action: "DELETE",
    entityType: "task",
    entityId: task.id,
    payload: { title: task.title },
  });

  revalidatePath("/cong-viec/danh-sach");
  revalidatePath("/cong-viec/lich");
  revalidatePath("/");
  return { ok: "Đã xoá công việc." };
}
