import {
  ChangeDetectionStrategy,
  Component,
  effect,
  ElementRef,
  inject,
  input,
  NgZone,
  OnDestroy,
  output,
  untracked,
  ViewEncapsulation,
} from '@angular/core';
import {
  BarcodeCapture,
  BarcodeCaptureSession,
  BarcodeCaptureSettings,
  Symbology,
} from 'scandit-web-datacapture-barcode';
import {
  Camera,
  DataCaptureContext,
  DataCaptureView,
  FrameSourceState,
} from 'scandit-web-datacapture-core';
import { IconButtonComponent } from '@isa/ui/buttons';
import { provideIcons } from '@ng-icons/core';
import { isaActionClose } from '@isa/icons';

/**
 * Component that provides the UI and functionality for barcode scanning.
 *
 * This component integrates with the Scandit SDK to render a camera view and process
 * barcode scanning. It is used internally by the ScannerService.
 */
@Component({
  selector: 'shared-scanner',
  styleUrls: ['./scanner.component.scss'],
  templateUrl: './scanner.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
  standalone: true,
  imports: [IconButtonComponent],
  providers: [provideIcons({ isaActionClose })],
  encapsulation: ViewEncapsulation.None, // Use global styles
  host: {
    class: 'shared-scanner',
  },
})
export class ScannerComponent implements OnDestroy {
  #zone = inject(NgZone);

  #elementRef = inject(ElementRef, { self: true });

  #dataCaptureView = new DataCaptureView();

  #dataCaptureContext: DataCaptureContext | null = null;

  #barcodeCapture: BarcodeCapture | null = null;

  #camera: Camera = Camera.default;

  /**
   * Input array of barcode symbologies to recognize.
   * Determines which types of barcodes the scanner will detect.
   */
  symbologies = input<Symbology[]>([]);

  /**
   * Effect that handles initializing the scanner when symbologies change.
   * Creates the data capture context and starts barcode capture with the specified symbologies.
   */
  symbologiesEffect = effect(() => {
    const symbologies = this.symbologies();
    untracked(async () => {
      await this.#createDataCaptureContext();
      await this.#startBarcodeCapture(symbologies);
    });
  });

  /**
   * Output event emitted when a barcode is successfully scanned.
   * Emits the barcode data as a string.
   */
  scan = output<string>();

  /**
   * Initializes the scanner component.
   * Sets up the data capture view and shows a progress bar while loading.
   * Configures the view for full-screen scanning experience.
   */ constructor() {
    // Configure the view to take maximum space
    this.#dataCaptureView.connectToElement(this.#elementRef.nativeElement);
    this.#dataCaptureView.showProgressBar();
  }

  /**
   * Creates and initializes the Scandit DataCaptureContext.
   * This is required before barcode scanning can begin.
   *
   * @returns Promise that resolves when the context is created
   */
  async #createDataCaptureContext(): Promise<void> {
    if (this.#dataCaptureContext) {
      return;
    }

    this.#dataCaptureContext = await DataCaptureContext.create();
    this.#dataCaptureView.setContext(this.#dataCaptureContext);
  }

  /**
   * Configures and starts barcode capture with the specified symbologies.
   * Sets up the camera as the frame source and configures event listeners.
   *
   * @param symbologies - Array of barcode types to recognize
   * @returns Promise that resolves to the initialized BarcodeCapture instance
   * @throws Error if DataCaptureContext is not initialized
   */
  async #startBarcodeCapture(
    symbologies: Symbology[],
  ): Promise<BarcodeCapture> {
    const dataCaptureContext = this.#dataCaptureContext;

    if (!dataCaptureContext) {
      throw new Error('DataCaptureContext is not initialized');
    }

    if (this.#barcodeCapture) {
      this.#cleanup();
    }

    const settings = new BarcodeCaptureSettings();
    settings.enableSymbologies(symbologies);

    console.log({ symbologies });

    this.#barcodeCapture = await BarcodeCapture.forContext(
      dataCaptureContext,
      settings,
    );

    this.#barcodeCapture.addListener({
      didScan: this.#didScan,
    });

    dataCaptureContext.setFrameSource(this.#camera);
    await this.#camera.switchToDesiredState(FrameSourceState.On);
    this.#dataCaptureView.hideProgressBar();
    return this.#barcodeCapture;
  }

  /**
   * Callback that handles barcode scanning events.
   * Runs within Angular zone to ensure proper change detection.
   * Emits the scanned barcode data via the scan output.
   *
   * @param _ - BarcodeCapture instance (unused)
   * @param session - The barcode capture session containing scan results
   */
  #didScan = (_: BarcodeCapture, session: BarcodeCaptureSession) => {
    this.#zone.run(() => {
      const result = session.newlyRecognizedBarcode;

      const code = result?.data;
      console.log('Scanned code:', code);
      if (code) {
        this.scan.emit(code);
      }
    });
  };
  /**
   * Cleanup resources when the component is destroyed.
   * Disables barcode capture, removes event listeners, and turns off the camera.
   * Runs outside Angular zone for better performance.
   */
  ngOnDestroy(): void {
    this.#zone.runOutsideAngular(() => {
      this.#cleanup();
    });
  }
  #cleanup() {
    this.#barcodeCapture?.setEnabled(false);
    this.#barcodeCapture?.removeListener({
      didScan: this.#didScan,
    });
    this.#camera.switchToDesiredState(FrameSourceState.Off);
  }
}
