import { beforeEach, describe, expect, it, vi } from "vitest";
import { ImportDataManagerImpl } from "./import-data-manager-impl";
import {
  PrimariaInteractionService,
  ConfirmationOptions,
  ConfirmComponentUI,
  ConfirmationResult,
} from "../interaction-service/interaction-service";

// Mock del PrimariaInteractionService
class MockInteractionService extends PrimariaInteractionService {
  private mockConfirmResult: { confirmed: boolean; result?: unknown } = { confirmed: true };

  // Mètode per configurar el resultat del mock
  setMockConfirmResult(result: { confirmed: boolean; result?: unknown }) {
    this.mockConfirmResult = result;
  }

  async confirm<TData = undefined, TResult = undefined>(
    _data: TData | undefined,
    _componentUI: ConfirmComponentUI<TData, TResult>,
    _options?: ConfirmationOptions | undefined,
  ): Promise<ConfirmationResult<TResult | undefined>> {
    return Promise.resolve(this.mockConfirmResult as ConfirmationResult<TResult | undefined>);
  }

  async confirmMessage(_message: string, _options?: ConfirmationOptions | undefined): Promise<ConfirmationResult> {
    return Promise.resolve({ confirmed: this.mockConfirmResult.confirmed, result: undefined });
  }
}

describe("ImportDataManagerImpl implements PrimariaImportDataManager", () => {
  let importDataManager: ImportDataManagerImpl;
  let mockInteractionService: MockInteractionService;

  beforeEach(() => {
    mockInteractionService = new MockInteractionService();
    importDataManager = new ImportDataManagerImpl(mockInteractionService);
  });

  describe("getCurrentImporterId", () => {
    it("should return empty string initially", () => {
      expect(importDataManager.getCurrentImporterId()).toBe("");
    });

    it("should return the current importer ID after calling import", async () => {
      mockInteractionService.setMockConfirmResult({ confirmed: true });

      const promise = importDataManager.import("test-importer-123");
      expect(importDataManager.getCurrentImporterId()).toBe("test-importer-123");

      await promise;
      expect(importDataManager.getCurrentImporterId()).toBe("");
    });
  });

  describe("selectItems", () => {
    it("should store selected items correctly", () => {
      const payload = {
        pluginId: "test-plugin",
        data: [{ id: "1", name: "Test Item" }],
        text: { raw: "Test Item", html: "<p>Test Item</p>" },
      };

      importDataManager.selectItems(payload);

      // Test via getCurrentImporterId and import method to verify internal state
      expect(() => importDataManager.selectItems(payload)).not.toThrow();
    });

    it("should handle multiple plugin selections", () => {
      const payload1 = {
        pluginId: "plugin-1",
        data: [{ id: "1", name: "Item 1" }],
        text: { raw: "Item 1", html: "<p>Item 1</p>" },
      };

      const payload2 = {
        pluginId: "plugin-2",
        data: [{ id: "2", name: "Item 2" }],
        text: { raw: "Item 2", html: "<p>Item 2</p>" },
      };

      expect(() => {
        importDataManager.selectItems(payload1);
        importDataManager.selectItems(payload2);
      }).not.toThrow();
    });

    it("should overwrite previous selections from same plugin", () => {
      const payload1 = {
        pluginId: "test-plugin",
        data: [{ id: "1", name: "First Item" }],
        text: { raw: "First Item", html: "<p>First Item</p>" },
      };

      const payload2 = {
        pluginId: "test-plugin",
        data: [{ id: "2", name: "Second Item" }],
        text: { raw: "Second Item", html: "<p>Second Item</p>" },
      };

      expect(() => {
        importDataManager.selectItems(payload1);
        importDataManager.selectItems(payload2);
      }).not.toThrow();
    });
  });

  describe("import", () => {
    it("should return accepted result with concatenated text when confirmed", async () => {
      mockInteractionService.setMockConfirmResult({ confirmed: true });

      // Mock the import to allow setting up items during the import process
      const originalConfirm = mockInteractionService.confirm.bind(mockInteractionService);
      vi.spyOn(mockInteractionService, "confirm").mockImplementation(async (...args) => {
        // Setup some selected items during the import process (simulating user interaction)
        importDataManager.selectItems({
          pluginId: "plugin-1",
          data: [{ id: "1", name: "Item 1" }],
          text: { raw: "Item 1", html: "<p>Item 1</p>" },
        });

        importDataManager.selectItems({
          pluginId: "plugin-2",
          data: [{ id: "2", name: "Item 2" }],
          text: { raw: "Item 2", html: "<p>Item 2</p>" },
        });

        return originalConfirm(...args);
      });

      const result = await importDataManager.import("test-importer");

      expect(result.accepted).toBe(true);
      expect(result.data).toEqual({
        "plugin-1": [{ id: "1", name: "Item 1" }],
        "plugin-2": [{ id: "2", name: "Item 2" }],
      });
      expect(result.text.raw).toBe("Item 1\n\nItem 2");
      expect(result.text.html).toBe("<p>Item 1</p><br><br><p>Item 2</p>");
    });

    it("should return cancelled result with empty data when not confirmed", async () => {
      mockInteractionService.setMockConfirmResult({ confirmed: false });

      // Setup some selected items
      importDataManager.selectItems({
        pluginId: "plugin-1",
        data: [{ id: "1", name: "Item 1" }],
        text: { raw: "Item 1", html: "<p>Item 1</p>" },
      });

      const result = await importDataManager.import("test-importer");

      expect(result.accepted).toBe(false);
      expect(result.data).toEqual({});
      expect(result.text.raw).toBe("");
      expect(result.text.html).toBe("");
    });

    it("should clear state after successful import", async () => {
      mockInteractionService.setMockConfirmResult({ confirmed: true });

      // Setup selected items
      importDataManager.selectItems({
        pluginId: "test-plugin",
        data: [{ id: "1", name: "Test Item" }],
        text: { raw: "Test Item", html: "<p>Test Item</p>" },
      });

      await importDataManager.import("test-importer");

      // Verify state is cleared
      expect(importDataManager.getCurrentImporterId()).toBe("");

      // Verify that subsequent import returns empty data
      const result = await importDataManager.import("another-importer");
      expect(result.data).toEqual({});
      expect(result.text.raw).toBe("");
      expect(result.text.html).toBe("");
    });

    it("should clear state after cancelled import", async () => {
      mockInteractionService.setMockConfirmResult({ confirmed: false });

      // Setup selected items
      importDataManager.selectItems({
        pluginId: "test-plugin",
        data: [{ id: "1", name: "Test Item" }],
        text: { raw: "Test Item", html: "<p>Test Item</p>" },
      });

      await importDataManager.import("test-importer");

      // Verify state is cleared
      expect(importDataManager.getCurrentImporterId()).toBe("");
    });

    it("should handle empty selections", async () => {
      mockInteractionService.setMockConfirmResult({ confirmed: true });

      const result = await importDataManager.import("test-importer");

      expect(result.accepted).toBe(true);
      expect(result.data).toEqual({});
      expect(result.text.raw).toBe("");
      expect(result.text.html).toBe("");
    });

    it("should handle error and clear state", async () => {
      mockInteractionService.setMockConfirmResult({ confirmed: true });

      // Mock the interaction service to throw an error
      vi.spyOn(mockInteractionService, "confirm").mockRejectedValueOnce(new Error("Test error"));

      // Setup selected items
      importDataManager.selectItems({
        pluginId: "test-plugin",
        data: [{ id: "1", name: "Test Item" }],
        text: { raw: "Test Item", html: "<p>Test Item</p>" },
      });

      await expect(importDataManager.import("test-importer")).rejects.toThrow("Test error");

      // Verify state is cleared even after error
      expect(importDataManager.getCurrentImporterId()).toBe("");
    });

    it("should handle text concatenation correctly with multiple plugins", async () => {
      mockInteractionService.setMockConfirmResult({ confirmed: true });

      // Mock the import to allow setting up items during the import process
      const originalConfirm = mockInteractionService.confirm.bind(mockInteractionService);
      vi.spyOn(mockInteractionService, "confirm").mockImplementation(async (...args) => {
        importDataManager.selectItems({
          pluginId: "diagnostics",
          data: [{ code: "E11.9", name: "Diabetes" }],
          text: { raw: "E11.9 - Diabetes", html: "<p><strong>E11.9</strong> - Diabetes</p>" },
        });

        importDataManager.selectItems({
          pluginId: "allergies",
          data: [{ name: "Penicillin", severity: "High" }],
          text: { raw: "Penicillin - High severity", html: "<p><strong>Penicillin</strong> - High severity</p>" },
        });

        return originalConfirm(...args);
      });

      const result = await importDataManager.import("test-importer");

      expect(result.text.raw).toBe("E11.9 - Diabetes\n\nPenicillin - High severity");
      expect(result.text.html).toBe(
        "<p><strong>E11.9</strong> - Diabetes</p><br><br><p><strong>Penicillin</strong> - High severity</p>",
      );
    });

    it("should handle empty text values in concatenation", async () => {
      mockInteractionService.setMockConfirmResult({ confirmed: true });

      // Mock the import to allow setting up items during the import process
      const originalConfirm = mockInteractionService.confirm.bind(mockInteractionService);
      vi.spyOn(mockInteractionService, "confirm").mockImplementation(async (...args) => {
        importDataManager.selectItems({
          pluginId: "plugin-1",
          data: [{ id: "1" }],
          text: { raw: "", html: "" },
        });

        importDataManager.selectItems({
          pluginId: "plugin-2",
          data: [{ id: "2" }],
          text: { raw: "Item 2", html: "<p>Item 2</p>" },
        });

        return originalConfirm(...args);
      });

      const result = await importDataManager.import("test-importer");

      expect(result.text.raw).toBe("Item 2");
      expect(result.text.html).toBe("<p>Item 2</p>");
    });
  });
});
