import {
  BarcodeScanner,
  BarcodeFormat,
  StartScanOptions,
  LensFacing,
} from "@capacitor-mlkit/barcode-scanning";
import {
  QRScannerService,
  ScanListener,
  QRScannerOptions,
  CameraStateListener,
  CameraState,
} from "./types";
import { logger } from "@/utils/logger";

export class CapacitorQRScanner implements QRScannerService {
  private scanListener: ScanListener | null = null;
  private isScanning = false;
  private listenerHandles: Array<() => Promise<void>> = [];
  private cleanupPromise: Promise<void> | null = null;
  private cameraStateListeners: Set<CameraStateListener> = new Set();
  private currentState: CameraState = "off";
  private currentStateMessage?: string;

  async checkPermissions(): Promise<boolean> {
    try {
      logger.debug("Checking camera permissions");
      const { camera } = await BarcodeScanner.checkPermissions();
      return camera === "granted";
    } catch (error) {
      const wrappedError =
        error instanceof Error ? error : new Error(String(error));
      logger.error("Error checking camera permissions:", {
        error: wrappedError.message,
      });
      return false;
    }
  }

  async requestPermissions(): Promise<boolean> {
    try {
      // First check if we already have permissions
      if (await this.checkPermissions()) {
        logger.debug("Camera permissions already granted");
        return true;
      }

      logger.debug("Requesting camera permissions");
      const { camera } = await BarcodeScanner.requestPermissions();
      const granted = camera === "granted";
      logger.debug(`Camera permissions ${granted ? "granted" : "denied"}`);
      return granted;
    } catch (error) {
      const wrappedError =
        error instanceof Error ? error : new Error(String(error));
      logger.error("Error requesting camera permissions:", {
        error: wrappedError.message,
      });
      return false;
    }
  }

  async isSupported(): Promise<boolean> {
    try {
      logger.debug("Checking scanner support");
      const { supported } = await BarcodeScanner.isSupported();
      logger.debug(`Scanner support: ${supported}`);
      return supported;
    } catch (error) {
      const wrappedError =
        error instanceof Error ? error : new Error(String(error));
      logger.error("Error checking scanner support:", {
        error: wrappedError.message,
      });
      return false;
    }
  }

  async startScan(options?: QRScannerOptions): Promise<void> {
    if (this.isScanning) {
      logger.debug("Scanner already running");
      return;
    }

    if (this.cleanupPromise) {
      logger.debug("Waiting for previous cleanup to complete");
      await this.cleanupPromise;
    }

    try {
      this.updateCameraState("initializing", "Starting camera...");

      // Ensure we have permissions before starting
      if (!(await this.checkPermissions())) {
        this.updateCameraState("permission_denied", "Camera permission denied");
        logger.debug("Requesting camera permissions");
        const granted = await this.requestPermissions();
        if (!granted) {
          throw new Error("Camera permission denied");
        }
      }

      // Check if scanning is supported
      if (!(await this.isSupported())) {
        this.updateCameraState(
          "error",
          "QR scanning not supported on this device",
        );
        throw new Error("QR scanning not supported on this device");
      }

      logger.info("Starting MLKit scanner");
      this.isScanning = true;
      this.updateCameraState("active", "Camera is active");

      const scanOptions: StartScanOptions = {
        formats: [BarcodeFormat.QrCode],
        lensFacing:
          options?.camera === "front" ? LensFacing.Front : LensFacing.Back,
      };

      logger.debug("Scanner options:", scanOptions);

      // Add listener for barcode scans
      const handle = await BarcodeScanner.addListener(
        "barcodeScanned",
        (result) => {
          if (this.scanListener && result.barcode?.rawValue) {
            this.scanListener.onScan(result.barcode.rawValue);
          }
        },
      );
      this.listenerHandles.push(handle.remove);

      // Start continuous scanning
      await BarcodeScanner.startScan(scanOptions);
      logger.info("MLKit scanner started successfully");
    } catch (error) {
      const wrappedError =
        error instanceof Error ? error : new Error(String(error));
      logger.error("Error during QR scan:", {
        error: wrappedError.message,
        stack: wrappedError.stack,
      });
      this.isScanning = false;
      this.updateCameraState("error", wrappedError.message);
      await this.cleanup();
      this.scanListener?.onError?.(wrappedError);
      throw wrappedError;
    }
  }

  async stopScan(): Promise<void> {
    if (!this.isScanning) {
      logger.debug("Scanner not running");
      return;
    }

    try {
      logger.debug("Stopping QR scanner");
      this.updateCameraState("off", "Camera stopped");
      await BarcodeScanner.stopScan();
      logger.info("QR scanner stopped successfully");
    } catch (error) {
      const wrappedError =
        error instanceof Error ? error : new Error(String(error));
      logger.error("Error stopping QR scan:", {
        error: wrappedError.message,
        stack: wrappedError.stack,
      });
      this.updateCameraState("error", wrappedError.message);
      this.scanListener?.onError?.(wrappedError);
      throw wrappedError;
    } finally {
      this.isScanning = false;
    }
  }

  addListener(listener: ScanListener): void {
    this.scanListener = listener;
  }

  async cleanup(): Promise<void> {
    // Prevent multiple simultaneous cleanup attempts
    if (this.cleanupPromise) {
      return this.cleanupPromise;
    }

    this.cleanupPromise = (async () => {
      try {
        logger.debug("Starting QR scanner cleanup");

        // Stop scanning if active
        if (this.isScanning) {
          await this.stopScan();
        }

        // Remove all listeners
        for (const handle of this.listenerHandles) {
          try {
            await handle();
          } catch (error) {
            logger.warn("Error removing listener:", error);
          }
        }

        logger.info("QR scanner cleanup completed");
      } catch (error) {
        const wrappedError =
          error instanceof Error ? error : new Error(String(error));
        logger.error("Error during cleanup:", {
          error: wrappedError.message,
          stack: wrappedError.stack,
        });
        throw wrappedError;
      } finally {
        this.listenerHandles = [];
        this.scanListener = null;
        this.cleanupPromise = null;
      }
    })();

    return this.cleanupPromise;
  }

  onStream(callback: (stream: MediaStream | null) => void): void {
    // No-op for native scanner
    callback(null);
  }

  addCameraStateListener(listener: CameraStateListener): void {
    this.cameraStateListeners.add(listener);
    // Immediately notify the new listener of current state
    listener.onStateChange(this.currentState, this.currentStateMessage);
  }

  removeCameraStateListener(listener: CameraStateListener): void {
    this.cameraStateListeners.delete(listener);
  }

  private updateCameraState(state: CameraState, message?: string): void {
    this.currentState = state;
    this.currentStateMessage = message;
    // Notify all listeners of state change
    for (const listener of this.cameraStateListeners) {
      listener.onStateChange(state, message);
    }
  }
}
