import {
  ChangeDetectionStrategy,
  Component,
  computed,
  signal,
} from '@angular/core';

import { Printer } from '../models';
import { ButtonComponent } from '@isa/ui/buttons';
import { DialogContentDirective } from '@isa/ui/dialog';
import { ListboxDirective, ListboxItemDirective } from '@isa/ui/input-controls';

/**
 * Input data for the printer dialog component
 */
export interface PrinterDialogData {
  /** Array of available printers to display */
  printers: Printer[];
  /** Optional error to display in the dialog */
  error?: unknown;
  /** Function to call when user selects a printer and clicks print */
  print: (printer: Printer) => PromiseLike<unknown>;
}

/**
 * Result returned when the printer dialog closes
 */
export interface PrinterDialogResult {
  /** Selected printer or undefined if user cancelled */
  printer: Printer | undefined;
}

/**
 * Dialog component for printer selection and print operations
 * Displays a list of printers, allows selection, and handles print errors
 */
@Component({
  selector: 'common-print-dialog',
  templateUrl: './print-dialog.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [ButtonComponent, ListboxDirective, ListboxItemDirective],
})
export class PrintDialogComponent extends DialogContentDirective<
  PrinterDialogData,
  PrinterDialogResult
> {
  /** Signal for the currently selected printer */
  printer = signal<Printer | undefined>(
    this.data.printers.find((p) => p.selected),
  );

  /** Computed array of selected printers for the listbox component */
  selected = computed(() => {
    const printer = this.printer();
    if (printer) {
      return [printer];
    }
    return [];
  });
  /** Signal indicating if a print operation is in progress */
  printing = signal(false);

  /** Signal for displaying error information */
  error = signal<unknown>(this.data.error);

  /** Computed property determining if the print button should be enabled */
  canPrint = computed(() => {
    return this.printer() !== undefined && !this.printing();
  });

  /**
   * Comparison function for printer objects
   * @param a First printer to compare
   * @param b Second printer to compare
   * @returns True if the printers have the same key
   */
  compareWith(a: Printer, b: Printer) {
    return a.key === b.key;
  }

  /**
   * Handles printer selection in the list
   * @param printer The printer that was selected
   */
  select(printer: Printer) {
    this.error.set(undefined);
    this.printer.set(printer);
  }
  /**
   * Executes the print operation with the selected printer
   * Sets error state if the print operation fails
   * Closes the dialog with the selected printer if successful
   */
  async print() {
    this.error.set(undefined);
    const printer = this.printer();
    if (!this.canPrint() || !printer) {
      return;
    }

    this.printing.set(true);

    try {
      await this.data.print(printer);
    } catch (error) {
      this.error.set(error);
      this.printing.set(false);
      return;
    }
    this.close({ printer });
  }

  /**
   * Formats error objects for display in the UI
   * @param error The error object to format
   * @returns A user-friendly error message string
   */
  formatError(error: unknown): string {
    if (error instanceof Error) {
      return error.message;
    } else if (typeof error === 'string') {
      return error;
    } else {
      return 'Unbekannter Fehler';
    }
  }
}
