/**
 * Unit Tests for Workflow Management Service
 * Tests tier-based access control and workflow CRUD operations
 */

import { WorkflowManagementService } from "@/lib/services/workflow-management.service";
import { prisma } from "@/lib/db";
import { WorkflowType } from "@/lib/types/workflow.types";

// Mock dependencies
jest.mock("@/lib/db");

jest.mock("@/lib/dual-database", () => ({
  saveToBothDatabases: jest.fn(),
  saveUserToBoth: jest.fn(),
  saveAuditLogToBoth: jest.fn(),
}), { virtual: true });
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { saveToBothDatabases } = require("@/lib/dual-database");

describe("WorkflowManagementService", () => {
  const mockUserId = "user-123";
  const mockWorkflowId = "workflow-123";

  beforeEach(() => {
    jest.clearAllMocks();
  });

  describe("getAllowedWorkflowsByTier", () => {
    it("should return correct workflows for tier1 (Starter)", () => {
      const workflows =
        WorkflowManagementService.getAllowedWorkflowsByTier("tier1");
      expect(workflows).toEqual(["ai_agent", "whatsapp", "appointments"]);
      expect(workflows).toHaveLength(3);
    });

    it("should return correct workflows for tier2 (Professional)", () => {
      const workflows =
        WorkflowManagementService.getAllowedWorkflowsByTier("tier2");
      expect(workflows).toContain("ai_agent");
      expect(workflows).toContain("quotations");
      expect(workflows).toContain("crm");
      expect(workflows).toHaveLength(7);
    });

    it("should return correct workflows for tier3 (Business)", () => {
      const workflows =
        WorkflowManagementService.getAllowedWorkflowsByTier("tier3");
      expect(workflows).toContain("voice_agent");
      expect(workflows).toContain("multi_staff");
      expect(workflows).toContain("recurring");
      expect(workflows).toHaveLength(10);
    });

    it("should return correct workflows for tier4 (Enterprise)", () => {
      const workflows =
        WorkflowManagementService.getAllowedWorkflowsByTier("tier4");
      expect(workflows).toContain("social_media");
      expect(workflows).toContain("ai_social");
      expect(workflows).toHaveLength(12);
    });

    it("should return correct workflows for tier5 (Ultimate)", () => {
      const workflows =
        WorkflowManagementService.getAllowedWorkflowsByTier("tier5");
      expect(workflows).toContain("lead_generation");
      expect(workflows).toContain("lead_scoring");
      expect(workflows).toHaveLength(14);
    });

    it("should return empty array for unknown tier", () => {
      const workflows =
        WorkflowManagementService.getAllowedWorkflowsByTier("tier99");
      expect(workflows).toEqual([]);
    });
  });

  describe("getTierMappings", () => {
    it("should return all tier mappings with display names", () => {
      const mappings = WorkflowManagementService.getTierMappings();
      expect(mappings).toHaveLength(5);
      expect(mappings[0]).toHaveProperty("tier", "tier1");
      expect(mappings[0]).toHaveProperty("displayName", "Starter");
      expect(mappings[0]).toHaveProperty("workflows");
    });
  });

  describe("getUserWorkflows", () => {
    it("should return workflows with permission flags for tier1 user", async () => {
      const mockWorkflows = [
        {
          id: "wf-1",
          userId: mockUserId,
          workflowType: "ai_agent",
          status: "active",
          config: { key: "value" },
          createdAt: new Date(),
          updatedAt: new Date(),
        },
        {
          id: "wf-2",
          userId: mockUserId,
          workflowType: "lead_generation",
          status: "inactive",
          config: {},
          createdAt: new Date(),
          updatedAt: new Date(),
        },
      ];

      (prisma.userWorkflow.findMany as jest.Mock).mockResolvedValue(
        mockWorkflows,
      );

      const result = await WorkflowManagementService.getUserWorkflows(
        mockUserId,
        "tier1",
      );

      expect(result).toHaveLength(2);
      expect(result[0].canActivate).toBe(true); // ai_agent is in tier1
      expect(result[0].requiresUpgrade).toBe(false);
      expect(result[1].canActivate).toBe(false); // lead_generation requires tier5
      expect(result[1].requiresUpgrade).toBe(true);
      expect(result[1].upgradeTier).toBe("Ultimate");
    });

    it("should return all workflows as accessible for tier5 user", async () => {
      const mockWorkflows = [
        {
          id: "wf-1",
          userId: mockUserId,
          workflowType: "ai_agent",
          status: "active",
          config: {},
          createdAt: new Date(),
          updatedAt: new Date(),
        },
        {
          id: "wf-2",
          userId: mockUserId,
          workflowType: "lead_scoring",
          status: "inactive",
          config: {},
          createdAt: new Date(),
          updatedAt: new Date(),
        },
      ];

      (prisma.userWorkflow.findMany as jest.Mock).mockResolvedValue(
        mockWorkflows,
      );

      const result = await WorkflowManagementService.getUserWorkflows(
        mockUserId,
        "tier5",
      );

      expect(result).toHaveLength(2);
      expect(result[0].canActivate).toBe(true);
      expect(result[1].canActivate).toBe(true);
      expect(result[0].requiresUpgrade).toBe(false);
      expect(result[1].requiresUpgrade).toBe(false);
    });
  });

  describe("getWorkflowById", () => {
    it("should return workflow when found", async () => {
      const mockWorkflow = {
        id: mockWorkflowId,
        userId: mockUserId,
        workflowType: "ai_agent",
        status: "active",
        config: { key: "value" },
        createdAt: new Date(),
        updatedAt: new Date(),
      };

      (prisma.userWorkflow.findFirst as jest.Mock).mockResolvedValue(
        mockWorkflow,
      );

      const result = await WorkflowManagementService.getWorkflowById(
        mockWorkflowId,
        mockUserId,
      );

      expect(result).toBeTruthy();
      expect(result?.id).toBe(mockWorkflowId);
      expect(prisma.userWorkflow.findFirst).toHaveBeenCalledWith({
        where: { id: mockWorkflowId, userId: mockUserId },
      });
    });

    it("should return null when workflow not found", async () => {
      (prisma.userWorkflow.findFirst as jest.Mock).mockResolvedValue(null);

      const result = await WorkflowManagementService.getWorkflowById(
        mockWorkflowId,
        mockUserId,
      );

      expect(result).toBeNull();
    });
  });

  describe("createWorkflow", () => {
    it.skip("should create workflow with inactive status", async () => {
      const createRequest = {
        workflowType: "ai_agent" as WorkflowType,
        n8nWorkflowId: "n8n-123",
        config: { apiKey: "test-key" },
      };

      const mockCreatedWorkflow = {
        id: mockWorkflowId,
        userId: mockUserId,
        workflowType: createRequest.workflowType,
        n8nWorkflowId: createRequest.n8nWorkflowId,
        status: "inactive",
        config: createRequest.config,
        lastRunAt: null,
        lastRunStatus: null,
        createdAt: new Date(),
        updatedAt: new Date(),
      };

      (prisma.userWorkflow.findFirst as jest.Mock).mockResolvedValue(null);
      (saveToBothDatabases as jest.Mock).mockResolvedValue(mockCreatedWorkflow);

      const result = await WorkflowManagementService.createWorkflow(
        mockUserId,
        createRequest,
      );

      expect(result.status).toBe("inactive");
      expect(result.workflowType).toBe("ai_agent");
      expect(saveToBothDatabases).toHaveBeenCalledWith(
        "UserWorkflow",
        "create",
        {
          data: {
            userId: mockUserId,
            workflowType: createRequest.workflowType,
            n8nWorkflowId: createRequest.n8nWorkflowId,
            status: "inactive",
            config: createRequest.config,
            lastRunStatus: null,
          },
        },
      );
    });

    it("should throw error if workflow already exists", async () => {
      const createRequest = {
        workflowType: "ai_agent" as WorkflowType,
        config: {},
      };

      (prisma.userWorkflow.findFirst as jest.Mock).mockResolvedValue({
        id: "existing-wf",
      });

      await expect(
        WorkflowManagementService.createWorkflow(mockUserId, createRequest),
      ).rejects.toThrow(
        "Workflow of type ai_agent already exists for this user",
      );
    });
  });

  describe("activateWorkflow", () => {
    it.skip("should activate workflow for allowed tier", async () => {
      const mockWorkflow = {
        id: mockWorkflowId,
        userId: mockUserId,
        workflowType: "ai_agent",
        status: "inactive",
        config: {},
        createdAt: new Date(),
        updatedAt: new Date(),
      };

      const mockUpdatedWorkflow = {
        ...mockWorkflow,
        status: "active",
      };

      (prisma.userWorkflow.findFirst as jest.Mock).mockResolvedValue(
        mockWorkflow,
      );
      (saveToBothDatabases as jest.Mock).mockResolvedValue(mockUpdatedWorkflow);

      const result = await WorkflowManagementService.activateWorkflow(
        mockWorkflowId,
        mockUserId,
        "tier1",
      );

      expect(result.success).toBe(true);
      expect(result.status).toBe("active");
      expect(saveToBothDatabases).toHaveBeenCalledWith(
        "UserWorkflow",
        "update",
        {
          where: { id: mockWorkflowId },
          data: {
            status: "active",
            updatedAt: expect.any(Date),
          },
        },
      );
    });

    it("should throw error for disallowed tier", async () => {
      const mockWorkflow = {
        id: mockWorkflowId,
        userId: mockUserId,
        workflowType: "lead_generation", // Requires tier5
        status: "inactive",
        config: {},
        createdAt: new Date(),
        updatedAt: new Date(),
      };

      (prisma.userWorkflow.findFirst as jest.Mock).mockResolvedValue(
        mockWorkflow,
      );

      await expect(
        WorkflowManagementService.activateWorkflow(
          mockWorkflowId,
          mockUserId,
          "tier1",
        ),
      ).rejects.toThrow("requires Ultimate tier or higher");
    });

    it("should throw error for unauthorized user", async () => {
      (prisma.userWorkflow.findFirst as jest.Mock).mockResolvedValue(null);

      await expect(
        WorkflowManagementService.activateWorkflow(
          mockWorkflowId,
          mockUserId,
          "tier1",
        ),
      ).rejects.toThrow("Workflow not found or unauthorized");
    });
  });

  describe("deactivateWorkflow", () => {
    it.skip("should deactivate workflow", async () => {
      const mockWorkflow = {
        id: mockWorkflowId,
        userId: mockUserId,
        workflowType: "ai_agent",
        status: "active",
        config: {},
        createdAt: new Date(),
        updatedAt: new Date(),
      };

      const mockUpdatedWorkflow = {
        ...mockWorkflow,
        status: "inactive",
      };

      (prisma.userWorkflow.findFirst as jest.Mock).mockResolvedValue(
        mockWorkflow,
      );
      (saveToBothDatabases as jest.Mock).mockResolvedValue(mockUpdatedWorkflow);

      const result = await WorkflowManagementService.deactivateWorkflow(
        mockWorkflowId,
        mockUserId,
      );

      expect(result.success).toBe(true);
      expect(result.status).toBe("inactive");
      expect(saveToBothDatabases).toHaveBeenCalledWith(
        "UserWorkflow",
        "update",
        {
          where: { id: mockWorkflowId },
          data: {
            status: "inactive",
            updatedAt: expect.any(Date),
          },
        },
      );
    });

    it("should throw error for unauthorized user", async () => {
      (prisma.userWorkflow.findFirst as jest.Mock).mockResolvedValue(null);

      await expect(
        WorkflowManagementService.deactivateWorkflow(
          mockWorkflowId,
          mockUserId,
        ),
      ).rejects.toThrow("Workflow not found or unauthorized");
    });
  });

  describe("updateWorkflowConfig", () => {
    it.skip("should update workflow configuration", async () => {
      const mockWorkflow = {
        id: mockWorkflowId,
        userId: mockUserId,
        workflowType: "ai_agent",
        status: "active",
        config: { oldKey: "oldValue" },
        createdAt: new Date(),
        updatedAt: new Date(),
      };

      const newConfig = { newKey: "newValue", apiKey: "updated-key" };

      const mockUpdatedWorkflow = {
        ...mockWorkflow,
        config: newConfig,
      };

      (prisma.userWorkflow.findFirst as jest.Mock).mockResolvedValue(
        mockWorkflow,
      );
      (saveToBothDatabases as jest.Mock).mockResolvedValue(mockUpdatedWorkflow);

      const result = await WorkflowManagementService.updateWorkflowConfig(
        mockWorkflowId,
        mockUserId,
        newConfig,
      );

      expect(result.config).toEqual(newConfig);
      expect(saveToBothDatabases).toHaveBeenCalledWith(
        "UserWorkflow",
        "update",
        {
          where: { id: mockWorkflowId },
          data: {
            config: newConfig,
            updatedAt: expect.any(Date),
          },
        },
      );
    });

    it("should throw error for unauthorized user", async () => {
      (prisma.userWorkflow.findFirst as jest.Mock).mockResolvedValue(null);

      await expect(
        WorkflowManagementService.updateWorkflowConfig(
          mockWorkflowId,
          mockUserId,
          {},
        ),
      ).rejects.toThrow("Workflow not found or unauthorized");
    });
  });

  describe("deleteWorkflow", () => {
    it.skip("should delete workflow", async () => {
      const mockWorkflow = {
        id: mockWorkflowId,
        userId: mockUserId,
        workflowType: "ai_agent",
        status: "inactive",
        config: {},
        createdAt: new Date(),
        updatedAt: new Date(),
      };

      (prisma.userWorkflow.findFirst as jest.Mock).mockResolvedValue(
        mockWorkflow,
      );
      (saveToBothDatabases as jest.Mock).mockResolvedValue({});

      await WorkflowManagementService.deleteWorkflow(
        mockWorkflowId,
        mockUserId,
      );

      expect(saveToBothDatabases).toHaveBeenCalledWith(
        "UserWorkflow",
        "delete",
        {
          where: { id: mockWorkflowId },
        },
      );
    });

    it("should throw error for unauthorized user", async () => {
      (prisma.userWorkflow.findFirst as jest.Mock).mockResolvedValue(null);

      await expect(
        WorkflowManagementService.deleteWorkflow(mockWorkflowId, mockUserId),
      ).rejects.toThrow("Workflow not found or unauthorized");
    });
  });

  describe("updateWorkflowRunStatus", () => {
    it.skip("should update workflow run status", async () => {
      const runDate = new Date("2025-01-15T10:00:00Z");
      const mockUpdatedWorkflow = {
        id: mockWorkflowId,
        lastRunAt: runDate,
        lastRunStatus: "success",
      };

      (saveToBothDatabases as jest.Mock).mockResolvedValue(mockUpdatedWorkflow);

      await WorkflowManagementService.updateWorkflowRunStatus(
        mockWorkflowId,
        "success",
        runDate,
      );

      expect(saveToBothDatabases).toHaveBeenCalledWith(
        "UserWorkflow",
        "update",
        {
          where: { id: mockWorkflowId },
          data: {
            lastRunAt: runDate,
            lastRunStatus: "success",
            updatedAt: expect.any(Date),
          },
        },
      );
    });
  });

  describe("getWorkflowStats", () => {
    it("should return workflow statistics", async () => {
      const mockWorkflows = [
        { status: "active" },
        { status: "active" },
        { status: "inactive" },
        { status: "error" },
      ];

      (prisma.userWorkflow.findMany as jest.Mock).mockResolvedValue(
        mockWorkflows,
      );

      const stats =
        await WorkflowManagementService.getWorkflowStats(mockUserId);

      expect(stats).toEqual({
        total: 4,
        active: 2,
        inactive: 1,
        error: 1,
      });
    });

    it("should return zero stats for user with no workflows", async () => {
      (prisma.userWorkflow.findMany as jest.Mock).mockResolvedValue([]);

      const stats =
        await WorkflowManagementService.getWorkflowStats(mockUserId);

      expect(stats).toEqual({
        total: 0,
        active: 0,
        inactive: 0,
        error: 0,
      });
    });
  });

  describe("Tier-based access control edge cases", () => {
    it.skip("should allow tier2 user to activate tier1 workflows", async () => {
      const mockWorkflow = {
        id: mockWorkflowId,
        userId: mockUserId,
        workflowType: "ai_agent", // tier1 workflow
        status: "inactive",
        config: {},
        createdAt: new Date(),
        updatedAt: new Date(),
      };

      const mockUpdatedWorkflow = {
        ...mockWorkflow,
        status: "active",
      };

      (prisma.userWorkflow.findFirst as jest.Mock).mockResolvedValue(
        mockWorkflow,
      );
      (saveToBothDatabases as jest.Mock).mockResolvedValue(mockUpdatedWorkflow);

      const result = await WorkflowManagementService.activateWorkflow(
        mockWorkflowId,
        mockUserId,
        "tier2",
      );

      expect(result.success).toBe(true);
    });

    it("should prevent tier2 user from activating tier3 workflows", async () => {
      const mockWorkflow = {
        id: mockWorkflowId,
        userId: mockUserId,
        workflowType: "voice_agent", // tier3 workflow
        status: "inactive",
        config: {},
        createdAt: new Date(),
        updatedAt: new Date(),
      };

      (prisma.userWorkflow.findFirst as jest.Mock).mockResolvedValue(
        mockWorkflow,
      );

      await expect(
        WorkflowManagementService.activateWorkflow(
          mockWorkflowId,
          mockUserId,
          "tier2",
        ),
      ).rejects.toThrow("requires Business tier or higher");
    });
  });
});
