/**
 * Mobile Camera Utilities
 * Provides camera access, photo capture, and image processing for mobile devices
 */

export interface CameraConfig {
  facingMode?: 'user' | 'environment';
  width?: number;
  height?: number;
  aspectRatio?: number;
}

export interface CapturedImage {
  blob: Blob;
  dataUrl: string;
  width: number;
  height: number;
}

export class CameraService {
  private stream: MediaStream | null = null;
  private videoElement: HTMLVideoElement | null = null;

  async checkCameraAvailability(): Promise<boolean> {
    try {
      const devices = await navigator.mediaDevices.enumerateDevices();
      return devices.some((device) => device.kind === 'videoinput');
    } catch {
      return false;
    }
  }

  async requestPermission(): Promise<PermissionState> {
    try {
      const result = await navigator.permissions.query({ name: 'camera' as PermissionName });
      return result.state;
    } catch {
      // Fallback: try to access camera to trigger permission prompt
      try {
        const stream = await navigator.mediaDevices.getUserMedia({ video: true });
        stream.getTracks().forEach((track) => track.stop());
        return 'granted';
      } catch {
        return 'denied';
      }
    }
  }

  async startCamera(videoElement: HTMLVideoElement, config: CameraConfig = {}): Promise<void> {
    const {
      facingMode = 'environment',
      width = 1280,
      height = 720,
      aspectRatio,
    } = config;

    const constraints: MediaStreamConstraints = {
      video: {
        facingMode,
        width: { ideal: width },
        height: { ideal: height },
        ...(aspectRatio && { aspectRatio }),
      },
    };

    try {
      this.stream = await navigator.mediaDevices.getUserMedia(constraints);
      videoElement.srcObject = this.stream;
      this.videoElement = videoElement;
      await videoElement.play();
    } catch (error) {
      throw new Error(`Failed to start camera: ${(error as Error).message}`);
    }
  }

  async capturePhoto(quality = 0.9): Promise<CapturedImage> {
    if (!this.videoElement || !this.stream) {
      throw new Error('Camera not started');
    }

    const canvas = document.createElement('canvas');
    const video = this.videoElement;

    canvas.width = video.videoWidth;
    canvas.height = video.videoHeight;

    const ctx = canvas.getContext('2d');
    if (!ctx) {
      throw new Error('Failed to get canvas context');
    }

    ctx.drawImage(video, 0, 0, canvas.width, canvas.height);

    return new Promise((resolve, reject) => {
      canvas.toBlob(
        (blob) => {
          if (!blob) {
            reject(new Error('Failed to capture photo'));
            return;
          }

          const dataUrl = canvas.toDataURL('image/jpeg', quality);

          resolve({
            blob,
            dataUrl,
            width: canvas.width,
            height: canvas.height,
          });
        },
        'image/jpeg',
        quality
      );
    });
  }

  async switchCamera(): Promise<void> {
    if (!this.videoElement) {
      throw new Error('Camera not started');
    }

    const currentTrack = this.stream?.getVideoTracks()[0];
    const currentFacingMode = currentTrack?.getSettings().facingMode;
    const newFacingMode = currentFacingMode === 'user' ? 'environment' : 'user';

    this.stopCamera();
    await this.startCamera(this.videoElement, { facingMode: newFacingMode });
  }

  stopCamera(): void {
    if (this.stream) {
      this.stream.getTracks().forEach((track) => track.stop());
      this.stream = null;
    }

    if (this.videoElement) {
      this.videoElement.srcObject = null;
      this.videoElement = null;
    }
  }

  isActive(): boolean {
    return this.stream !== null && this.stream.active;
  }
}

// Image processing utilities
export async function cropImage(
  image: CapturedImage,
  x: number,
  y: number,
  width: number,
  height: number
): Promise<CapturedImage> {
  const canvas = document.createElement('canvas');
  canvas.width = width;
  canvas.height = height;

  const ctx = canvas.getContext('2d');
  if (!ctx) {
    throw new Error('Failed to get canvas context');
  }

  const img = await loadImage(image.dataUrl);
  ctx.drawImage(img, x, y, width, height, 0, 0, width, height);

  return new Promise((resolve, reject) => {
    canvas.toBlob(
      (blob) => {
        if (!blob) {
          reject(new Error('Failed to crop image'));
          return;
        }

        resolve({
          blob,
          dataUrl: canvas.toDataURL('image/jpeg', 0.9),
          width,
          height,
        });
      },
      'image/jpeg',
      0.9
    );
  });
}

export async function resizeImage(
  image: CapturedImage,
  maxWidth: number,
  maxHeight: number
): Promise<CapturedImage> {
  const img = await loadImage(image.dataUrl);

  let { width, height } = img;
  const ratio = Math.min(maxWidth / width, maxHeight / height);

  if (ratio < 1) {
    width = Math.round(width * ratio);
    height = Math.round(height * ratio);
  }

  const canvas = document.createElement('canvas');
  canvas.width = width;
  canvas.height = height;

  const ctx = canvas.getContext('2d');
  if (!ctx) {
    throw new Error('Failed to get canvas context');
  }

  ctx.drawImage(img, 0, 0, width, height);

  return new Promise((resolve, reject) => {
    canvas.toBlob(
      (blob) => {
        if (!blob) {
          reject(new Error('Failed to resize image'));
          return;
        }

        resolve({
          blob,
          dataUrl: canvas.toDataURL('image/jpeg', 0.9),
          width,
          height,
        });
      },
      'image/jpeg',
      0.9
    );
  });
}

function loadImage(src: string): Promise<HTMLImageElement> {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.onload = () => resolve(img);
    img.onerror = reject;
    img.src = src;
  });
}

// Singleton instance
let cameraInstance: CameraService | null = null;

export function getCamera(): CameraService {
  if (!cameraInstance) {
    cameraInstance = new CameraService();
  }
  return cameraInstance;
}

export default CameraService;
