import { Dialog, DialogRef } from '@angular/cdk/dialog';
import { TestBed } from '@angular/core/testing';
import {
  injectDialog,
  injectFeedbackDialog,
  injectMessageDialog,
  injectTextInputDialog,
  injectNumberInputDialog,
  injectConfirmationDialog,
  injectFeedbackErrorDialog,
} from './injects';
import { MessageDialogComponent } from './message-dialog/message-dialog.component';
import { DialogComponent } from './dialog.component';
import { DIALOG_CONTENT, DIALOG_TITLE, DISPLAY_DIALOG_CLOSE } from './tokens';
import { Component } from '@angular/core';
import { DialogContentDirective } from './dialog-content.directive';
import { TextInputDialogComponent } from './text-input-dialog/text-input-dialog.component';
import { FeedbackDialogComponent } from './feedback-dialog/feedback-dialog.component';
import { NumberInputDialogComponent } from './number-input-dialog/number-input-dialog.component';
import { ConfirmationDialogComponent } from './confirmation-dialog/confirmation-dialog.component';
import { FeedbackErrorDialogComponent } from './feedback-error-dialog/feedback-error-dialog.component';

// Test component extending DialogContentDirective for testing
@Component({ template: '' })
class TestDialogContentComponent extends DialogContentDirective<
  unknown,
  unknown
> {}

describe('Dialog Injects', () => {
  // Mock DialogRef
  const mockDialogRef: DialogRef<unknown> = {
    close: jest.fn(),
    // Add other properties as needed for the DialogRef interface
  } as unknown as DialogRef<unknown>;

  // Mock dialog.open function
  const mockDialogOpen = jest.fn().mockReturnValue(mockDialogRef);

  // Mock Dialog service
  const mockDialog = {
    open: mockDialogOpen,
  };

  // Setup test module before each test
  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [{ provide: Dialog, useValue: mockDialog }],
    });

    // Reset mocks between tests
    jest.clearAllMocks();
  });

  describe('injectDialog', () => {
    it('should return a function that opens a dialog', () => {
      // Arrange
      const componentType = TestDialogContentComponent;
      const title = 'Test Title';

      // Act
      const openDialog = TestBed.runInInjectionContext(() =>
        injectDialog(componentType, { title }),
      );

      // Assert
      expect(typeof openDialog).toBe('function');
    });

    it('should call dialog.open with correct parameters when executed', () => {
      // Arrange
      const componentType = TestDialogContentComponent;
      const title = 'Test Title';
      const data = { message: 'Test Message' };

      // Act
      const openDialog = TestBed.runInInjectionContext(() =>
        injectDialog(componentType, { title }),
      );
      const result = openDialog({ data });

      // Assert
      expect(mockDialogOpen).toHaveBeenCalledWith(
        DialogComponent,
        expect.objectContaining({
          data,
          minWidth: '30rem',
          hasBackdrop: true,
          disableClose: true,
        }),
      );
      expect(result).toBe(mockDialogRef);
    });

    it('should use provided title override if specified', () => {
      // Arrange
      const componentType = TestDialogContentComponent;
      const defaultTitle = 'Default Title';
      const overrideTitle = 'Override Title';
      const data = { message: 'Test Message' };

      // Act
      const openDialog = TestBed.runInInjectionContext(() =>
        injectDialog(componentType, { title: defaultTitle }),
      );
      openDialog({ title: overrideTitle, data });

      // Assert
      const callOptions = mockDialogOpen.mock.calls[0][1];
      const injector = callOptions.injector;
      expect(injector.get(DIALOG_TITLE)).toBe(overrideTitle);
    });

    it('should use default title if no override is specified', () => {
      // Arrange
      const componentType = TestDialogContentComponent;
      const defaultTitle = 'Default Title';
      const data = { message: 'Test Message' };

      // Act
      const openDialog = TestBed.runInInjectionContext(() =>
        injectDialog(componentType, { title: defaultTitle }),
      );
      openDialog({ data });

      // Assert
      const callOptions = mockDialogOpen.mock.calls[0][1];
      const injector = callOptions.injector;
      expect(injector.get(DIALOG_TITLE)).toBe(defaultTitle);
    });

    it('should set the correct component in the injector', () => {
      // Arrange
      const componentType = TestDialogContentComponent;
      const data = { message: 'Test Message' };

      // Act
      const openDialog = TestBed.runInInjectionContext(() =>
        injectDialog(componentType),
      );
      openDialog({ data });

      // Assert
      const callOptions = mockDialogOpen.mock.calls[0][1];
      const injector = callOptions.injector;
      expect(injector.get(DIALOG_CONTENT)).toBe(componentType);
    });

    it('should set displayClose to false by default', () => {
      // Arrange
      const componentType = TestDialogContentComponent;
      const data = { message: 'Test Message' };

      // Act
      const openDialog = TestBed.runInInjectionContext(() =>
        injectDialog(componentType),
      );
      openDialog({ data });

      // Assert
      const callOptions = mockDialogOpen.mock.calls[0][1];
      const injector = callOptions.injector;
      expect(injector.get(DISPLAY_DIALOG_CLOSE)).toBe(false);
    });

    it('should use provided displayClose option from inject options', () => {
      // Arrange
      const componentType = TestDialogContentComponent;
      const data = { message: 'Test Message' };

      // Act
      const openDialog = TestBed.runInInjectionContext(() =>
        injectDialog(componentType, { displayClose: true }),
      );
      openDialog({ data });

      // Assert
      const callOptions = mockDialogOpen.mock.calls[0][1];
      const injector = callOptions.injector;
      expect(injector.get(DISPLAY_DIALOG_CLOSE)).toBe(true);
    });

    it('should use provided displayClose option from open options and override inject options', () => {
      // Arrange
      const componentType = TestDialogContentComponent;
      const data = { message: 'Test Message' };

      // Act
      const openDialog = TestBed.runInInjectionContext(() =>
        injectDialog(componentType, { displayClose: true }),
      );
      openDialog({ data, displayClose: false });

      // Assert
      const callOptions = mockDialogOpen.mock.calls[0][1];
      const injector = callOptions.injector;
      expect(injector.get(DISPLAY_DIALOG_CLOSE)).toBe(false);
    });
  });

  describe('injectMessageDialog', () => {
    it('should create a dialog injector for MessageDialogComponent', () => {
      // Act
      const openMessageDialog = TestBed.runInInjectionContext(() =>
        injectMessageDialog(),
      );
      openMessageDialog({ data: { message: 'Test message' } });

      // Assert
      const callOptions = mockDialogOpen.mock.calls[0][1];
      const injector = callOptions.injector;
      expect(injector.get(DIALOG_CONTENT)).toBe(MessageDialogComponent);
    });
  });

  describe('injectTextInputDialog', () => {
    it('should create a dialog injector for TextInputDialogComponent', () => {
      // Act
      const openTextInputDialog = TestBed.runInInjectionContext(() =>
        injectTextInputDialog(),
      );
      openTextInputDialog({
        data: {
          message: 'Test message',
          inputLabel: 'Enter value',
          inputDefaultValue: '',
        },
      });

      // Assert
      const callOptions = mockDialogOpen.mock.calls[0][1];
      const injector = callOptions.injector;
      expect(injector.get(DIALOG_CONTENT)).toBe(TextInputDialogComponent);
    });
  });

  describe('injectNumberInputDialog', () => {
    it('should create a dialog injector for NumberInputDialogComponent', () => {
      // Act
      const openNumberInputDialog = TestBed.runInInjectionContext(() =>
        injectNumberInputDialog(),
      );
      openNumberInputDialog({
        data: {
          message: 'Test message',
          inputLabel: 'Enter value',
          inputDefaultValue: 0,
        },
      });

      // Assert
      const callOptions = mockDialogOpen.mock.calls[0][1];
      const injector = callOptions.injector;
      expect(injector.get(DIALOG_CONTENT)).toBe(NumberInputDialogComponent);
    });
  });

  describe('injectFeedbackDialog', () => {
    it('should create a dialog injector for FeedbackDialogComponent', () => {
      // Act
      const openFeedbackDialog = TestBed.runInInjectionContext(() =>
        injectFeedbackDialog(),
      );
      openFeedbackDialog({
        data: {
          message: 'Test message',
        },
      });

      // Assert
      const callOptions = mockDialogOpen.mock.calls[0][1];
      const injector = callOptions.injector;
      expect(injector.get(DIALOG_CONTENT)).toBe(FeedbackDialogComponent);
    });
  });

  describe('injectConfirmationDialog', () => {
    it('should create a dialog injector for ConfirmationDialogComponent', () => {
      // Act
      const openConfirmationDialog = TestBed.runInInjectionContext(() =>
        injectConfirmationDialog(),
      );
      openConfirmationDialog({
        data: {
          message: 'Test message',
        },
      });

      // Assert
      const callOptions = mockDialogOpen.mock.calls[0][1];
      const injector = callOptions.injector;
      expect(injector.get(DIALOG_CONTENT)).toBe(ConfirmationDialogComponent);
    });
  });

  describe('injectFeedbackErrorDialog', () => {
    it('should create a dialog injector for FeedbackErrorDialogComponent', () => {
      // Act
      const openFeedbackErrorDialog = TestBed.runInInjectionContext(() =>
        injectFeedbackErrorDialog(),
      );
      openFeedbackErrorDialog({
        data: {
          errorMessage: 'Test error message',
        },
      });

      // Assert
      const callOptions = mockDialogOpen.mock.calls[0][1];
      const injector = callOptions.injector;
      expect(injector.get(DIALOG_CONTENT)).toBe(FeedbackErrorDialogComponent);
    });
  });
});
