import { prisma } from "@/lib/__mocks__/database";
import { ActionClass, Prisma, Survey } from "@prisma/client";
import { beforeEach, describe, expect, test, vi } from "vitest";
import { testInputValidation } from "vitestSetup";
import { PrismaErrorType } from "@formbricks/database/types/error";
import { TSurveyFollowUp } from "@formbricks/database/types/survey-follow-up";
import { TActionClass } from "@formbricks/types/action-classes";
import { DatabaseError, InvalidInputError, ResourceNotFoundError } from "@formbricks/types/errors";
import { TSegment } from "@formbricks/types/segment";
import { TSurvey, TSurveyCreateInput, TSurveyQuestionTypeEnum } from "@formbricks/types/surveys/types";
import { getActionClasses } from "@/lib/actionClass/service";
import {
  getOrganizationByEnvironmentId,
  subscribeOrganizationMembersToSurveyResponses,
} from "@/lib/organization/service";
import { evaluateLogic } from "@/lib/surveyLogic/utils";
import {
  mockActionClass,
  mockId,
  mockOrganizationOutput,
  mockSurveyOutput,
  mockSurveyWithLogic,
  mockTransformedSurveyOutput,
  updateSurveyInput,
} from "./__mock__/survey.mock";
import {
  createSurvey,
  getSurvey,
  getSurveyCount,
  getSurveys,
  getSurveysByActionClassId,
  getSurveysBySegmentId,
  handleTriggerUpdates,
  loadNewSegmentInSurvey,
  updateSurvey,
  updateSurveyInternal,
} from "./service";

// Mock organization service
vi.mock("@/lib/organization/service", () => ({
  getOrganizationByEnvironmentId: vi.fn().mockResolvedValue({
    id: "org123",
  }),
  subscribeOrganizationMembersToSurveyResponses: vi.fn(),
}));

// Mock actionClass service
vi.mock("@/lib/actionClass/service", () => ({
  getActionClasses: vi.fn(),
}));

beforeEach(() => {
  prisma.survey.count.mockResolvedValue(1);
});

describe("evaluateLogic with mockSurveyWithLogic", () => {
  test("should return true when q1 answer is blue", () => {
    const data = { q1: "blue" };
    const variablesData = {};

    const result = evaluateLogic(
      mockSurveyWithLogic,
      data,
      variablesData,
      mockSurveyWithLogic.blocks[0].logic![0].conditions,
      "default"
    );
    expect(result).toBe(true);
  });

  test("should return false when q1 answer is not blue", () => {
    const data = { q1: "red" };
    const variablesData = {};

    const result = evaluateLogic(
      mockSurveyWithLogic,
      data,
      variablesData,
      mockSurveyWithLogic.blocks[0].logic![0].conditions,
      "default"
    );
    expect(result).toBe(false);
  });

  test("should return true when q1 is blue and q2 is pizza", () => {
    const data = { q1: "blue", q2: "pizza" };
    const variablesData = {};

    const result = evaluateLogic(
      mockSurveyWithLogic,
      data,
      variablesData,
      mockSurveyWithLogic.blocks[0].logic![1].conditions,
      "default"
    );
    expect(result).toBe(true);
  });

  test("should return false when q1 is blue but q2 is not pizza", () => {
    const data = { q1: "blue", q2: "burger" };
    const variablesData = {};

    const result = evaluateLogic(
      mockSurveyWithLogic,
      data,
      variablesData,
      mockSurveyWithLogic.blocks[0].logic![1].conditions,
      "default"
    );
    expect(result).toBe(false);
  });

  test("should return true when q2 is pizza or q3 is Inception", () => {
    const data = { q2: "pizza", q3: "Inception" };
    const variablesData = {};

    const result = evaluateLogic(
      mockSurveyWithLogic,
      data,
      variablesData,
      mockSurveyWithLogic.blocks[0].logic![2].conditions,
      "default"
    );
    expect(result).toBe(true);
  });

  test("should return true when var1 is equal to single select question value", () => {
    const data = { q4: "lmao" };
    const variablesData = { siog1dabtpo3l0a3xoxw2922: "lmao" };

    const result = evaluateLogic(
      mockSurveyWithLogic,
      data,
      variablesData,
      mockSurveyWithLogic.blocks[0].logic![3].conditions,
      "default"
    );
    expect(result).toBe(true);
  });

  test("should return false when var1 is not equal to single select question value", () => {
    const data = { q4: "lol" };
    const variablesData = { siog1dabtpo3l0a3xoxw2922: "damn" };

    const result = evaluateLogic(
      mockSurveyWithLogic,
      data,
      variablesData,
      mockSurveyWithLogic.blocks[0].logic![3].conditions,
      "default"
    );
    expect(result).toBe(false);
  });

  test("should return true when var2 is greater than 30 and less than open text number value", () => {
    const data = { q5: "40" };
    const variablesData = { km1srr55owtn2r7lkoh5ny1u: 35 };

    const result = evaluateLogic(
      mockSurveyWithLogic,
      data,
      variablesData,
      mockSurveyWithLogic.blocks[0].logic![4].conditions,
      "default"
    );
    expect(result).toBe(true);
  });

  test("should return false when var2 is not greater than 30 or greater than open text number value", () => {
    const data = { q5: "40" };
    const variablesData = { km1srr55owtn2r7lkoh5ny1u: 25 };

    const result = evaluateLogic(
      mockSurveyWithLogic,
      data,
      variablesData,
      mockSurveyWithLogic.blocks[0].logic![4].conditions,
      "default"
    );
    expect(result).toBe(false);
  });

  test("should return for complex condition", () => {
    const data = { q6: ["lmao", "XD"], q1: "green", q2: "pizza", q3: "inspection", name: "pizza" };
    const variablesData = { siog1dabtpo3l0a3xoxw2922: "tokyo" };

    const result = evaluateLogic(
      mockSurveyWithLogic,
      data,
      variablesData,
      mockSurveyWithLogic.blocks[0].logic![5].conditions,
      "default"
    );
    expect(result).toBe(true);
  });
});

describe("Tests for getSurvey", () => {
  describe("Happy Path", () => {
    test("Returns a survey", async () => {
      prisma.survey.findUnique.mockResolvedValueOnce(mockSurveyOutput);
      const survey = await getSurvey(mockId);
      expect(survey).toEqual(mockTransformedSurveyOutput);
    });

    test("Returns null if survey is not found", async () => {
      prisma.survey.findUnique.mockResolvedValueOnce(null);
      const survey = await getSurvey(mockId);
      expect(survey).toBeNull();
    });
  });

  describe("Sad Path", () => {
    testInputValidation(getSurvey, "123#");

    test("should throw a DatabaseError error if there is a PrismaClientKnownRequestError", async () => {
      const mockErrorMessage = "Mock error message";
      const errToThrow = new Prisma.PrismaClientKnownRequestError(mockErrorMessage, {
        code: PrismaErrorType.UniqueConstraintViolation,
        clientVersion: "0.0.1",
      });
      prisma.survey.findUnique.mockRejectedValue(errToThrow);
      await expect(getSurvey(mockId)).rejects.toThrow(DatabaseError);
    });

    test("should throw an error if there is an unknown error", async () => {
      const mockErrorMessage = "Mock error message";
      prisma.survey.findUnique.mockRejectedValue(new Error(mockErrorMessage));
      await expect(getSurvey(mockId)).rejects.toThrow(Error);
    });
  });
});

describe("Tests for getSurveysByActionClassId", () => {
  describe("Happy Path", () => {
    test("Returns an array of surveys for a given actionClassId", async () => {
      prisma.survey.findMany.mockResolvedValueOnce([mockSurveyOutput]);
      const surveys = await getSurveysByActionClassId(mockId);
      expect(surveys).toEqual([mockTransformedSurveyOutput]);
    });

    test("Returns an empty array if no surveys are found", async () => {
      prisma.survey.findMany.mockResolvedValueOnce([]);
      const surveys = await getSurveysByActionClassId(mockId);
      expect(surveys).toEqual([]);
    });
  });

  describe("Sad Path", () => {
    testInputValidation(getSurveysByActionClassId, "123#");

    test("should throw an error if there is an unknown error", async () => {
      const mockErrorMessage = "Unknown error occurred";
      prisma.survey.findMany.mockRejectedValue(new Error(mockErrorMessage));
      await expect(getSurveysByActionClassId(mockId)).rejects.toThrow(Error);
    });
  });
});

describe("Tests for getSurveys", () => {
  describe("Happy Path", () => {
    test("Returns an array of surveys for a given environmentId, limit(optional) and offset(optional)", async () => {
      prisma.survey.findMany.mockResolvedValueOnce([mockSurveyOutput]);
      const surveys = await getSurveys(mockId);
      expect(surveys).toEqual([mockTransformedSurveyOutput]);
    });

    test("Returns an empty array if no surveys are found", async () => {
      prisma.survey.findMany.mockResolvedValueOnce([]);

      const surveys = await getSurveys(mockId);
      expect(surveys).toEqual([]);
    });
  });

  describe("Sad Path", () => {
    testInputValidation(getSurveysByActionClassId, "123#");

    test("should throw a DatabaseError error if there is a PrismaClientKnownRequestError", async () => {
      const mockErrorMessage = "Mock error message";
      const errToThrow = new Prisma.PrismaClientKnownRequestError(mockErrorMessage, {
        code: PrismaErrorType.UniqueConstraintViolation,
        clientVersion: "0.0.1",
      });

      prisma.survey.findMany.mockRejectedValue(errToThrow);
      await expect(getSurveys(mockId)).rejects.toThrow(DatabaseError);
    });

    test("should throw an error if there is an unknown error", async () => {
      const mockErrorMessage = "Unknown error occurred";
      prisma.survey.findMany.mockRejectedValue(new Error(mockErrorMessage));
      await expect(getSurveys(mockId)).rejects.toThrow(Error);
    });
  });
});

describe("Tests for updateSurvey", () => {
  beforeEach(() => {
    vi.mocked(getActionClasses).mockResolvedValueOnce([mockActionClass] as TActionClass[]);
    vi.mocked(getOrganizationByEnvironmentId).mockResolvedValueOnce(mockOrganizationOutput);
  });

  describe("Happy Path", () => {
    test("Updates a survey successfully", async () => {
      prisma.survey.findUnique.mockResolvedValueOnce(mockSurveyOutput);
      prisma.survey.update.mockResolvedValueOnce(mockSurveyOutput);
      const updatedSurvey = await updateSurvey(updateSurveyInput);
      expect(updatedSurvey).toEqual(mockTransformedSurveyOutput);
    });

    // Note: Language handling tests (for languages.length > 0 fix) are covered in
    // apps/web/modules/survey/editor/lib/survey.test.ts where we have better control
    // over the test mocks. The key fix ensures languages.length > 0 (not > 1) is used.
  });

  describe("Sad Path", () => {
    testInputValidation(updateSurvey, "123#");

    test("Throws ResourceNotFoundError if the survey does not exist", async () => {
      prisma.survey.findUnique.mockRejectedValueOnce(
        new ResourceNotFoundError("Survey", updateSurveyInput.id)
      );
      await expect(updateSurvey(updateSurveyInput)).rejects.toThrow(ResourceNotFoundError);
    });

    test("should throw a DatabaseError error if there is a PrismaClientKnownRequestError", async () => {
      const mockErrorMessage = "Mock error message";
      const errToThrow = new Prisma.PrismaClientKnownRequestError(mockErrorMessage, {
        code: PrismaErrorType.UniqueConstraintViolation,
        clientVersion: "0.0.1",
      });
      prisma.survey.findUnique.mockResolvedValueOnce(mockSurveyOutput);
      prisma.survey.update.mockRejectedValue(errToThrow);
      await expect(updateSurvey(updateSurveyInput)).rejects.toThrow(DatabaseError);
    });

    test("should throw an error if there is an unknown error", async () => {
      const mockErrorMessage = "Unknown error occurred";
      prisma.survey.findUnique.mockResolvedValueOnce(mockSurveyOutput);
      prisma.survey.update.mockRejectedValue(new Error(mockErrorMessage));
      await expect(updateSurvey(updateSurveyInput)).rejects.toThrow(Error);
    });
  });
});

describe("Tests for getSurveyCount service", () => {
  describe("Happy Path", () => {
    test("Counts the total number of surveys for a given environment ID", async () => {
      const count = await getSurveyCount(mockId);
      expect(count).toEqual(1);
    });

    test("Returns zero count when there are no surveys for a given environment ID", async () => {
      prisma.survey.count.mockResolvedValue(0);
      const count = await getSurveyCount(mockId);
      expect(count).toEqual(0);
    });
  });

  describe("Sad Path", () => {
    testInputValidation(getSurveyCount, "123#");

    test("Throws a generic Error for other unexpected issues", async () => {
      const mockErrorMessage = "Mock error message";
      prisma.survey.count.mockRejectedValue(new Error(mockErrorMessage));

      await expect(getSurveyCount(mockId)).rejects.toThrow(Error);
    });
  });
});

describe("Tests for handleTriggerUpdates", () => {
  const mockEnvironmentId = "env-123";
  const mockActionClassId1 = "action-123";
  const mockActionClassId2 = "action-456";

  const mockActionClasses: ActionClass[] = [
    {
      id: mockActionClassId1,
      createdAt: new Date(),
      updatedAt: new Date(),
      environmentId: mockEnvironmentId,
      name: "Test Action 1",
      description: "Test action description 1",
      type: "code",
      key: "test-action-1",
      noCodeConfig: null,
    },
    {
      id: mockActionClassId2,
      createdAt: new Date(),
      updatedAt: new Date(),
      environmentId: mockEnvironmentId,
      name: "Test Action 2",
      description: "Test action description 2",
      type: "code",
      key: "test-action-2",
      noCodeConfig: null,
    },
  ];

  test("adds new triggers correctly", () => {
    const updatedTriggers = [
      {
        actionClass: {
          id: mockActionClassId1,
          name: "Test Action 1",
          environmentId: mockEnvironmentId,
          type: "code",
          key: "test-action-1",
        },
      },
    ] as TSurvey["triggers"];
    const currentTriggers: TSurvey["triggers"] = [];

    const result = handleTriggerUpdates(updatedTriggers, currentTriggers, mockActionClasses);

    expect(result).toHaveProperty("create");
    expect(result.create).toEqual([{ actionClassId: mockActionClassId1 }]);
  });

  test("removes deleted triggers correctly", () => {
    const updatedTriggers: TSurvey["triggers"] = [];
    const currentTriggers = [
      {
        actionClass: {
          id: mockActionClassId1,
          name: "Test Action 1",
          environmentId: mockEnvironmentId,
          type: "code",
          key: "test-action-1",
        },
      },
    ] as TSurvey["triggers"];

    const result = handleTriggerUpdates(updatedTriggers, currentTriggers, mockActionClasses);

    expect(result).toHaveProperty("deleteMany");
    expect(result.deleteMany).toEqual({ actionClassId: { in: [mockActionClassId1] } });
  });

  test("handles both adding and removing triggers", () => {
    const updatedTriggers = [
      {
        actionClass: {
          id: mockActionClassId2,
          name: "Test Action 2",
          environmentId: mockEnvironmentId,
          type: "code",
          key: "test-action-2",
        },
      },
    ] as TSurvey["triggers"];

    const currentTriggers = [
      {
        actionClass: {
          id: mockActionClassId1,
          name: "Test Action 1",
          environmentId: mockEnvironmentId,
          type: "code",
          key: "test-action-1",
        },
      },
    ] as TSurvey["triggers"];

    const result = handleTriggerUpdates(updatedTriggers, currentTriggers, mockActionClasses);

    expect(result).toHaveProperty("create");
    expect(result).toHaveProperty("deleteMany");
    expect(result.create).toEqual([{ actionClassId: mockActionClassId2 }]);
    expect(result.deleteMany).toEqual({ actionClassId: { in: [mockActionClassId1] } });
  });

  test("returns empty object when no triggers provided", () => {
    // @ts-expect-error -- This is a test case to check the empty input
    const result = handleTriggerUpdates(undefined, [], mockActionClasses);
    expect(result).toEqual({});
  });

  test("throws InvalidInputError for invalid trigger IDs", () => {
    const updatedTriggers = [
      {
        actionClass: {
          id: "invalid-action-id",
          name: "Invalid Action",
          environmentId: mockEnvironmentId,
          type: "code",
          key: "invalid-action",
        },
      },
    ] as TSurvey["triggers"];

    const currentTriggers: TSurvey["triggers"] = [];

    expect(() => handleTriggerUpdates(updatedTriggers, currentTriggers, mockActionClasses)).toThrow(
      InvalidInputError
    );
  });

  test("throws InvalidInputError for duplicate trigger IDs", () => {
    const updatedTriggers = [
      {
        actionClass: {
          id: mockActionClassId1,
          name: "Test Action 1",
          environmentId: mockEnvironmentId,
          type: "code",
          key: "test-action-1",
        },
      },
      {
        actionClass: {
          id: mockActionClassId1, // Duplicated ID
          name: "Test Action 1",
          environmentId: mockEnvironmentId,
          type: "code",
          key: "test-action-1",
        },
      },
    ] as TSurvey["triggers"];
    const currentTriggers: TSurvey["triggers"] = [];

    expect(() => handleTriggerUpdates(updatedTriggers, currentTriggers, mockActionClasses)).toThrow(
      InvalidInputError
    );
  });
});

describe("Tests for createSurvey", () => {
  const mockEnvironmentId = "env123";
  const mockUserId = "user123";

  const mockCreateSurveyInput = {
    name: "Test Survey",
    type: "app" as const,
    createdBy: mockUserId,
    status: "inProgress" as const,
    welcomeCard: {
      enabled: true,
      headline: { default: "Welcome" },
      html: { default: "<p>Welcome to our survey</p>" },
    },
    questions: [
      {
        id: "q1",
        type: TSurveyQuestionTypeEnum.OpenText,
        inputType: "text",
        headline: { default: "What is your favorite color?" },
        required: true,
        charLimit: {
          enabled: false,
        },
      },
      {
        id: "q2",
        type: TSurveyQuestionTypeEnum.OpenText,
        inputType: "text",
        headline: { default: "What is your favorite food?" },
        required: true,
        charLimit: {
          enabled: false,
        },
      },
      {
        id: "q3",
        type: TSurveyQuestionTypeEnum.OpenText,
        inputType: "text",
        headline: { default: "What is your favorite movie?" },
        required: true,
        charLimit: {
          enabled: false,
        },
      },
      {
        id: "q4",
        type: TSurveyQuestionTypeEnum.MultipleChoiceSingle,
        headline: { default: "Select a number:" },
        choices: [
          { id: "mvedaklp0gxxycprpyhhwen7", label: { default: "lol" } },
          { id: "i7ws8uqyj66q5x086vbqtm8n", label: { default: "lmao" } },
          { id: "cy8hbbr9e2q6ywbfjbzwdsqn", label: { default: "XD" } },
          { id: "sojc5wwxc5gxrnuib30w7t6s", label: { default: "hehe" } },
        ],
        required: true,
      },
      {
        id: "q5",
        type: TSurveyQuestionTypeEnum.OpenText,
        inputType: "number",
        headline: { default: "Select your age group:" },
        required: true,
        charLimit: {
          enabled: false,
        },
      },
      {
        id: "q6",
        type: TSurveyQuestionTypeEnum.MultipleChoiceMulti,
        headline: { default: "Select your age group:" },
        required: true,
        choices: [
          { id: "mvedaklp0gxxycprpyhhwen7", label: { default: "lol" } },
          { id: "i7ws8uqyj66q5x086vbqtm8n", label: { default: "lmao" } },
          { id: "cy8hbbr9e2q6ywbfjbzwdsqn", label: { default: "XD" } },
          { id: "sojc5wwxc5gxrnuib30w7t6s", label: { default: "hehe" } },
        ],
      },
    ],
    variables: [],
    hiddenFields: { enabled: false, fieldIds: [] },
    endings: [],
    displayOption: "respondMultiple" as const,
    languages: [],
  } as TSurveyCreateInput;

  const mockActionClasses = [
    {
      id: "action-123",
      createdAt: new Date(),
      updatedAt: new Date(),
      environmentId: mockEnvironmentId,
      name: "Test Action",
      description: "Test action description",
      type: "code",
      key: "test-action",
      noCodeConfig: null,
    },
  ];

  beforeEach(() => {
    vi.mocked(getActionClasses).mockResolvedValue(mockActionClasses as TActionClass[]);
  });

  describe("Happy Path", () => {
    test("creates a survey successfully", async () => {
      vi.mocked(getOrganizationByEnvironmentId).mockResolvedValueOnce(mockOrganizationOutput);
      prisma.survey.create.mockResolvedValueOnce({
        ...mockSurveyOutput,
      });

      const result = await createSurvey(mockEnvironmentId, mockCreateSurveyInput);

      expect(prisma.survey.create).toHaveBeenCalled();
      expect(result.name).toEqual(mockSurveyOutput.name);
      expect(subscribeOrganizationMembersToSurveyResponses).toHaveBeenCalled();
    });

    test("creates a private segment for app surveys", async () => {
      vi.mocked(getOrganizationByEnvironmentId).mockResolvedValueOnce(mockOrganizationOutput);
      prisma.survey.create.mockResolvedValueOnce({
        ...mockSurveyOutput,
        type: "app",
      });

      prisma.segment.create.mockResolvedValueOnce({
        id: "segment-123",
        environmentId: mockEnvironmentId,
        title: mockSurveyOutput.id,
        isPrivate: true,
        filters: [],
        createdAt: new Date(),
        updatedAt: new Date(),
      } as unknown as TSegment);

      await createSurvey(mockEnvironmentId, {
        ...mockCreateSurveyInput,
        type: "app",
      });

      expect(prisma.segment.create).toHaveBeenCalled();
      expect(prisma.survey.update).toHaveBeenCalled();
    });

    test("creates survey with follow-ups", async () => {
      vi.mocked(getOrganizationByEnvironmentId).mockResolvedValueOnce(mockOrganizationOutput);
      const followUp = {
        id: "followup1",
        name: "Follow up 1",
        trigger: { type: "response", properties: null },
        action: {
          type: "send-email",
          properties: {
            to: "abc@example.com",
            attachResponseData: true,
            body: "Hello",
            from: "hello@exmaple.com",
            replyTo: ["hello@example.com"],
            subject: "Follow up",
          },
        },
        surveyId: mockSurveyOutput.id,
        createdAt: new Date(),
        updatedAt: new Date(),
      } as TSurveyFollowUp;

      const surveyWithFollowUps = {
        ...mockCreateSurveyInput,
        followUps: [followUp],
      };

      prisma.survey.create.mockResolvedValueOnce({
        ...mockSurveyOutput,
      });

      await createSurvey(mockEnvironmentId, surveyWithFollowUps);

      expect(prisma.survey.create).toHaveBeenCalledWith(
        expect.objectContaining({
          data: expect.objectContaining({
            followUps: {
              create: [
                expect.objectContaining({
                  name: "Follow up 1",
                }),
              ],
            },
          }),
        })
      );
    });
  });

  describe("Sad Path", () => {
    testInputValidation(createSurvey, "123#", mockCreateSurveyInput);

    test("throws ResourceNotFoundError if organization not found", async () => {
      vi.mocked(getOrganizationByEnvironmentId).mockResolvedValueOnce(null);
      await expect(createSurvey(mockEnvironmentId, mockCreateSurveyInput)).rejects.toThrow(
        ResourceNotFoundError
      );
    });

    test("throws DatabaseError if there is a Prisma error", async () => {
      vi.mocked(getOrganizationByEnvironmentId).mockResolvedValueOnce(mockOrganizationOutput);
      const mockError = new Prisma.PrismaClientKnownRequestError("Database error", {
        code: PrismaErrorType.UniqueConstraintViolation,
        clientVersion: "1.0.0",
      });
      prisma.survey.create.mockRejectedValueOnce(mockError);

      await expect(createSurvey(mockEnvironmentId, mockCreateSurveyInput)).rejects.toThrow(DatabaseError);
    });
  });
});

describe("Tests for loadNewSegmentInSurvey", () => {
  const mockSurveyId = mockId;
  const mockNewSegmentId = "segment456";
  const mockCurrentSegmentId = "segment-123";
  const mockEnvironmentId = "env-123";

  describe("Happy Path", () => {
    test("loads new segment successfully", async () => {
      // Set up mocks for existing survey
      prisma.survey.findUnique.mockResolvedValueOnce({
        ...mockSurveyOutput,
      });
      // Mock segment exists
      prisma.segment.findUnique.mockResolvedValueOnce({
        id: mockNewSegmentId,
        environmentId: mockEnvironmentId,
        filters: [],
        title: "Test Segment",
        isPrivate: false,
        createdAt: new Date(),
        updatedAt: new Date(),
        description: "Test Segment Description",
      });
      // Mock survey update
      prisma.survey.update.mockResolvedValueOnce({
        ...mockSurveyOutput,
        segmentId: mockNewSegmentId,
      });
      const result = await loadNewSegmentInSurvey(mockSurveyId, mockNewSegmentId);
      expect(prisma.survey.update).toHaveBeenCalledWith({
        where: { id: mockSurveyId },
        data: {
          segment: {
            connect: {
              id: mockNewSegmentId,
            },
          },
        },
        select: expect.anything(),
      });
      expect(result).toEqual(
        expect.objectContaining({
          segmentId: mockNewSegmentId,
        })
      );
    });

    test("deletes private segment when changing to a new segment", async () => {
      const mockSegment = {
        id: mockCurrentSegmentId,
        environmentId: mockEnvironmentId,
        title: mockId, // Private segments have title = surveyId
        isPrivate: true,
        filters: [],
        surveys: [mockSurveyId],
        createdAt: new Date(),
        updatedAt: new Date(),
        description: "Test Segment Description",
      };

      // Set up mocks for existing survey with private segment
      prisma.survey.findUnique.mockResolvedValueOnce({
        ...mockSurveyOutput,
        segment: mockSegment,
      } as Survey);

      // Mock segment exists
      prisma.segment.findUnique.mockResolvedValueOnce({
        ...mockSegment,
        id: mockNewSegmentId,
        environmentId: mockEnvironmentId,
      });

      // Mock survey update
      prisma.survey.update.mockResolvedValueOnce({
        ...mockSurveyOutput,
        segment: {
          id: mockNewSegmentId,
          environmentId: mockEnvironmentId,
          title: "Test Segment",
          isPrivate: false,
          filters: [],
          surveys: [{ id: mockSurveyId }],
        },
      } as Survey);

      // Mock segment delete
      prisma.segment.delete.mockResolvedValueOnce({
        id: mockCurrentSegmentId,
        environmentId: mockEnvironmentId,
        surveys: [{ id: mockSurveyId }],
      } as unknown as TSegment);

      await loadNewSegmentInSurvey(mockSurveyId, mockNewSegmentId);

      // Verify the private segment was deleted
      expect(prisma.segment.delete).toHaveBeenCalledWith({
        where: { id: mockCurrentSegmentId },
        select: expect.anything(),
      });
    });
  });

  describe("Sad Path", () => {
    testInputValidation(loadNewSegmentInSurvey, "123#", "123#");

    test("throws ResourceNotFoundError when survey not found", async () => {
      prisma.survey.findUnique.mockResolvedValueOnce(null);

      await expect(loadNewSegmentInSurvey(mockSurveyId, mockNewSegmentId)).rejects.toThrow(
        ResourceNotFoundError
      );
    });

    test("throws ResourceNotFoundError when segment not found", async () => {
      // Set up mock for existing survey
      prisma.survey.findUnique.mockResolvedValueOnce({
        ...mockSurveyOutput,
      });

      // Segment not found
      prisma.segment.findUnique.mockResolvedValueOnce(null);

      await expect(loadNewSegmentInSurvey(mockSurveyId, mockNewSegmentId)).rejects.toThrow(
        ResourceNotFoundError
      );
    });

    test("throws DatabaseError on Prisma error", async () => {
      // Set up mock for existing survey
      prisma.survey.findUnique.mockResolvedValueOnce({
        ...mockSurveyOutput,
      });

      //   // Mock segment exists
      prisma.segment.findUnique.mockResolvedValueOnce({
        id: mockNewSegmentId,
        environmentId: mockEnvironmentId,
        filters: [],
        title: "Test Segment",
        isPrivate: false,
        createdAt: new Date(),
        updatedAt: new Date(),
        description: "Test Segment Description",
      });

      // Mock Prisma error on update
      const mockError = new Prisma.PrismaClientKnownRequestError("Database error", {
        code: PrismaErrorType.UniqueConstraintViolation,
        clientVersion: "1.0.0",
      });

      prisma.survey.update.mockRejectedValueOnce(mockError);

      await expect(loadNewSegmentInSurvey(mockSurveyId, mockNewSegmentId)).rejects.toThrow(DatabaseError);
    });
  });
});

describe("Tests for getSurveysBySegmentId", () => {
  const mockSegmentId = "segment-123";

  describe("Happy Path", () => {
    test("returns surveys associated with a segment", async () => {
      prisma.survey.findMany.mockResolvedValueOnce([mockSurveyOutput]);

      const result = await getSurveysBySegmentId(mockSegmentId);

      expect(prisma.survey.findMany).toHaveBeenCalledWith({
        where: { segmentId: mockSegmentId },
        select: expect.anything(),
      });

      expect(result).toHaveLength(1);
      expect(result[0]).toEqual(
        expect.objectContaining({
          id: mockSurveyOutput.id,
        })
      );
    });

    test("returns empty array when no surveys found", async () => {
      prisma.survey.findMany.mockResolvedValueOnce([]);

      const result = await getSurveysBySegmentId(mockSegmentId);

      expect(result).toEqual([]);
    });
  });

  describe("Sad Path", () => {
    test("throws DatabaseError on Prisma error", async () => {
      const mockError = new Prisma.PrismaClientKnownRequestError("Database error", {
        code: PrismaErrorType.UniqueConstraintViolation,
        clientVersion: "1.0.0",
      });
      prisma.survey.findMany.mockRejectedValueOnce(mockError);

      await expect(getSurveysBySegmentId(mockSegmentId)).rejects.toThrow(DatabaseError);
    });

    test("throws error on unexpected error", async () => {
      prisma.survey.findMany.mockRejectedValueOnce(new Error("Unexpected error"));

      await expect(getSurveysBySegmentId(mockSegmentId)).rejects.toThrow(Error);
    });
  });
});

describe("updateSurveyDraftAction", () => {
  beforeEach(() => {
    vi.mocked(getActionClasses).mockResolvedValue([mockActionClass] as TActionClass[]);
    vi.mocked(getOrganizationByEnvironmentId).mockResolvedValue(mockOrganizationOutput);
  });

  describe("Happy Path", () => {
    test("should save draft with missing translations", async () => {
      prisma.survey.findUnique.mockResolvedValue(mockSurveyOutput);
      prisma.survey.update.mockResolvedValue(mockSurveyOutput);

      // Create a survey with incomplete i18n/fields
      const incompleteSurvey = {
        ...updateSurveyInput,
        questions: [
          {
            id: "q1",
            type: TSurveyQuestionTypeEnum.OpenText,
            // Missing headline or other required fields
          },
        ],
      } as unknown as TSurvey;

      // Expect success (skipValidation = true)
      const result = await updateSurveyInternal(incompleteSurvey, true);
      expect(result).toBeDefined();
      expect(prisma.survey.update).toHaveBeenCalled();
    });

    test("should allow draft with invalid images if gating is applied", async () => {
      prisma.survey.findUnique.mockResolvedValue(mockSurveyOutput);
      prisma.survey.update.mockResolvedValue(mockSurveyOutput);

      const surveyWithInvalidImage = {
        ...updateSurveyInput,
        questions: [
          {
            id: "q1",
            type: TSurveyQuestionTypeEnum.OpenText,
            headline: { default: "Question" },
            imageUrl: "http://invalid-image-url.com/image.txt", // Invalid image extension
          },
        ],
      } as unknown as TSurvey;

      // Expect success (skipValidation = true)
      await updateSurveyInternal(surveyWithInvalidImage, true);
      expect(prisma.survey.update).toHaveBeenCalled();
    });
  });

  describe("Sad Path", () => {
    test("should reject publishing survey with incomplete translations", async () => {
      // Create a draft with missing translations
      const incompleteSurvey = {
        ...updateSurveyInput,
        questions: [
          {
            id: "q1",
            type: TSurveyQuestionTypeEnum.OpenText,
            // Missing headline
          },
        ],
      } as unknown as TSurvey;

      // Expect validation error (skipValidation = false)
      await expect(updateSurveyInternal(incompleteSurvey, false)).rejects.toThrow();
    });
  });
});
