import { createDemandRequirement, fillDemandRequirement } from "@capakraken/application";
import { buildTaskAction, CreateDemandRequirementSchema, FillDemandRequirementSchema } from "@capakraken/shared";
import { z } from "zod";
import { checkBudgetThresholds } from "../lib/budget-alerts.js";
import { generateAutoSuggestions } from "../lib/auto-staffing.js";
import { invalidateDashboardCache } from "../lib/cache.js";
import { logger } from "../lib/logger.js";
import { dispatchWebhooks } from "../lib/webhook-dispatcher.js";
import { emitAllocationCreated, emitAllocationUpdated, emitNotificationCreated } from "../sse/event-bus.js";

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

export function invalidateDashboardCacheInBackground(): void {
  runAllocationBackgroundEffect("invalidateDashboardCache", () => invalidateDashboardCache());
}

export function checkBudgetThresholdsInBackground(
  db: import("@capakraken/db").PrismaClient,
  projectId: string,
): void {
  runAllocationBackgroundEffect(
    "checkBudgetThresholds",
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    () => checkBudgetThresholds(db as any, projectId),
    { projectId },
  );
}

export function dispatchAllocationWebhookInBackground(
  db: import("@capakraken/db").PrismaClient,
  event: string,
  payload: Record<string, unknown>,
): void {
  runAllocationBackgroundEffect(
    "dispatchWebhooks",
    () => dispatchWebhooks(db, event, payload),
    { event },
  );
}

export function generateAutoSuggestionsInBackground(
  db: import("@capakraken/db").PrismaClient,
  demandRequirementId: string,
): void {
  runAllocationBackgroundEffect(
    "generateAutoSuggestions",
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    () => generateAutoSuggestions(db as any, demandRequirementId),
    { demandRequirementId },
  );
}

export async function createDemandRequirementWithEffects(
  db: import("@capakraken/db").PrismaClient,
  input: z.infer<typeof CreateDemandRequirementSchema>,
) {
  const demandRequirement = await db.$transaction(async (tx) => {
    return createDemandRequirement(
      tx as unknown as Parameters<typeof createDemandRequirement>[0],
      input,
    );
  });

  emitAllocationCreated({
    id: demandRequirement.id,
    projectId: demandRequirement.projectId,
    resourceId: null,
  });
  invalidateDashboardCacheInBackground();

  const [project, roleEntity, managers] = await Promise.all([
    db.project.findUnique({
      where: { id: demandRequirement.projectId },
      select: { name: true },
    }),
    demandRequirement.roleId
      ? db.role.findUnique({
          where: { id: demandRequirement.roleId },
          select: { name: true },
        })
      : Promise.resolve(null),
    db.user.findMany({
      where: { systemRole: { in: ["ADMIN", "MANAGER"] } },
      select: { id: true },
    }),
  ]);
  const roleName = roleEntity?.name ?? demandRequirement.role ?? "Unspecified role";
  const projectName = project?.name ?? "Unknown project";
  const headcount = demandRequirement.headcount ?? 1;

  for (const manager of managers) {
    const task = await db.notification.create({
      data: {
        userId: manager.id,
        category: "TASK",
        type: "DEMAND_FILL",
        priority: "NORMAL",
        title: `Staff demand: ${roleName} for ${projectName}`,
        body: `${headcount} ${roleName} needed for project ${projectName}`,
        taskStatus: "OPEN",
        taskAction: buildTaskAction("fill_demand", demandRequirement.id),
        entityId: demandRequirement.id,
        entityType: "demand",
        link: `/projects/${demandRequirement.projectId}`,
        channel: "in_app",
      },
    });
    emitNotificationCreated(manager.id, task.id);
  }

  checkBudgetThresholdsInBackground(db, demandRequirement.projectId);
  generateAutoSuggestionsInBackground(db, demandRequirement.id);

  return demandRequirement;
}

export async function fillDemandRequirementWithEffects(
  db: import("@capakraken/db").PrismaClient,
  input: z.infer<typeof FillDemandRequirementSchema>,
) {
  const result = await fillDemandRequirement(db, input);

  emitAllocationCreated({
    id: result.assignment.id,
    projectId: result.assignment.projectId,
    resourceId: result.assignment.resourceId,
  });

  emitAllocationUpdated({
    id: result.updatedDemandRequirement.id,
    projectId: result.updatedDemandRequirement.projectId,
    resourceId: null,
  });
  invalidateDashboardCacheInBackground();
  checkBudgetThresholdsInBackground(db, result.assignment.projectId);

  if (result.updatedDemandRequirement.headcount > 0
    && result.updatedDemandRequirement.status !== "COMPLETED") {
    generateAutoSuggestionsInBackground(db, result.updatedDemandRequirement.id);
  }

  return result;
}
