'use client';

import { useState, useEffect, useRef } from 'react';
import { usePathname, useRouter } from 'next/navigation';
import { BrowserMultiFormatReader, DecodeHintType, BarcodeFormat } from '@zxing/library';
import { scanQr } from '@/modules/sessionModule/qrScan/actions/cameraActions';
import { isCameraStartActive, stopMediaStream } from '@/lib/cameraLifecycle';

type Props = {
  readonly isOpen: boolean;
  readonly onClose: () => void;
};

// Helper: load image with timeout (moved top-level to reduce nesting complexity)
async function loadImage(src: string, timeoutMs = 5000): Promise<HTMLImageElement> {
  const img = new window.Image();
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), timeoutMs);
  return await new Promise<HTMLImageElement>((resolve, reject) => {
    controller.signal.addEventListener('abort', () => {
      clearTimeout(timeout);
      reject(new Error('image-timeout'));
    });
    img.onload = () => { clearTimeout(timeout); resolve(img); };
    img.onerror = () => { clearTimeout(timeout); reject(new Error('image-load-error')); };
    img.src = src;
  });
}

export default function MobileScanModal({ isOpen, onClose }: Props) {
  const [isScanning, setIsScanning] = useState(false);
  const [scanResult, setScanResult] = useState<string | null>(null);
  const [serverMessage, setServerMessage] = useState<string | null>(null);
  const [error, setError] = useState<string | null>(null);
  const [scanSequence, setScanSequence] = useState(0);
  const [isUploading, setIsUploading] = useState(false);
  const [cameraReady, setCameraReady] = useState(false);
  const [devices, setDevices] = useState<MediaDeviceInfo[]>([]);
  const [currentDeviceId, setCurrentDeviceId] = useState<string | null>(null);
  const [permissionDenied, setPermissionDenied] = useState(false);
  const router = useRouter();
  const didRedirectRef = useRef(false);
  const pathname = usePathname();
  const scanSessionRef = useRef(0);
  const isOpenRef = useRef(isOpen);
  
  const videoRef = useRef<HTMLVideoElement>(null);
  const fileInputRef = useRef<HTMLInputElement>(null);
  const codeReaderRef = useRef<BrowserMultiFormatReader | null>(null);
  const redirectOverlayRef = useRef<HTMLDivElement | null>(null);
  const cameraProbeStreamRef = useRef<MediaStream | null>(null);
  const cameraStartGenerationRef = useRef(0);
  const cameraReadyTimerRef = useRef<number | null>(null);

  // Handle modal open/close
  useEffect(() => {
    isOpenRef.current = isOpen;
    if (isOpen) {
      document.body.style.overflow = 'hidden';
      scanSessionRef.current += 1;
      resetState();
      setIsScanning(true);
    } else {
      document.body.style.overflow = '';
      setIsScanning(false);
      resetState();
    }

    return () => {
      document.body.style.overflow = '';
    };
  }, [isOpen]);

  // Handle camera
  useEffect(() => {
    if (isScanning && isOpen) {
      startCamera();
    } else {
      stopCamera();
    }

    return () => stopCamera();
  }, [isScanning, isOpen]);

  const resetState = () => {
    setScanResult(null);
    setServerMessage(null);
    setError(null);
    setCameraReady(false);
    didRedirectRef.current = false;
    setScanSequence(0);
  };
  const cameraStartingRef = useRef(false);

  const startCamera = async (deviceIdOverride?: string) => {
    const expectedGeneration = cameraStartGenerationRef.current;
    const isStartActive = () => isCameraStartActive({
      expectedGeneration,
      currentGeneration: cameraStartGenerationRef.current,
      isOpen: isOpenRef.current,
    });

    try {
      if (cameraStartingRef.current) return; // avoid parallel inits
      cameraStartingRef.current = true;
      setPermissionDenied(false);
      setError(null);
      // Request permission early if labels hidden
      if (!devices.length) {
        try {
          const probeStream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: { ideal: 'environment' } } });
          cameraProbeStreamRef.current = probeStream;
          if (!isStartActive()) {
            stopMediaStream(probeStream);
            cameraProbeStreamRef.current = null;
            return;
          }
          stopMediaStream(probeStream);
          cameraProbeStreamRef.current = null;
        } catch (permErr: any) {
          if (permErr?.name === 'NotAllowedError') {
            if (!isStartActive()) return;
            setPermissionDenied(true);
            setError('Camera access is blocked. Set this site\'s camera permission to Allow, then retry.');
            return;
          }
        }
      }

      if (!isStartActive()) return;

      // Fresh enumeration to avoid relying on possibly stale state
      const all = await navigator.mediaDevices.enumerateDevices();
      if (!isStartActive()) return;
      const vids = all.filter(d => d.kind === 'videoinput');
      setDevices(vids);
      if (!vids.length) {
        setError('No camera device detected.');
        return;
      }

      const codeReader = new BrowserMultiFormatReader();
      codeReaderRef.current = codeReader;

      let useDeviceId = deviceIdOverride || currentDeviceId;
      if (!useDeviceId) {
        const env = vids.find(v => /back|rear|environment/i.test(v.label));
        useDeviceId = env?.deviceId || vids[0].deviceId;
        setCurrentDeviceId(useDeviceId);
      }

      if (!useDeviceId) {
        setError('No camera device detected.');
        return;
      }

      if (videoRef.current) {
        await codeReader.decodeFromVideoDevice(
          useDeviceId,
          videoRef.current,
          async (result) => {
            if (result) {
              const sessionId = scanSessionRef.current;
              if (!isOpenRef.current || sessionId !== scanSessionRef.current) return;
              const text = result.getText();
              didRedirectRef.current = false;
              setScanResult(text);
              setServerMessage(null);
              setError(null);
              setScanSequence(prev => prev + 1);
              void scanQr(text).then((serverResponse) => {
                if (!isOpenRef.current || sessionId !== scanSessionRef.current) return;
                if (serverResponse.status === 'success') {
                  setServerMessage(serverResponse.data);
                } else {
                  setError(serverResponse.data);
                }
              });
            }
          }
        );
        if (!isStartActive()) {
          stopCamera();
          return;
        }
        const v = videoRef.current;
        const confirmReady = () => {
          if (!isStartActive()) return;
          if (cameraReadyTimerRef.current !== null) {
            window.clearTimeout(cameraReadyTimerRef.current);
            cameraReadyTimerRef.current = null;
          }
          setCameraReady(true);
          setError(null);
        };
        v.onloadedmetadata = confirmReady;
        v.onplaying = confirmReady;
        cameraReadyTimerRef.current = window.setTimeout(confirmReady, 1200);
      }
    } catch (err: any) {
      if (!isStartActive()) return;
      console.error('Error starting camera:', err);
      if (err?.name === 'NotAllowedError') {
        setError('Camera access is blocked. Set this site\'s camera permission to Allow, then retry.');
        setPermissionDenied(true);
      } else {
        setError(prev => prev || 'Failed to start camera. Please check permissions.');
      }
    } finally {
      cameraStartingRef.current = false;
    }
  };

  const stopCamera = () => {
    cameraStartGenerationRef.current += 1;
    if (cameraReadyTimerRef.current !== null) {
      window.clearTimeout(cameraReadyTimerRef.current);
      cameraReadyTimerRef.current = null;
    }
    if (codeReaderRef.current) {
      codeReaderRef.current.reset();
      codeReaderRef.current = null;
    }
    if (videoRef.current?.srcObject) {
      stopMediaStream(videoRef.current.srcObject as MediaStream);
      videoRef.current.srcObject = null;
    }
    stopMediaStream(cameraProbeStreamRef.current);
    cameraProbeStreamRef.current = null;
    setCameraReady(false);
  };

  const switchCamera = async () => {
    if (devices.length < 2) return; // nothing to switch
    const idx = devices.findIndex(d => d.deviceId === currentDeviceId);
    const next = devices[(idx + 1) % devices.length];
    setCurrentDeviceId(next.deviceId);
    stopCamera();
    await startCamera(next.deviceId);
  };

  const handleFileUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    if (!file) return;

    setIsUploading(true);
    setError(null);

    try {
      const supportedTypes = ['image/png', 'image/jpeg', 'image/webp', 'image/jpg', 'image/bmp', 'image/gif'];
      if (file.type && !supportedTypes.includes(file.type)) {
        throw new Error('Unsupported image format');
      }

      // Gunakan Object URL dan hints yang lebih komprehensif untuk meningkatkan decoding
      const objectUrl = URL.createObjectURL(file);

      const hints = new Map();
      hints.set(DecodeHintType.TRY_HARDER, true);
      hints.set(DecodeHintType.PURE_BARCODE, false);
      hints.set(DecodeHintType.POSSIBLE_FORMATS, [
        BarcodeFormat.QR_CODE,
        BarcodeFormat.DATA_MATRIX,
        BarcodeFormat.AZTEC,
        BarcodeFormat.PDF_417,
        BarcodeFormat.MAXICODE,
      ]);

      const codeReader = new BrowserMultiFormatReader(hints);

      let text: string | null = null;

      // 1) Coba decode langsung dari URL
      try {
        const res1 = await (codeReader as any).decodeFromImageUrl?.(objectUrl);
        if (res1) {
          text = res1.getText();
        }
      } catch {}

      // 2) Enhanced fallback dengan preprocessing gambar
      const tryDecodeFromCanvas = async (img: HTMLImageElement) => {
        const maxDim = 1500; // Tingkatkan resolusi maksimum untuk mobile
        const scale = Math.min(1, maxDim / Math.max(img.naturalWidth || img.width, img.naturalHeight || img.height));
        const baseWidth = Math.max(1, Math.floor((img.naturalWidth || img.width) * scale));
        const baseHeight = Math.max(1, Math.floor((img.naturalHeight || img.height) * scale));

        // Preprocessing function untuk meningkatkan kontras dan kualitas
        const preprocessCanvas = (canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D) => {
          const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
          const data = imageData.data;

          // Convert to grayscale dan tingkatkan kontras
          for (let i = 0; i < data.length; i += 4) {
            const gray = Math.round(0.299 * data[i] + 0.587 * data[i + 1] + 0.114 * data[i + 2]);
            
            // Tingkatkan kontras dengan threshold
            const enhanced = gray > 128 ? Math.min(255, gray * 1.2) : Math.max(0, gray * 0.8);
            
            data[i] = enhanced;     // red
            data[i + 1] = enhanced; // green
            data[i + 2] = enhanced; // blue
            // alpha tetap sama
          }

          ctx.putImageData(imageData, 0, 0);
        };

        const attemptDecodeRotation = async (angle: number) => {
          const radians = (angle * Math.PI) / 180;
          const canvas = document.createElement('canvas');
          const ctx = canvas.getContext('2d');
          if (!ctx) return null;
          const cos = Math.abs(Math.cos(radians));
          const sin = Math.abs(Math.sin(radians));
          const newWidth = Math.ceil(baseWidth * cos + baseHeight * sin);
          const newHeight = Math.ceil(baseWidth * sin + baseHeight * cos);
          canvas.width = newWidth;
          canvas.height = newHeight;
          ctx.save();
          ctx.translate(canvas.width / 2, canvas.height / 2);
          ctx.rotate(radians);
          ctx.imageSmoothingEnabled = false;
          ctx.drawImage(img, -baseWidth / 2, -baseHeight / 2, baseWidth, baseHeight);
          ctx.restore();
          // raw
          try {
            const resCanvas = await (codeReader as any).decodeFromCanvas?.(canvas);
            if (resCanvas) return resCanvas.getText();
          } catch {}
          // processed
          try {
            preprocessCanvas(canvas, ctx);
            const resCanvas2 = await (codeReader as any).decodeFromCanvas?.(canvas);
            if (resCanvas2) return resCanvas2.getText();
          } catch {}
          // image fallback
          try {
            const tmp = await loadImage(canvas.toDataURL('image/png'));
            const resImg = await codeReader.decodeFromImage(tmp);
            if (resImg) return resImg.getText();
          } catch {}
          return null;
        };

        // rotation attempts
        for (const angle of [0, 90, 180, 270, 45, 135, 225, 315]) {
          const val = await attemptDecodeRotation(angle);
          if (val) return val;
        }

        // Coba dengan ukuran berbeda jika masih gagal
        const scales = [0.5, 1.5, 2.0];
        for (const scaleMultiplier of scales) {
          const scaledWidth = Math.floor(baseWidth * scaleMultiplier);
          const scaledHeight = Math.floor(baseHeight * scaleMultiplier);
          
          const canvas = document.createElement('canvas');
          const ctx = canvas.getContext('2d');
          if (!ctx) continue;

          canvas.width = scaledWidth;
          canvas.height = scaledHeight;

          ctx.imageSmoothingEnabled = scaleMultiplier < 1;
          ctx.drawImage(img, 0, 0, scaledWidth, scaledHeight);

          try {
            preprocessCanvas(canvas, ctx);
            const resCanvas = await (codeReader as any).decodeFromCanvas?.(canvas);
            if (resCanvas) return resCanvas.getText();
          } catch {}
        }

        return null;
      };

      if (!text) {
        const img = new window.Image();
        img.crossOrigin = 'anonymous'; // Untuk menghindari CORS issues
        img.src = objectUrl;
        await new Promise((resolve, reject) => {
          img.onload = resolve;
          img.onerror = reject;
          setTimeout(reject, 10000); // timeout 10 detik
        });
        text = await tryDecodeFromCanvas(img);
      }

      // Cleanup object URL
      URL.revokeObjectURL(objectUrl);

      if (!text) throw new Error('QR not detected');

      const sessionId = scanSessionRef.current;
      didRedirectRef.current = false;
      setScanResult(text);
      setServerMessage(null);
      setError(null);
      setScanSequence(prev => prev + 1);
      void scanQr(text).then((serverResponse) => {
        if (!isOpenRef.current || sessionId !== scanSessionRef.current) return;
        if (serverResponse.status === 'success') {
          setServerMessage(serverResponse.data);
        } else {
          setError(serverResponse.data);
        }
      });
    } catch (err: any) {
      console.error('Mobile QR Decode Error:', err);
      if (err?.message === 'Unsupported image format') {
        setError('Format gambar tidak didukung. Gunakan PNG, JPEG, WEBP, atau BMP.');
      } else if (err?.message?.includes('timeout')) {
        setError('Timeout saat memproses gambar. Coba dengan gambar yang lebih kecil.');
      } else {
        setError('Tidak dapat membaca QR code dari gambar. Tips: pastikan gambar jelas, tidak blur, QR code terlihat penuh, dan coba dengan pencahayaan yang baik.');
      }
    } finally {
      setIsUploading(false);
    }
  };

  const handleClose = () => {
    setIsScanning(false);
    stopCamera();
    onClose();
  };

  // Show a global overlay spinner while redirecting
  const showRedirectOverlay = () => {
    if (redirectOverlayRef.current || typeof document === 'undefined') return;
    const el = document.createElement('div');
    el.setAttribute('aria-busy', 'true');
    el.setAttribute('aria-live', 'polite');
    el.className = 'fixed inset-0 z-[9999] bg-black/50 backdrop-blur-sm flex items-center justify-center';
    el.innerHTML = `
      <div role="status" aria-label="Redirecting" class="flex flex-col items-center gap-3 text-white">
        <div class="h-12 w-12 rounded-full border-4 border-white/30 border-t-white animate-spin"></div>
        <div class="text-sm">Redirecting…</div>
      </div>
    `;
    document.body.appendChild(el);
    redirectOverlayRef.current = el;
    // Safety cleanup after 5s in case navigation stalls
    window.setTimeout(() => {
      if (redirectOverlayRef.current) {
        redirectOverlayRef.current.remove();
        redirectOverlayRef.current = null;
      }
    }, 5000);
  };

  // Redirect otomatis ketika QR berhasil terbaca pada mobile modal
  useEffect(() => {
    if (!scanResult || didRedirectRef.current) return;

    // Show overlay, then close modal before redirecting
    didRedirectRef.current = true;
    setIsScanning(false);
    stopCamera();
    showRedirectOverlay();
    onClose();

    const value = scanResult.trim();
    try {
      const url = new URL(value);
      const target = url.pathname + url.search + url.hash;
      if (typeof window !== 'undefined') {
        const current = window.location.pathname + window.location.search + window.location.hash;
        if (target === current) {
          window.location.href = window.location.origin + target;
          return;
        }
      }
      router.push(target);
    } catch {
      if (value.startsWith('/')) {
        if (typeof window !== 'undefined') {
          const current = window.location.pathname + window.location.search + window.location.hash;
          if (value === current) {
            window.location.href = window.location.origin + value;
            return;
          }
        }
        router.push(value);
      }
    }
  }, [scanResult, scanSequence, router, onClose]);

  // Remove overlay as soon as route/pathname changes (SPA navigation completed)
  useEffect(() => {
    if (redirectOverlayRef.current && didRedirectRef.current) {
      redirectOverlayRef.current.remove();
      redirectOverlayRef.current = null;
    }
  }, [pathname]);

  // Cleanup on unmount to avoid overlay getting stuck
  useEffect(() => {
    return () => {
      if (redirectOverlayRef.current) {
        redirectOverlayRef.current.remove();
        redirectOverlayRef.current = null;
      }
    };
  }, []);

  return (
    <>
      {isOpen && (
      <div className="fixed inset-0 z-50 md:hidden">
        {/* Backdrop */}
        <div 
          className="absolute inset-0 bg-black/80"
          onClick={handleClose}
        />
        
        {/* Bottom Sheet */}
        <div className="absolute inset-x-0 bottom-0 bg-white dark:bg-gray-900 rounded-t-3xl shadow-2xl animate-in slide-in-from-bottom duration-300">
          {/* Handle bar */}
          <div className="flex justify-center pt-4 pb-2">
            <div className="w-12 h-1 bg-gray-300 dark:bg-gray-600 rounded-full" />
          </div>

          {/* Header */}
          <div className="flex items-center justify-between px-6 py-4 border-b border-gray-200 dark:border-gray-700">
            <h2 className="text-xl font-semibold text-gray-900 dark:text-white">QR Scanner</h2>
            <button
              onClick={handleClose}
              className="w-8 h-8 flex items-center justify-center text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
            >
              <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
              </svg>
            </button>
          </div>

          <div className="max-h-[80vh] overflow-y-auto">
            {/* Camera Section */}
            <div className="p-4">
              <div className="relative bg-black rounded-2xl overflow-hidden aspect-square">
                <video
                  ref={videoRef}
                  className="w-full h-full object-cover"
                  playsInline
                  muted
                />
                {/* Switch camera button */}
                {devices.length > 1 && (
                  <button
                    type="button"
                    onClick={switchCamera}
                    className="absolute top-3 right-3 z-10 bg-black/50 text-white px-3 py-1.5 rounded-lg text-xs font-medium backdrop-blur-sm hover:bg-black/60 active:scale-95"
                  >
                    Switch
                  </button>
                )}
                
                {/* Scanning overlay */}
                <div className="absolute inset-0 pointer-events-none">
                  {/* Scanning frame */}
                  <div className="absolute inset-0 flex items-center justify-center">
                    <div className="relative w-64 h-64 max-w-[80%] max-h-[80%]">
                      {/* Corner guides */}
                      <div className="absolute top-0 left-0 w-6 h-6 border-l-3 border-t-3 border-white opacity-80"></div>
                      <div className="absolute top-0 right-0 w-6 h-6 border-r-3 border-t-3 border-white opacity-80"></div>
                      <div className="absolute bottom-0 left-0 w-6 h-6 border-l-3 border-b-3 border-white opacity-80"></div>
                      <div className="absolute bottom-0 right-0 w-6 h-6 border-r-3 border-b-3 border-white opacity-80"></div>
                      
                      {/* Scanning line */}
                      <div className="absolute inset-0 flex items-center">
                        <div className="w-full h-0.5 bg-gradient-to-r from-transparent via-blue-400 to-transparent animate-pulse"></div>
                      </div>
                    </div>
                  </div>

                  {/* Status */}
                  <div className="absolute top-4 left-4 right-4 flex justify-between">
                    <div className="bg-black/60 backdrop-blur-sm rounded-lg px-3 py-2">
                      <div className="flex items-center space-x-2">
                        <div className="w-2 h-2 bg-green-400 rounded-full animate-pulse"></div>
                        <span className="text-white text-sm">
                          {(() => {
                            if (permissionDenied) return 'Permission needed';
                            if (cameraReady) return 'Ready';
                            if (error) return 'Initializing...';
                            return 'Starting...';
                          })()}
                        </span>
                      </div>
                    </div>
                    {permissionDenied && (
                      <div className="pointer-events-auto mt-2 flex flex-col gap-2 items-start">
                        <div className="bg-red-500/80 text-white text-xs px-2 py-1 rounded">
                          Camera blocked. Enable in browser/site settings.
                        </div>
                        <button
                          type="button"
                          onClick={() => { stopCamera(); startCamera(); }}
                          className="text-xs px-2 py-1 rounded bg-white/80 text-gray-800 font-medium shadow hover:bg-white"
                        >
                          Retry Permission
                        </button>
                      </div>
                    )}
                  </div>

                  <div className="absolute bottom-4 inset-x-4">
                    <div className="bg-black/60 backdrop-blur-sm rounded-lg px-4 py-2 text-center">
                      <p className="text-white text-sm">Position QR code in frame</p>
                    </div>
                  </div>
                </div>
              </div>
            </div>

            {/* Upload option */}
            <div className="px-4 pb-4">
              <div className="relative">
                <div className="absolute inset-0 flex items-center">
                  <div className="w-full border-t border-gray-200 dark:border-gray-700" />
                </div>
                <div className="relative flex justify-center text-sm">
                  <span className="bg-white dark:bg-gray-900 px-3 text-gray-500">or</span>
                </div>
              </div>
              
              <label className={`mt-4 block w-full text-center px-4 py-3 border-2 border-dashed border-gray-300 dark:border-gray-600 text-gray-600 dark:text-gray-400 font-medium rounded-xl transition-colors ${
                isUploading ? 'opacity-50' : 'active:bg-gray-50 dark:active:bg-gray-800'
              }`}>
                {isUploading ? 'Processing...' : 'Upload from Gallery'}
                <input
                  ref={fileInputRef}
                  type="file"
                  accept="image/*"
                  className="hidden"
                  disabled={isUploading}
                  onChange={handleFileUpload}
                />
              </label>
            </div>

            {/* Results */}
            {(scanResult || serverMessage || error) && (
              <div className="px-4 pb-6 space-y-3">
                {scanResult && (
                  <div className="p-4 rounded-xl bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-700">
                    <p className="text-green-800 dark:text-green-200 font-medium mb-2">Scanned:</p>
                    <p className="text-sm font-mono break-words text-green-700 dark:text-green-300 bg-white dark:bg-green-800/20 p-2 rounded border">{scanResult}</p>
                  </div>
                )}

                {serverMessage && (
                  <div className="p-4 rounded-xl bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-700">
                    <p className="text-blue-800 dark:text-blue-200 font-medium mb-2">Response:</p>
                    <p className="text-sm text-blue-700 dark:text-blue-300">{serverMessage}</p>
                  </div>
                )}

                {error && (
                  <div className="p-4 rounded-xl bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-700">
                    <p className="text-red-800 dark:text-red-200 font-medium mb-2">Error:</p>
                    <p className="text-sm text-red-700 dark:text-red-300">{error}</p>
                  </div>
                )}
              </div>
            )}
          </div>

          {/* Safe area for bottom navigation */}
          <div className="h-safe-area-inset-bottom" />
        </div>
      </div>
      )}
    </>
  );
}
