'use client';

import { useState, useRef, useEffect } from 'react';

const API_URL = process.env.NEXT_PUBLIC_API_URL || '';
const DB_NAME = 'bird-capture-queue';
const STORE_NAME = 'pending';
const MAX_IMAGE_PX = 1568;

async function openDB(): Promise<IDBDatabase> {
  return new Promise((resolve, reject) => {
    const req = indexedDB.open(DB_NAME, 1);
    req.onupgradeneeded = () => req.result.createObjectStore(STORE_NAME, { keyPath: 'id' });
    req.onsuccess = () => resolve(req.result);
    req.onerror = () => reject(req.error);
  });
}

async function queueCapture(audio: Blob | null, image: Blob | null) {
  const db = await openDB();
  const id = Date.now().toString();
  return new Promise<void>((resolve, reject) => {
    const tx = db.transaction(STORE_NAME, 'readwrite');
    tx.objectStore(STORE_NAME).put({ id, audio: audio || null, image: image || null, timestamp: new Date().toISOString() });
    tx.oncomplete = () => resolve();
    tx.onerror = () => reject(tx.error);
  });
}

async function getPendingCaptures(): Promise<any[]> {
  const db = await openDB();
  return new Promise((resolve, reject) => {
    const tx = db.transaction(STORE_NAME, 'readonly');
    const req = tx.objectStore(STORE_NAME).getAll();
    req.onsuccess = () => resolve(req.result);
    req.onerror = () => reject(req.error);
  });
}

async function removeCapture(id: string) {
  const db = await openDB();
  return new Promise<void>((resolve, reject) => {
    const tx = db.transaction(STORE_NAME, 'readwrite');
    tx.objectStore(STORE_NAME).delete(id);
    tx.oncomplete = () => resolve();
    tx.onerror = () => reject(tx.error);
  });
}

async function resizeImage(file: File): Promise<Blob> {
  return new Promise((resolve) => {
    const img = new Image();
    const url = URL.createObjectURL(file);
    img.onload = () => {
      URL.revokeObjectURL(url);
      const { width, height } = img;
      const maxDim = MAX_IMAGE_PX;
      let w = width, h = height;
      if (w > maxDim || h > maxDim) {
        if (w > h) { h = Math.round(h * maxDim / w); w = maxDim; }
        else { w = Math.round(w * maxDim / h); h = maxDim; }
      }
      const canvas = document.createElement('canvas');
      canvas.width = w; canvas.height = h;
      const ctx = canvas.getContext('2d')!;
      ctx.drawImage(img, 0, 0, w, h);
      canvas.toBlob((blob) => resolve(blob!), 'image/jpeg', 0.88);
    };
    img.src = url;
  });
}

async function submitCapture(audio: Blob | null, image: Blob | null): Promise<{ ok: boolean; errorType?: string }> {
  try {
    const form = new FormData();
    if (audio) form.append('audio', audio, 'capture.webm');
    if (image) form.append('image', image, 'capture.jpg');
    const res = await fetch(`${API_URL}/api/capture`, { method: 'POST', body: form });
    if (res.ok) return { ok: true };
    const body = await res.json().catch(() => ({}));
    return { ok: false, errorType: body.error_type };
  } catch {
    return { ok: false };
  }
}

type CaptureMode = 'idle' | 'image-selected' | 'recording' | 'submitting' | 'saved' | 'queued' | 'error';

export default function CapturePage() {
  const [mode, setMode] = useState<CaptureMode>('idle');
  const [imagePreview, setImagePreview] = useState<string | null>(null);
  const [imageBlob, setImageBlob] = useState<Blob | null>(null);
  const [countdown, setCountdown] = useState(0);
  const [pendingCount, setPendingCount] = useState(0);
  const [recentCaptures, setRecentCaptures] = useState<{name: string}[]>([]);

  const mediaRecorderRef = useRef<MediaRecorder | null>(null);
  const chunksRef = useRef<Blob[]>([]);
  const streamRef = useRef<MediaStream | null>(null);
  const wakeLockRef = useRef<any>(null);
  const countdownRef = useRef<any>(null);
  const mimeTypeRef = useRef('audio/webm');
  const fileInputRef = useRef<HTMLInputElement>(null);
  const imageBlobRef = useRef<Blob | null>(null);
  const MAX_SECONDS = 120;

  useEffect(() => { imageBlobRef.current = imageBlob; }, [imageBlob]);

  useEffect(() => {
    loadRecentCaptures();
    checkPending();
    window.addEventListener('focus', retryQueue);
    window.addEventListener('online', retryQueue);

    // Listen for capture_saved SSE — refresh list when transcription completes
    let es: EventSource | null = null;
    function connectSSE() {
      es = new EventSource(`${API_URL}/api/captures/events`);
      es.onmessage = (e) => {
        try {
          const data = JSON.parse(e.data);
          if (data.type === 'capture_saved') loadRecentCaptures();
        } catch {}
      };
      es.onerror = () => { es?.close(); setTimeout(connectSSE, 10000); };
    }
    connectSSE();

    return () => {
      window.removeEventListener('focus', retryQueue);
      window.removeEventListener('online', retryQueue);
      es?.close();
    };
  }, []);

  async function loadRecentCaptures() {
    try {
      const res = await fetch(`${API_URL}/api/captures`);
      if (res.ok) { const data = await res.json(); setRecentCaptures(data.captures || []); }
    } catch {}
  }

  async function checkPending() {
    try { const p = await getPendingCaptures(); setPendingCount(p.length); } catch {}
  }

  async function retryQueue() {
    try {
      const pending = await getPendingCaptures();
      for (const item of pending) {
        const result = await submitCapture(item.audio, item.image);
        if (result.ok) await removeCapture(item.id);
      }
      const remaining = await getPendingCaptures();
      setPendingCount(remaining.length);
      if (remaining.length < pending.length) loadRecentCaptures();
    } catch {}
  }

  function handleCameraClick() { fileInputRef.current?.click(); }

  async function handleImageSelected(e: React.ChangeEvent<HTMLInputElement>) {
    const file = e.target.files?.[0];
    if (!file) return;
    const resized = await resizeImage(file);
    setImageBlob(resized);
    imageBlobRef.current = resized;
    setImagePreview(URL.createObjectURL(resized));
    setMode('image-selected');
    e.target.value = '';
  }

  function clearImage() {
    if (imagePreview) URL.revokeObjectURL(imagePreview);
    setImagePreview(null);
    setImageBlob(null);
    imageBlobRef.current = null;
    setMode('idle');
    stopRecording();
  }

  async function acquireWakeLock() {
    try { if ('wakeLock' in navigator) wakeLockRef.current = await (navigator as any).wakeLock.request('screen'); } catch {}
  }

  function releaseWakeLock() { wakeLockRef.current?.release(); wakeLockRef.current = null; }

  async function startRecording() {
    try {
      if (!streamRef.current || streamRef.current.getTracks().every(t => t.readyState === 'ended')) {
        streamRef.current = await navigator.mediaDevices.getUserMedia({ audio: true });
      }
      const mimeType = MediaRecorder.isTypeSupported('audio/webm') ? 'audio/webm'
        : MediaRecorder.isTypeSupported('audio/mp4') ? 'audio/mp4' : 'audio/ogg';
      mimeTypeRef.current = mimeType;
      chunksRef.current = [];
      const mr = new MediaRecorder(streamRef.current, { mimeType });
      mr.ondataavailable = e => { if (e.data.size > 0) chunksRef.current.push(e.data); };
      mr.onstop = async () => {
        streamRef.current?.getTracks().forEach(t => t.stop());
        streamRef.current = null;
        const audioBlob = chunksRef.current.length > 0 ? new Blob(chunksRef.current, { type: mimeTypeRef.current }) : null;
        await submitFinal(audioBlob);
      };
      mr.start(1000);
      mediaRecorderRef.current = mr;
      setMode('recording');
      setCountdown(MAX_SECONDS);
      await acquireWakeLock();
      countdownRef.current = setInterval(() => {
        setCountdown(prev => { if (prev <= 1) { stopRecording(); return 0; } return prev - 1; });
      }, 1000);
    } catch {
      setMode('error');
      setTimeout(() => setMode(imageBlobRef.current ? 'image-selected' : 'idle'), 3000);
    }
  }

  function stopRecording() {
    if (countdownRef.current) { clearInterval(countdownRef.current); countdownRef.current = null; }
    releaseWakeLock();
    if (mediaRecorderRef.current?.state === 'recording') mediaRecorderRef.current.stop();
  }

  async function submitFinal(audioBlob: Blob | null, retryCount = 0) {
    setMode('submitting');
    const currentImageBlob = imageBlobRef.current;
    const result = await submitCapture(audioBlob, currentImageBlob);

    if (result.ok) {
      setMode('saved');
      if (imagePreview) URL.revokeObjectURL(imagePreview);
      setImagePreview(null);
      setImageBlob(null);
      imageBlobRef.current = null;
      // Optimistic update — show immediately as processing
      setRecentCaptures(prev => [{ name: `${new Date().toISOString().slice(0,16).replace('T','-').replace(':','-')}-voice ⏳` }, ...prev].slice(0, 4));
      setTimeout(() => setMode('idle'), 3000);
      return;
    }

    const isTranscriptionFailure = result.errorType === 'transcription_failed' || result.errorType === 'empty_transcript';
    if (isTranscriptionFailure && retryCount < 2) {
      setTimeout(() => submitFinal(audioBlob, retryCount + 1), 3000 * (retryCount + 1));
      return;
    }

    try {
      await queueCapture(audioBlob, currentImageBlob);
      await checkPending();
      setMode('queued');
    } catch {
      setMode('error');
    }
    if (imagePreview) URL.revokeObjectURL(imagePreview);
    setImagePreview(null);
    setImageBlob(null);
    imageBlobRef.current = null;
    setTimeout(() => setMode('idle'), 4000);
  }

  async function submitImageOnly() { await submitFinal(null); }

  function handleVoiceTap() {
    if (mode === 'recording') stopRecording();
    else if (mode === 'image-selected' || mode === 'idle') startRecording();
  }

  const isRecording = mode === 'recording';
  const isBusy = mode === 'submitting';
  const hasImage = imageBlob !== null;

  return (
    <div style={{ minHeight: '100dvh', background: 'var(--bg)', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'space-between', paddingTop: 'max(48px, env(safe-area-inset-top))', paddingBottom: 'max(32px, env(safe-area-inset-bottom))', paddingLeft: 'max(24px, env(safe-area-inset-left))', paddingRight: 'max(24px, env(safe-area-inset-right))', fontFamily: 'var(--font-mono)' }}>

      <div style={{ textAlign: 'center' }}>
        <p style={{ fontSize: '10px', letterSpacing: '0.15em', color: 'var(--text3)', textTransform: 'uppercase', margin: 0 }}>Bird — field recorder</p>
        {pendingCount > 0 && <p style={{ fontSize: '9px', color: '#c8821a', letterSpacing: '0.1em', margin: '4px 0 0', textTransform: 'uppercase' }}>{pendingCount} capture{pendingCount > 1 ? 's' : ''} queued</p>}
      </div>

      <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '28px', width: '100%', maxWidth: '320px' }}>

        {imagePreview && (
          <div style={{ position: 'relative', width: '100%' }}>
            <img src={imagePreview} alt="capture preview" style={{ width: '100%', maxHeight: '240px', objectFit: 'cover', borderRadius: '12px', border: '1px solid var(--border)', display: 'block' }} />
            <button onClick={clearImage} style={{ position: 'absolute', top: '8px', right: '8px', background: 'rgba(0,0,0,0.6)', border: 'none', borderRadius: '50%', width: '28px', height: '28px', color: '#fff', cursor: 'pointer', fontSize: '16px', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>×</button>
          </div>
        )}

        {isRecording && (
          <div style={{ display: 'flex', alignItems: 'center', gap: '3px', height: '32px' }}>
            {[4,8,16,6,24,10,32,14,20,8,12,28,6,18,4].map((h, i) => (
              <div key={i} style={{ width: '2px', height: `${h}px`, background: 'var(--accent)', borderRadius: '1px', animation: `wave ${0.8 + (i % 4) * 0.2}s ease-in-out infinite alternate` }} />
            ))}
          </div>
        )}

        <div style={{ display: 'flex', gap: '24px', alignItems: 'center', justifyContent: 'center' }}>
          <button onClick={handleCameraClick} disabled={isBusy || isRecording} style={{ width: '72px', height: '72px', borderRadius: '50%', border: `1.5px solid ${hasImage ? 'var(--accent)' : 'var(--border2)'}`, background: hasImage ? 'var(--accent-light)' : 'var(--bg2)', cursor: isBusy || isRecording ? 'not-allowed' : 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', transition: 'all 0.2s', touchAction: 'manipulation', opacity: isBusy || isRecording ? 0.5 : 1 }} aria-label="Take photo or choose image">
            <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke={hasImage ? 'var(--accent)' : 'var(--text3)'} strokeWidth="1.5"><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/><circle cx="12" cy="13" r="4"/></svg>
          </button>

          <button onPointerUp={handleVoiceTap} disabled={isBusy} style={{ width: '120px', height: '120px', borderRadius: '50%', border: `1.5px solid ${isRecording ? '#a32d2d' : 'var(--accent)'}`, background: isRecording ? 'rgba(163,45,45,0.08)' : 'var(--bg2)', cursor: isBusy ? 'not-allowed' : 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', transition: 'all 0.3s', touchAction: 'manipulation', animation: isBusy ? 'none' : isRecording ? 'recordPulse 1.5s ease-in-out infinite' : 'idlePulse 3s ease-in-out infinite' }} aria-label={isRecording ? 'Stop recording' : 'Record voice annotation'}>
            {isBusy ? (
              <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" style={{ color: 'var(--accent)' }}><circle cx="12" cy="12" r="3" opacity="0.6"><animate attributeName="opacity" values="0.6;1;0.6" dur="1s" repeatCount="indefinite"/></circle></svg>
            ) : isRecording ? (
              <div style={{ width: '20px', height: '20px', background: '#a32d2d', borderRadius: '4px' }} />
            ) : (
              <svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="var(--accent)" strokeWidth="1.5"><path d="M12 1a4 4 0 0 1 4 4v6a4 4 0 0 1-8 0V5a4 4 0 0 1 4-4z"/><path d="M5 11a7 7 0 0 0 14 0"/><line x1="12" y1="18" x2="12" y2="22"/><line x1="8" y1="22" x2="16" y2="22"/></svg>
            )}
          </button>

          {hasImage && !isRecording && !isBusy ? (
            <button onClick={submitImageOnly} style={{ width: '72px', height: '72px', borderRadius: '50%', border: '1.5px solid var(--accent)', background: 'var(--accent-light)', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', transition: 'all 0.2s', touchAction: 'manipulation' }} aria-label="Save image without annotation">
              <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="var(--accent)" strokeWidth="2"><polyline points="20 6 9 17 4 12"/></svg>
            </button>
          ) : <div style={{ width: '72px', height: '72px' }} />}
        </div>

        {isRecording && (
          <div style={{ width: '100%' }}>
            <div style={{ width: '100%', height: '2px', background: 'var(--border)', borderRadius: '1px', overflow: 'hidden', marginBottom: '6px' }}>
              <div style={{ height: '100%', width: `${((MAX_SECONDS - countdown) / MAX_SECONDS) * 100}%`, background: countdown <= 15 ? '#a32d2d' : 'var(--accent)', borderRadius: '1px', transition: 'width 1s linear' }} />
            </div>
            <p style={{ fontSize: '10px', color: 'var(--text3)', margin: 0, textAlign: 'center' }}>{Math.floor(countdown/60)}:{(countdown%60).toString().padStart(2,'0')} left</p>
          </div>
        )}

        <div style={{ textAlign: 'center', minHeight: '20px' }}>
          {mode === 'idle' && <p style={{ fontSize: '10px', color: 'var(--text3)', letterSpacing: '0.12em', textTransform: 'uppercase', margin: 0 }}>tap camera or mic</p>}
          {mode === 'image-selected' && <p style={{ fontSize: '10px', color: 'var(--text3)', letterSpacing: '0.10em', textTransform: 'uppercase', margin: 0 }}>annotate with voice or save now</p>}
          {mode === 'recording' && <p style={{ fontSize: '10px', color: '#a32d2d', letterSpacing: '0.12em', textTransform: 'uppercase', margin: 0 }}>recording annotation</p>}
          {mode === 'submitting' && <p style={{ fontSize: '10px', color: 'var(--text3)', letterSpacing: '0.12em', textTransform: 'uppercase', margin: 0 }}>transcribing...</p>}
          {mode === 'saved' && <p style={{ fontSize: '10px', color: 'var(--accent)', letterSpacing: '0.12em', textTransform: 'uppercase', margin: 0 }}>saved ✓</p>}
          {mode === 'queued' && <p style={{ fontSize: '10px', color: '#c8821a', letterSpacing: '0.12em', textTransform: 'uppercase', margin: 0 }}>queued — will sync when online</p>}
          {mode === 'error' && <p style={{ fontSize: '10px', color: '#a32d2d', letterSpacing: '0.12em', textTransform: 'uppercase', margin: 0 }}>error — try again</p>}
        </div>
      </div>

      <div style={{ width: '100%', maxWidth: '320px' }}>
        <p style={{ fontSize: '9px', color: 'var(--text3)', letterSpacing: '0.12em', textTransform: 'uppercase', margin: '0 0 8px' }}>recent captures</p>
        {recentCaptures.length === 0 ? (
          <p style={{ fontSize: '10px', color: 'var(--text3)', margin: 0, opacity: 0.5 }}>none yet</p>
        ) : (
          <div style={{ display: 'flex', flexDirection: 'column', gap: '5px' }}>
            {recentCaptures.slice(0, 4).map((c, i) => (
              <div key={i} style={{ display: 'flex', alignItems: 'center', gap: '8px', padding: '6px 10px', background: 'var(--bg2)', borderRadius: '6px', border: '1px solid var(--border)' }}>
                <div style={{ width: '6px', height: '6px', borderRadius: '50%', flexShrink: 0, background: c.name.includes('image') ? '#c8821a' : 'var(--accent)' }} />
                <span style={{ fontSize: '10px', color: 'var(--text2)', flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{c.name}</span>
              </div>
            ))}
          </div>
        )}
      </div>

      <input ref={fileInputRef} type="file" accept="image/*" onChange={handleImageSelected} style={{ display: 'none' }} />

      <style>{`
        @keyframes idlePulse { 0%, 100% { box-shadow: 0 0 0 0 rgba(45,158,107,0); } 50% { box-shadow: 0 0 0 12px rgba(45,158,107,0.06); } }
        @keyframes recordPulse { 0%, 100% { box-shadow: 0 0 0 0 rgba(163,45,45,0); } 50% { box-shadow: 0 0 0 16px rgba(163,45,45,0.08); } }
        @keyframes wave { from { transform: scaleY(0.4); } to { transform: scaleY(1.4); } }
      `}</style>
    </div>
  );
}
