/* eslint-disable @typescript-eslint/no-explicit-any */
import { Dialog } from '@angular/cdk/dialog';
import { ComponentType } from '@angular/cdk/portal';
import { inject, Injector } from '@angular/core';
import { DialogContentDirective } from './dialog-content.directive';
import { DialogComponent } from './dialog.component';
import {
  DIALOG_CLASS_LIST,
  DIALOG_CONTENT,
  DIALOG_TITLE,
  DISPLAY_DIALOG_CLOSE,
} from './tokens';
import { MessageDialogComponent } from './message-dialog/message-dialog.component';
import { TextInputDialogComponent } from './text-input-dialog/text-input-dialog.component';
import { NumberInputDialogComponent } from './number-input-dialog/number-input-dialog.component';
import {
  FeedbackDialogComponent,
  FeedbackDialogData,
} from './feedback-dialog/feedback-dialog.component';
import {
  ConfirmationDialogComponent,
  ConfirmationDialogData,
} from './confirmation-dialog/confirmation-dialog.component';
import {
  FeedbackErrorDialogComponent,
  FeedbackErrorDialogData,
} from './feedback-error-dialog/feedback-error-dialog.component';

export interface InjectDialogOptions {
  /** Optional title override for the dialog */
  title?: string;

  /** Optional flag to control whether the close button is displayed */
  displayClose?: boolean;

  /** Optional additional CSS classes to apply to the dialog */
  classList?: string[];

  /** Optional width for the dialog */
  width?: string;

  /** Optional height for the dialog */
  height?: string;

  /** Optional minWidth for the dialog */
  minWidth?: string;

  /** Optional maxWidth for the dialog */
  maxWidth?: string;

  /** Optional minHeight for the dialog */
  minHeight?: string;

  /** Optional maxHeight for the dialog */
  maxHeight?: string;

  /** Optional hasBackdrop for the dialog */
  hasBackdrop?: boolean;

  /** Optional disableClose for the dialog */
  disableClose?: boolean;
}

/**
 * Options for opening a dialog using injectDialog function
 * @template D The type of data passed to the dialog
 */
export interface OpenDialogOptions<D> extends InjectDialogOptions {
  /** Data to pass to the dialog component */
  data: D;
}

/**
 * Creates a function to open a dialog with a specific component
 * Uses Angular's DI system for dialog configuration
 *
 * @template C The type of component to display in the dialog
 * @param componentType The component class to instantiate in the dialog
 * @param title Default title for the dialog
 * @returns A function that opens the dialog with provided options
 */
export function injectDialog<C extends DialogContentDirective<any, any>>(
  componentType: ComponentType<C>,
  injectOptions?: InjectDialogOptions,
) {
  type D = C extends DialogContentDirective<infer D, any> ? D : never;
  type R = C extends DialogContentDirective<any, infer R> ? R : never;

  const cdkDialog = inject(Dialog);
  const injector = inject(Injector);

  return (openOptions?: OpenDialogOptions<D>) => {
    const dialogInjector = Injector.create({
      parent: injector,
      providers: [
        {
          provide: DIALOG_CONTENT,
          useValue: componentType,
        },
        {
          provide: DIALOG_TITLE,
          useValue: openOptions?.title ?? injectOptions?.title,
        },
        {
          provide: DISPLAY_DIALOG_CLOSE,
          useValue:
            openOptions?.displayClose ?? injectOptions?.displayClose ?? false, // #5275 Die Anzeige des X Buttons im Dialog ist defaultmäßig auf false -> Bei true müssten ALLE eingesetzten Dialoge überprüft werden, da bei close ein undefined emitted wird und mit diesem Wert evtl. bereits Logik verbunden ist
        },
        {
          provide: DIALOG_CLASS_LIST,
          useValue: openOptions?.classList ?? injectOptions?.classList ?? [],
        },
      ],
    });

    const dialogRef = cdkDialog.open<R, D, DialogComponent<D, R, C>>(
      DialogComponent<D, R, C>,
      {
        data: openOptions?.data,
        injector: dialogInjector,
        width: openOptions?.width ?? injectOptions?.width,
        height: openOptions?.height ?? injectOptions?.height,
        minWidth: openOptions?.minWidth ?? injectOptions?.minWidth ?? '30rem',
        maxWidth: openOptions?.maxWidth ?? injectOptions?.maxWidth,
        minHeight: openOptions?.minHeight ?? injectOptions?.minHeight,
        maxHeight: openOptions?.maxHeight ?? injectOptions?.maxHeight,
        hasBackdrop:
          openOptions?.hasBackdrop ?? injectOptions?.hasBackdrop ?? true,
        disableClose:
          openOptions?.disableClose ?? injectOptions?.disableClose ?? true,
      },
    );

    return dialogRef;
  };
}

/**
 * Convenience function that returns a pre-configured ConfirmationDialog injector
 * @param options Optional configuration for the dialog
 * @returns A function to open a confirmation dialog
 */
export const injectConfirmationDialog = (
  options?: OpenDialogOptions<ConfirmationDialogData>,
) => injectDialog(ConfirmationDialogComponent, options);

/**
 * Convenience function that returns a pre-configured MessageDialog injector
 * @returns A function to open a message dialog
 */
export const injectMessageDialog = () => injectDialog(MessageDialogComponent);

/**
 * Convenience function that returns a pre-configured TextInputDialog injector
 * @returns A function to open a text input dialog
 */
export const injectTextInputDialog = () =>
  injectDialog(TextInputDialogComponent);

/**
 * Convenience function that returns a pre-configured NumberInputDialog injector
 * @returns A function to open a number input dialog
 */
export const injectNumberInputDialog = () =>
  injectDialog(NumberInputDialogComponent);

/**
 * Convenience function that returns a pre-configured FeedbackDialog injector
 * @returns A function to open a feedback dialog
 */
export const injectFeedbackDialog = (
  options?: OpenDialogOptions<FeedbackDialogData>,
) =>
  injectDialog(FeedbackDialogComponent, {
    disableClose: false,
    minWidth: '20rem',
    classList: ['gap-0'],
    ...options,
  });

/**
 * Convenience function that returns a pre-configured FeedbackErrorDialog injector
 * @returns A function to open a feedback error dialog
 */
export const injectFeedbackErrorDialog = (
  options?: OpenDialogOptions<FeedbackErrorDialogData>,
) =>
  injectDialog(FeedbackErrorDialogComponent, {
    disableClose: false,
    minWidth: '20rem',
    classList: ['gap-0'],
    ...options,
  });
