'use client';

/**
 * Photo Proof Component
 * 
 * Camera integration for delivery proof with:
 * - Camera capture
 * - Gallery upload
 * - Image preview & crop
 * - IndexedDB storage for offline
 * - Compression for upload
 */

import { useState, useRef, useCallback, useEffect } from 'react';
import { Camera, Image as ImageIcon, X, RotateCcw, Check, Loader2, Upload, Trash2 } from 'lucide-react';

type PhotoCaptureMeta = {
  stampedAt: string;
  location?: { lat: number; lng: number; accuracy?: number };
  hash: string;
  facesBlurred: boolean;
  source: 'camera' | 'upload';
  tamperRisk: boolean;
};

type FaceDetectorCtor = new (options?: { fastMode?: boolean; maxDetectedFaces?: number }) => {
  detect: (image: CanvasImageSource) => Promise<Array<{ boundingBox: DOMRectReadOnly }>>;
};

interface PhotoProofProps {
  onPhotoCapture: (imageBlob: Blob, thumbnail: string, meta: PhotoCaptureMeta) => void;
  existingPhotoUrl?: string;
  maxSizeKB?: number;
  quality?: number;
  className?: string;
  enableStamp?: boolean;
  enableFaceBlur?: boolean;
}

export function PhotoProof({
  onPhotoCapture,
  existingPhotoUrl,
  maxSizeKB = 500,
  quality = 0.8,
  className = '',
  enableStamp = true,
  enableFaceBlur = true,
}: PhotoProofProps) {
  const [isCameraOpen, setIsCameraOpen] = useState(false);
  const [capturedImage, setCapturedImage] = useState<string | null>(existingPhotoUrl || null);
  const [isProcessing, setIsProcessing] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [facingMode, setFacingMode] = useState<'user' | 'environment'>('environment');

  const videoRef = useRef<HTMLVideoElement>(null);
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const fileInputRef = useRef<HTMLInputElement>(null);
  const streamRef = useRef<MediaStream | null>(null);

  const getCurrentLocation = useCallback((): Promise<{ lat: number; lng: number; accuracy?: number } | null> => {
    return new Promise((resolve) => {
      if (!navigator.geolocation) return resolve(null);
      navigator.geolocation.getCurrentPosition(
        (pos) => resolve({
          lat: pos.coords.latitude,
          lng: pos.coords.longitude,
          accuracy: pos.coords.accuracy,
        }),
        () => resolve(null),
        { enableHighAccuracy: true, timeout: 5000, maximumAge: 60_000 }
      );
    });
  }, []);

  const hashBlob = useCallback(async (blob: Blob): Promise<string> => {
    try {
      if (!crypto?.subtle) return '';
      const buffer = await blob.arrayBuffer();
      const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);
      return Array.from(new Uint8Array(hashBuffer))
        .map((b) => b.toString(16).padStart(2, '0'))
        .join('');
    } catch {
      return '';
    }
  }, []);

  const detectFaces = useCallback(async (canvas: HTMLCanvasElement) => {
    const FaceDetectorCtor = (window as any).FaceDetector as FaceDetectorCtor | undefined;
    if (!FaceDetectorCtor) return [] as DOMRectReadOnly[];
    try {
      const detector = new FaceDetectorCtor({ fastMode: true, maxDetectedFaces: 6 });
      const faces = await detector.detect(canvas);
      return faces.map((f) => f.boundingBox);
    } catch {
      return [] as DOMRectReadOnly[];
    }
  }, []);

  const applyFaceBlur = useCallback((canvas: HTMLCanvasElement, faces: DOMRectReadOnly[]) => {
    if (!faces.length) return false;
    const ctx = canvas.getContext('2d');
    if (!ctx) return false;
    const temp = document.createElement('canvas');
    temp.width = canvas.width;
    temp.height = canvas.height;
    const tctx = temp.getContext('2d');
    if (!tctx) return false;
    tctx.drawImage(canvas, 0, 0);
    ctx.save();
    ctx.filter = 'blur(10px)';
    faces.forEach((box) => {
      ctx.drawImage(temp, box.x, box.y, box.width, box.height, box.x, box.y, box.width, box.height);
    });
    ctx.restore();
    return true;
  }, []);

  const applyStamp = useCallback((canvas: HTMLCanvasElement, stampedAt: string, location: { lat: number; lng: number; accuracy?: number } | null) => {
    const ctx = canvas.getContext('2d');
    if (!ctx) return;

    const padding = 16;
    const lineHeight = 18;
    const lines = [
      `Time: ${new Date(stampedAt).toLocaleString()}`,
      location ? `GPS: ${location.lat.toFixed(6)}, ${location.lng.toFixed(6)}${location.accuracy ? ` (±${Math.round(location.accuracy)}m)` : ''}` : 'GPS: Unavailable',
    ];

    const boxHeight = padding + lineHeight * lines.length;
    ctx.save();
    ctx.fillStyle = 'rgba(0, 0, 0, 0.55)';
    ctx.fillRect(0, canvas.height - boxHeight, canvas.width, boxHeight);
    ctx.fillStyle = '#fff';
    ctx.font = '14px sans-serif';
    ctx.textBaseline = 'top';
    lines.forEach((line, i) => {
      ctx.fillText(line, padding, canvas.height - boxHeight + padding / 2 + i * lineHeight);
    });
    ctx.restore();
  }, []);

  // Compress image
  const compressImage = useCallback(async (
    imageData: string,
    maxSize: number,
    initialQuality: number
  ): Promise<{ blob: Blob; thumbnail: string }> => {
    return new Promise((resolve, reject) => {
      const img = new window.Image();
      img.onload = () => {
        const canvas = document.createElement('canvas');
        const ctx = canvas.getContext('2d');
        if (!ctx) {
          reject(new Error('Canvas context not available'));
          return;
        }

        // Calculate dimensions (max 1200px)
        let { width, height } = img;
        const maxDim = 1200;
        if (width > maxDim || height > maxDim) {
          if (width > height) {
            height = (height / width) * maxDim;
            width = maxDim;
          } else {
            width = (width / height) * maxDim;
            height = maxDim;
          }
        }

        canvas.width = width;
        canvas.height = height;
        ctx.drawImage(img, 0, 0, width, height);

        // Try compressing
        let currentQuality = initialQuality;
        const compress = () => {
          canvas.toBlob(
            (blob) => {
              if (!blob) {
                reject(new Error('Failed to compress image'));
                return;
              }

              const sizeKB = blob.size / 1024;
              if (sizeKB > maxSize && currentQuality > 0.3) {
                currentQuality -= 0.1;
                compress();
                return;
              }

              // Create thumbnail (200px)
              const thumbCanvas = document.createElement('canvas');
              const thumbCtx = thumbCanvas.getContext('2d');
              const thumbSize = 200;
              const thumbRatio = Math.min(thumbSize / width, thumbSize / height);
              thumbCanvas.width = width * thumbRatio;
              thumbCanvas.height = height * thumbRatio;
              thumbCtx?.drawImage(img, 0, 0, thumbCanvas.width, thumbCanvas.height);
              
              resolve({
                blob,
                thumbnail: thumbCanvas.toDataURL('image/jpeg', 0.6),
              });
            },
            'image/jpeg',
            currentQuality
          );
        };
        compress();
      };
      img.onerror = () => reject(new Error('Failed to load image'));
      img.src = imageData;
    });
  }, []);

  const processImage = useCallback(async (imageData: string, source: 'camera' | 'upload') => {
    const img = new Image();
    img.src = imageData;
    await img.decode();

    const canvas = document.createElement('canvas');
    canvas.width = img.width;
    canvas.height = img.height;
    const ctx = canvas.getContext('2d');
    if (!ctx) throw new Error('Canvas context not available');
    ctx.drawImage(img, 0, 0);

    let facesBlurred = false;
    if (enableFaceBlur) {
      const faces = await detectFaces(canvas);
      facesBlurred = applyFaceBlur(canvas, faces);
    }

    const stampedAt = new Date().toISOString();
    const location = enableStamp ? await getCurrentLocation() : null;
    if (enableStamp) {
      applyStamp(canvas, stampedAt, location);
    }

    const previewUrl = canvas.toDataURL('image/jpeg', 0.92);
    const { blob, thumbnail } = await compressImage(previewUrl, maxSizeKB, quality);
    const hash = await hashBlob(blob);

    return {
      previewUrl,
      blob,
      thumbnail,
      meta: {
        stampedAt,
        location: location || undefined,
        hash,
        facesBlurred,
        source,
        tamperRisk: source === 'upload',
      } as PhotoCaptureMeta,
    };
  }, [applyFaceBlur, applyStamp, compressImage, detectFaces, enableFaceBlur, enableStamp, getCurrentLocation, hashBlob, maxSizeKB, quality]);

  // Start camera
  const startCamera = useCallback(async () => {
    try {
      setError(null);
      setIsProcessing(true);
      
      const stream = await navigator.mediaDevices.getUserMedia({
        video: {
          facingMode,
          width: { ideal: 1280 },
          height: { ideal: 720 },
        },
      });

      streamRef.current = stream;
      if (videoRef.current) {
        videoRef.current.srcObject = stream;
        await videoRef.current.play();
      }
      
      setIsCameraOpen(true);
      setIsProcessing(false);

      // Haptic feedback
      if (navigator.vibrate) {
        navigator.vibrate(30);
      }
    } catch (err: any) {
      setIsProcessing(false);
      if (err.name === 'NotAllowedError') {
        setError('Camera access denied. Please enable it in settings.');
      } else {
        setError('Failed to access camera.');
      }
    }
  }, [facingMode]);

  // Stop camera
  const stopCamera = useCallback(() => {
    if (streamRef.current) {
      streamRef.current.getTracks().forEach(track => track.stop());
      streamRef.current = null;
    }
    if (videoRef.current) {
      videoRef.current.srcObject = null;
    }
    setIsCameraOpen(false);
  }, []);

  // Switch camera
  const switchCamera = useCallback(() => {
    stopCamera();
    setFacingMode(prev => prev === 'user' ? 'environment' : 'user');
  }, [stopCamera]);

  // Restart camera after switch
  useEffect(() => {
    if (isCameraOpen) {
      startCamera();
    }
  }, [facingMode]);

  // Capture photo
  const capturePhoto = useCallback(async () => {
    if (!videoRef.current || !canvasRef.current) return;

    setIsProcessing(true);
    const video = videoRef.current;
    const canvas = canvasRef.current;
    const ctx = canvas.getContext('2d');

    if (!ctx) {
      setError('Canvas context not available');
      setIsProcessing(false);
      return;
    }

    canvas.width = video.videoWidth;
    canvas.height = video.videoHeight;
    ctx.drawImage(video, 0, 0);

    const imageData = canvas.toDataURL('image/jpeg', 0.9);
    
    try {
      const processed = await processImage(imageData, 'camera');
      setCapturedImage(processed.previewUrl);
      stopCamera();
      onPhotoCapture(processed.blob, processed.thumbnail, processed.meta);

      // Haptic feedback
      if (navigator.vibrate) {
        navigator.vibrate([50, 30, 50]);
      }
    } catch (err) {
      setError('Failed to process image');
    }
    
    setIsProcessing(false);
  }, [processImage, stopCamera, onPhotoCapture]);

  // Handle file upload
  const handleFileUpload = useCallback(async (event: React.ChangeEvent<HTMLInputElement>) => {
    const file = event.target.files?.[0];
    if (!file) return;

    setIsProcessing(true);
    setError(null);

    const reader = new FileReader();
    reader.onload = async (e) => {
      const imageData = e.target?.result as string;
      
      try {
        const processed = await processImage(imageData, 'upload');
        setCapturedImage(processed.previewUrl);
        onPhotoCapture(processed.blob, processed.thumbnail, processed.meta);
      } catch (err) {
        setError('Failed to process image');
      }
      
      setIsProcessing(false);
    };
    reader.onerror = () => {
      setError('Failed to read file');
      setIsProcessing(false);
    };
    reader.readAsDataURL(file);

    // Reset input
    event.target.value = '';
  }, [processImage, onPhotoCapture]);

  // Retake photo
  const retakePhoto = useCallback(() => {
    setCapturedImage(null);
    startCamera();
  }, [startCamera]);

  // Delete photo
  const deletePhoto = useCallback(() => {
    setCapturedImage(null);
    
    // Haptic feedback
    if (navigator.vibrate) {
      navigator.vibrate(100);
    }
  }, []);

  // Cleanup on unmount
  useEffect(() => {
    return () => {
      stopCamera();
    };
  }, [stopCamera]);

  return (
    <div className={`rounded-xl border border-slate-200 bg-slate-50 overflow-hidden ${className}`}>
      {error && (
        <div className="bg-red-50 p-3 text-sm text-red-600 flex items-center gap-2">
          <X className="h-4 w-4" />
          {error}
        </div>
      )}

      {capturedImage ? (
        // Preview captured/uploaded image
        <div className="relative">
          <img
            src={capturedImage}
            alt="Captured proof"
            className="w-full h-48 object-cover"
          />
          <div className="absolute bottom-2 right-2 flex gap-2">
            <button
              onClick={retakePhoto}
              className="flex h-10 w-10 items-center justify-center rounded-full bg-white/90 text-slate-700 shadow-lg hover:bg-white transition-all"
              title="Retake"
            >
              <RotateCcw className="h-5 w-5" />
            </button>
            <button
              onClick={deletePhoto}
              className="flex h-10 w-10 items-center justify-center rounded-full bg-red-500/90 text-white shadow-lg hover:bg-red-500 transition-all"
              title="Delete"
            >
              <Trash2 className="h-5 w-5" />
            </button>
          </div>
          <div className="absolute top-2 left-2 flex items-center gap-1 rounded-full bg-emerald-500 px-2 py-1 text-xs font-medium text-white">
            <Check className="h-3 w-3" />
            Photo captured
          </div>
        </div>
      ) : isCameraOpen ? (
        // Camera view
        <div className="relative">
          <video
            ref={videoRef}
            autoPlay
            playsInline
            muted
            className="w-full h-64 object-cover bg-black"
          />
          <canvas ref={canvasRef} className="hidden" />
          
          {/* Camera controls */}
          <div className="absolute bottom-4 left-0 right-0 flex items-center justify-center gap-4">
            <button
              onClick={stopCamera}
              className="flex h-12 w-12 items-center justify-center rounded-full bg-white/80 text-slate-700 shadow-lg hover:bg-white transition-all"
              title="Cancel"
            >
              <X className="h-6 w-6" />
            </button>
            <button
              onClick={capturePhoto}
              disabled={isProcessing}
              className="flex h-16 w-16 items-center justify-center rounded-full bg-white shadow-lg hover:scale-105 transition-all disabled:opacity-50 border-4 border-slate-200"
              title="Capture"
            >
              {isProcessing ? (
                <Loader2 className="h-8 w-8 animate-spin text-slate-600" />
              ) : (
                <div className="h-12 w-12 rounded-full bg-red-500" />
              )}
            </button>
            <button
              onClick={switchCamera}
              className="flex h-12 w-12 items-center justify-center rounded-full bg-white/80 text-slate-700 shadow-lg hover:bg-white transition-all"
              title="Switch camera"
            >
              <RotateCcw className="h-6 w-6" />
            </button>
          </div>

          {/* Viewfinder corners */}
          <div className="absolute inset-8 pointer-events-none">
            <div className="absolute top-0 left-0 w-8 h-8 border-l-2 border-t-2 border-white/70 rounded-tl" />
            <div className="absolute top-0 right-0 w-8 h-8 border-r-2 border-t-2 border-white/70 rounded-tr" />
            <div className="absolute bottom-0 left-0 w-8 h-8 border-l-2 border-b-2 border-white/70 rounded-bl" />
            <div className="absolute bottom-0 right-0 w-8 h-8 border-r-2 border-b-2 border-white/70 rounded-br" />
          </div>
        </div>
      ) : (
        // Initial state - options to take photo or upload
        <div className="p-4">
          <div className="text-sm font-medium text-slate-700 mb-3">Photo Proof of Delivery</div>
          <div className="flex gap-3">
            <button
              onClick={startCamera}
              disabled={isProcessing}
              className="flex-1 flex flex-col items-center gap-2 rounded-xl bg-emerald-50 p-4 text-emerald-700 hover:bg-emerald-100 transition-all disabled:opacity-50"
            >
              {isProcessing ? (
                <Loader2 className="h-8 w-8 animate-spin" />
              ) : (
                <Camera className="h-8 w-8" />
              )}
              <span className="text-sm font-medium">Take Photo</span>
            </button>
            <button
              onClick={() => fileInputRef.current?.click()}
              disabled={isProcessing}
              className="flex-1 flex flex-col items-center gap-2 rounded-xl bg-blue-50 p-4 text-blue-700 hover:bg-blue-100 transition-all disabled:opacity-50"
            >
              <Upload className="h-8 w-8" />
              <span className="text-sm font-medium">Upload</span>
            </button>
          </div>
          <input
            ref={fileInputRef}
            type="file"
            accept="image/*"
            title="Upload photo proof"
            aria-label="Upload photo proof"
            onChange={handleFileUpload}
            className="hidden"
          />
          <p className="mt-2 text-xs text-slate-500 text-center">
            Capture delivery receipt or proof
          </p>
        </div>
      )}
    </div>
  );
}

// Hook for photo storage
export function usePhotoStorage() {
  const savePhoto = useCallback(async (
    deliveryId: string,
    imageBlob: Blob,
    thumbnail: string
  ): Promise<string> => {
    return new Promise((resolve, reject) => {
      const request = indexedDB.open('gcx_fshs_photos', 1);
      
      request.onerror = () => reject(request.error);
      
      request.onupgradeneeded = () => {
        const db = request.result;
        if (!db.objectStoreNames.contains('photos')) {
          db.createObjectStore('photos', { keyPath: 'id' });
        }
      };
      
      request.onsuccess = () => {
        const db = request.result;
        const transaction = db.transaction('photos', 'readwrite');
        const store = transaction.objectStore('photos');
        
        const id = `photo_${deliveryId}_${Date.now()}`;
        store.put({ id, blob: imageBlob, thumbnail, createdAt: Date.now() });
        
        transaction.oncomplete = () => resolve(id);
        transaction.onerror = () => reject(transaction.error);
      };
    });
  }, []);

  const getPhoto = useCallback(async (id: string): Promise<{ blob: Blob; thumbnail: string } | null> => {
    return new Promise((resolve, reject) => {
      const request = indexedDB.open('gcx_fshs_photos', 1);
      
      request.onerror = () => reject(request.error);
      
      request.onsuccess = () => {
        const db = request.result;
        const transaction = db.transaction('photos', 'readonly');
        const store = transaction.objectStore('photos');
        const getRequest = store.get(id);
        
        getRequest.onsuccess = () => {
          const result = getRequest.result;
          resolve(result ? { blob: result.blob, thumbnail: result.thumbnail } : null);
        };
        getRequest.onerror = () => reject(getRequest.error);
      };
    });
  }, []);

  const deletePhoto = useCallback(async (id: string): Promise<void> => {
    return new Promise((resolve, reject) => {
      const request = indexedDB.open('gcx_fshs_photos', 1);
      
      request.onerror = () => reject(request.error);
      
      request.onsuccess = () => {
        const db = request.result;
        const transaction = db.transaction('photos', 'readwrite');
        const store = transaction.objectStore('photos');
        store.delete(id);
        
        transaction.oncomplete = () => resolve();
        transaction.onerror = () => reject(transaction.error);
      };
    });
  }, []);

  return { savePhoto, getPhoto, deletePhoto };
}
