import { db } from "@/lib/db";

// ─── Types ────────────────────────────────────────────────────────────────────

type Audience =
  | "admins" // → users who have "view" on the relevant module
  | "employee" // → the specific employee (pass employeeUserId in data)
  | "manager" // → the reporting manager of the employee
  | "both" // → admins + the employee
  | "all"; // → admins + employee + manager

type EventDef = {
  /** Which module permission gates who counts as "admin" for this event */
  module: string;
  audience: Audience;
  title: (data: Record<string, unknown>) => string;
  body: (data: Record<string, unknown>) => string;
};

// ─── Event catalogue ──────────────────────────────────────────────────────────
// Add a new entry here for every event you want.
// "admins" = users whose role has { module, actions: ["view"] }
// No hardcoded role names anywhere.

const EVENT_CATALOGUE: Record<string, EventDef> = {
  // ── Leave ──────────────────────────────────────────────────────────────────
  "leave.request.submitted": {
    module: "leave",
    audience: "all",
    title: (d) => `Leave request from ${d.employeeName}`,
    body: (d) =>
      `${d.employeeName} has requested ${d.duration} day(s) of ${d.leaveType} leave from ${d.startDate} to ${d.endDate}.`,
  },
  "leave.request.approved": {
    module: "leave",
    audience: "employee",
    title: () => "Your leave request was approved ✅",
    body: (d) =>
      `Your ${d.leaveType} leave (${d.duration} day(s) from ${d.startDate} to ${d.endDate}) has been approved by ${d.approvedBy}.`,
  },
  "leave.request.rejected": {
    module: "leave",
    audience: "employee",
    title: () => "Your leave request was rejected ❌",
    body: (d) =>
      `Your ${d.leaveType} leave request (${d.startDate} to ${d.endDate}) was rejected${d.reason ? `. Reason: "${d.reason}"` : "."}`,
  },
  "leave.request.updated": {
    module: "leave",
    audience: "admins",
    title: (d) => `Leave request updated - ${d.employeeName}`,
    body: (d) =>
      `${d.employeeName} has updated their leave request for ${d.leaveType} (${d.startDate} to ${d.endDate}).`,
  },
  "leave.request.cancelled": {
    module: "leave",
    audience: "admins",
    title: (d) => `Leave request cancelled - ${d.employeeName}`,
    body: (d) =>
      `${d.employeeName} has cancelled their ${d.leaveType} leave request.`,
  },

  // ── Payroll ────────────────────────────────────────────────────────────────
  "payroll.slip.published": {
    module: "payroll",
    audience: "employee",
    title: () => "Your payslip is ready",
    body: (d) =>
      `Your payslip for ${d.month} ${d.year} has been published. Net payable: ${d.netPayable}.`,
  },
  "payroll.approved": {
    module: "payroll",
    audience: "admins",
    title: (d) => `Payroll approved for ${d.month} ${d.year}`,
    body: (d) =>
      `${d.approvedBy} approved payroll for ${d.month} ${d.year} (${d.employeeCount} employees).`,
  },

  // ── Employees ──────────────────────────────────────────────────────────────
  "resignation.submitted": {
    module: "employees",
    audience: "admins",
    title: (d) => `Resignation notice from ${d.employeeName}`,
    body: (d) =>
      `${d.employeeName} submitted a resignation notice${d.lastDay ? ` with last working day ${d.lastDay}` : ""}.`,
  },
  "employee.created": {
    module: "employees",
    audience: "admins",
    title: (d) => `New employee added — ${d.employeeName}`,
    body: (d) =>
      `${d.employeeName} has been added to ${d.department ?? "the system"}.`,
  },

  // ── Documents ──────────────────────────────────────────────────────────────
  "document.expiry.warning": {
    module: "employees",
    audience: "admins",
    title: (d) => `Document expiring soon — ${d.employeeName}`,
    body: (d) =>
      `${d.employeeName}'s ${d.documentType} expires on ${d.expiryDate}.`,
  },

  // ── Attendance ─────────────────────────────────────────────────────────────
  "attendance.absent": {
    module: "attendance",
    audience: "manager",
    title: (d) => `Absent today — ${d.employeeName}`,
    body: (d) =>
      `${d.employeeName} (${d.department}) has no attendance record for ${d.date}.`,
  },
  "attendance.late.checkin": {
    module: "attendance",
    audience: "manager",
    title: (d) => `Late check-in — ${d.employeeName}`,
    body: (d) =>
      `${d.employeeName} checked in late at ${d.checkInTime} (expected ${d.expectedTime}).`,
  },
  "attendance.missing.checkout": {
    module: "attendance",
    audience: "manager",
    title: (d) => `Missing checkout — ${d.employeeName}`,
    body: (d) => `${d.employeeName} did not check out on ${d.date}.`,
  },

  // ── Custom / one-off ───────────────────────────────────────────────────────
  // Pass customTitle, customBody, and optionally override audience/module in data.
  custom: {
    module: "notifications",
    audience: "admins",
    title: (d) => String(d.customTitle ?? "System notification"),
    body: (d) => String(d.customBody ?? ""),
  },
};

// ─── Notify data type ─────────────────────────────────────────────────────────

export type NotifyData = Record<string, unknown> & {
  /** Required when audience is "employee" or "both" or "all" */
  employeeUserId?: string;
  /** Required when audience is "manager" or "all" */
  managerId?: string;
  /** The user who triggered the event — used as notification sender */
  triggeredByUserId?: string;
  /** Override the audience from the event def (useful for "custom") */
  audience?: Audience;
  /** Override the module used for admin resolution (useful for "custom") */
  module?: string;
};

// ─── Main notify() function ───────────────────────────────────────────────────

export async function notify(
  eventType: keyof typeof EVENT_CATALOGUE | (string & {}),
  data: NotifyData,
): Promise<void> {
  try {
    const def = EVENT_CATALOGUE[eventType];
    if (!def) {
      console.warn(`[notify] Unknown event type: "${eventType}"`);
      return;
    }

    const title = def.title(data);
    const body = def.body(data);
    const audience = data.audience ?? def.audience;
    const module_ = data.module ?? def.module;

    // ── Resolve recipients ─────────────────────────────────────────────────
    const recipientIds = new Set<string>();

    if (audience === "admins" || audience === "both" || audience === "all") {
      // Find users whose role has "view" permission on the relevant module.
      // This works for ANY role name — fully dynamic.
      const usersWithAccess = await db.user.findMany({
        where: {
          isActive: true,
          roles: {
            some: {
              role: {
                permissions: {
                  some: {
                    module: module_,
                    actions: { has: "view" },
                  },
                },
              },
            },
          },
        },
        select: { id: true },
      });
      usersWithAccess.forEach((u) => recipientIds.add(u.id));
    }

    if (
      (audience === "employee" || audience === "both" || audience === "all") &&
      data.employeeUserId
    ) {
      recipientIds.add(data.employeeUserId);
    }

    if ((audience === "manager" || audience === "all") && data.managerId) {
      recipientIds.add(data.managerId);
    }

    // Don't notify the person who triggered the event (they know already)
    if (data.triggeredByUserId) {
      recipientIds.delete(data.triggeredByUserId);
    }

    if (recipientIds.size === 0) {
      // No recipients — nothing to do (could be a solo admin acting on themselves)
      return;
    }

    // ── Resolve sender ─────────────────────────────────────────────────────
    const senderId = data.triggeredByUserId || (await getSystemUserId());
    if (!senderId) {
      console.warn("[notify] Could not resolve a sender user id — skipping");
      return;
    }

    // ── Write to DB ────────────────────────────────────────────────────────
    await db.notification.create({
      data: {
        title,
        body,
        type: "targeted",
        createdById: senderId,
        recipients: {
          create: [...recipientIds].map((userId) => ({ userId })),
        },
      },
    });
  } catch (err) {
    // Never crash the calling route — notifications are best-effort
    console.error("[notify] Failed:", err);
  }
}

// ─── Helpers ──────────────────────────────────────────────────────────────────

// Fallback "system" sender: first active user who can manage notifications.
// Cached per process restart.
let _systemUserId: string | null = null;

async function getSystemUserId(): Promise<string | null> {
  if (_systemUserId) return _systemUserId;
  const user = await db.user.findFirst({
    where: {
      isActive: true,
      roles: {
        some: {
          role: {
            permissions: {
              some: {
                module: "notifications",
                actions: { has: "view" },
              },
            },
          },
        },
      },
    },
    select: { id: true },
  });
  _systemUserId = user?.id ?? null;
  return _systemUserId;
}

export { EVENT_CATALOGUE };
