import { buildTaskAction } from "@capakraken/shared";
import { VacationStatus, VacationType } from "@capakraken/db";
import { createNotification } from "../lib/create-notification.js";
import { sendEmail } from "../lib/email.js";
import { logger } from "../lib/logger.js";
import { dispatchWebhooks } from "../lib/webhook-dispatcher.js";
import { emitTaskAssigned } from "../sse/event-bus.js";
import type { TRPCContext } from "../trpc.js";

type VacationDb = TRPCContext["db"];

function runVacationBackgroundEffect(
  effectName: string,
  execute: () => unknown,
  metadata: Record<string, unknown> = {},
): void {
  void Promise.resolve()
    .then(execute)
    .catch((error) => {
      logger.error(
        { err: error, effectName, ...metadata },
        "Vacation background side effect failed",
      );
    });
}

async function notifyVacationStatus(
  db: VacationDb,
  vacationId: string,
  resourceId: string,
  newStatus: VacationStatus,
  rejectionReason?: string | null,
) {
  const resource = await db.resource.findUnique({
    where: { id: resourceId },
    select: {
      displayName: true,
      user: { select: { id: true, email: true, name: true } },
    },
  });
  if (!resource?.user) return;

  const statusLabel = newStatus === VacationStatus.APPROVED ? "approved" : "rejected";
  const title = `Vacation request ${statusLabel}`;
  const body = rejectionReason
    ? `Your vacation request was ${statusLabel}. Reason: ${rejectionReason}`
    : `Your vacation request has been ${statusLabel}.`;

  await createNotification({
    db,
    userId: resource.user.id,
    type: `VACATION_${newStatus}`,
    title,
    body,
    entityId: vacationId,
    entityType: "vacation",
  });

  if (resource.user.email) {
    void sendEmail({
      to: resource.user.email,
      subject: `CapaKraken — ${title}`,
      text: body,
    });
  }
}

export function notifyVacationStatusInBackground(
  db: VacationDb,
  vacationId: string,
  resourceId: string,
  newStatus: VacationStatus,
  rejectionReason?: string | null,
): void {
  runVacationBackgroundEffect(
    "notifyVacationStatus",
    () => notifyVacationStatus(db, vacationId, resourceId, newStatus, rejectionReason),
    { vacationId, resourceId, newStatus },
  );
}

export function dispatchVacationWebhookInBackground(
  db: VacationDb,
  event: string,
  payload: Record<string, unknown>,
): void {
  runVacationBackgroundEffect(
    "dispatchWebhooks",
    () => dispatchWebhooks(db, event, payload),
    { event },
  );
}

export async function createVacationApprovalTasks(args: {
  db: VacationDb;
  submittedByUserId: string;
  vacationId: string;
  resourceName: string;
  vacationType: VacationType;
  startDate: Date;
  endDate: Date;
}): Promise<void> {
  const { db, submittedByUserId, vacationId, resourceName, vacationType, startDate, endDate } = args;
  const startStr = startDate.toISOString().slice(0, 10);
  const endStr = endDate.toISOString().slice(0, 10);
  const managers = await db.user.findMany({
    where: { systemRole: { in: ["ADMIN", "MANAGER"] } },
    select: { id: true },
  });

  for (const manager of managers) {
    if (manager.id === submittedByUserId) continue;
    const taskId = await createNotification({
      db,
      userId: manager.id,
      category: "APPROVAL",
      type: "VACATION_APPROVAL",
      priority: "NORMAL",
      title: `Vacation approval: ${resourceName}`,
      body: `${resourceName} requests ${vacationType} from ${startStr} to ${endStr}`,
      taskStatus: "OPEN",
      taskAction: buildTaskAction("approve_vacation", vacationId),
      entityId: vacationId,
      entityType: "vacation",
      link: "/vacations",
      senderId: submittedByUserId,
      channel: "in_app",
    });
    emitTaskAssigned(manager.id, taskId);
  }
}

export async function completeVacationApprovalTasks(
  db: VacationDb,
  vacationId: string,
  completedById?: string,
): Promise<void> {
  await db.notification.updateMany({
    where: {
      taskAction: buildTaskAction("approve_vacation", vacationId),
      category: "APPROVAL",
      taskStatus: "OPEN",
    },
    data: {
      taskStatus: "DONE",
      completedAt: new Date(),
      ...(completedById ? { completedBy: completedById } : {}),
    },
  });
}
