import z from "zod";

import { getTemplateBodyForAction } from "@calcom/features/ee/workflows/lib/actionHelperFunctions";
import compareReminderBodyToTemplate from "@calcom/features/ee/workflows/lib/compareReminderBodyToTemplate";
import { Task } from "@calcom/features/tasker/repository";
import logger from "@calcom/lib/logger";
import { getTranslation } from "@calcom/lib/server/i18n";
import { getTimeFormatStringFromUserTimeFormat } from "@calcom/lib/timeFormat";
import prisma from "@calcom/prisma";
import { scheduleWorkflowNotifications } from "@calcom/trpc/server/routers/viewer/workflows/util";

export const scanWorkflowBodySchema = z.object({
  userId: z.number(),
  // deprecated: use workflowStepId instead
  workflowStepIds: z.array(z.number()).optional(),
  workflowStepId: z.number().optional(),
  createdAt: z.string().optional(),
});

const log = logger.getSubLogger({ prefix: ["[tasker] scanWorkflowBody"] });

export async function scanWorkflowBody(payload: string) {
  const { workflowStepIds, userId, createdAt, workflowStepId } = scanWorkflowBodySchema.parse(
    JSON.parse(payload)
  );

  const stepIdsToScan: number[] = workflowStepIds ? workflowStepIds : [];

  if (workflowStepId && !workflowStepIds) {
    if (createdAt) {
      const hasNewerTask = await Task.hasNewerScanTaskForStepId(workflowStepId, createdAt);

      if (hasNewerTask) return;

      stepIdsToScan.push(workflowStepId);
    } else {
      stepIdsToScan.push(workflowStepId);
    }
  }

  if (stepIdsToScan.length === 0) return;

  const workflowSteps = await prisma.workflowStep.findMany({
    where: {
      id: {
        in: stepIdsToScan,
      },
    },
    include: {
      workflow: {
        select: {
          user: {
            select: {
              locale: true,
              timeFormat: true,
              whitelistWorkflows: true,
            },
          },
        },
      },
    },
  });

  if (process.env.IFFY_API_KEY) {
    for (const workflowStep of workflowSteps) {
      if (!workflowStep.reminderBody) {
        await prisma.workflowStep.update({
          where: {
            id: workflowStep.id,
          },
          data: {
            verifiedAt: new Date(),
          },
        });
        continue;
      }

      const timeFormat = getTimeFormatStringFromUserTimeFormat(workflowStep.workflow.user?.timeFormat);

      // Determine if body is a template
      const defaultTemplate = getTemplateBodyForAction({
        action: workflowStep.action,
        locale: workflowStep.workflow.user?.locale ?? "en",
        t: await getTranslation(workflowStep.workflow.user?.locale ?? "en", "common"),
        template: workflowStep.template,
        timeFormat,
      });

      if (!defaultTemplate) {
        log.error(`Template not found for action ${workflowStep.action}, template ${workflowStep.template}`);
        continue;
      }

      if (
        compareReminderBodyToTemplate({ reminderBody: workflowStep.reminderBody, template: defaultTemplate })
      ) {
        await prisma.workflowStep.update({
          where: {
            id: workflowStep.id,
          },
          data: {
            verifiedAt: new Date(),
          },
        });
        continue;
      }

      const isSpam = await iffyScanBody(workflowStep.reminderBody, workflowStep.id);

      if (isSpam) {
        if (!workflowStep.workflow.user?.whitelistWorkflows) {
          // We won't delete the workflow step incase it is flagged as a false positive
          log.warn(`Workflow step ${workflowStep.id} is spam with body ${workflowStep.reminderBody}`);

          // Return early if spam is detected
          return;
        }
        log.warn(`For whitelisted user, workflow step ${workflowStep.id} marked as spam`);
      }

      await prisma.workflowStep.update({
        where: {
          id: workflowStep.id,
        },
        data: {
          verifiedAt: new Date(),
        },
      });
    }
  }

  if (!process.env.IFFY_API_KEY) {
    log.info("IFFY_API_KEY not set, skipping scan");
    await prisma.workflowStep.updateMany({
      where: {
        id: {
          in: stepIdsToScan,
        },
      },
      data: {
        verifiedAt: new Date(),
      },
    });
  }

  const workflow = await prisma.workflow.findFirst({
    where: {
      steps: {
        some: {
          id: {
            in: stepIdsToScan,
          },
        },
      },
    },
    include: {
      activeOn: true,
      team: true,
    },
  });

  if (!workflow) {
    log.warn(`Workflow with steps ${stepIdsToScan} not found`);
    return;
  }

  const isOrg = !!workflow?.team?.isOrganization;

  await scheduleWorkflowNotifications({
    activeOn: workflow.activeOn.map((activeOn) => activeOn.eventTypeId) ?? [],
    isOrg,
    workflowSteps: workflowSteps.map((step) => ({
      ...step,
      verifiedAt: new Date(),
    })),
    time: workflow.time,
    timeUnit: workflow.timeUnit,
    trigger: workflow.trigger,
    userId,
    teamId: workflow.team?.id || null,
  });
}

export const iffyScanBody = async (body: string, workflowStepId: number) => {
  try {
    const response = await fetch("https://api.iffy.com/api/v1/moderate", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${process.env.IFFY_API_KEY}`,
      },
      body: JSON.stringify({
        clientId: `Workflow step - ${workflowStepId}`,
        name: "Workflow",
        entity: "WorkflowBody",
        content: body,
        passthrough: true,
      }),
    });

    const data = await response.json();
    return data.flagged;
  } catch (error) {
    log.error(`Error scanning workflow body for workflow step ${workflowStepId}:`, error);
  }
};
