import React, { useState, useEffect } from 'react';
import { FileText, Image as LucideImage, CheckCircle, XCircle, Play, AlertCircle, Loader2, Trash2, ArrowRightLeft, UploadCloud, Clock } from 'lucide-react';

/* Subtitle Verifier (Docker/HF Version)
  - Connects to local backend at /api/generate
  - No API Key required on frontend
*/

// --- Utility Functions ---
const timeStringToMs = (timeStr) => {
  if (!timeStr) return 0;
  const [time, ms] = timeStr.replace(',', '.').split('.');
  const [hours, minutes, seconds] = time.split(':').map(Number);
  return (hours * 3600000) + (minutes * 60000) + (seconds * 1000) + (Number(ms) || 0);
};

const filenameToMs = (filename) => {
  const match = filename.match(/(\d{1,2})_(\d{2})_(\d{2})_(\d{3})/);
  if (!match) return null;
  const [_, h, m, s, ms] = match;
  return (Number(h) * 3600000) + (Number(m) * 60000) + (Number(s) * 1000) + Number(ms);
};

const msToTime = (duration) => {
  const milliseconds = Math.floor((duration % 1000));
  const seconds = Math.floor((duration / 1000) % 60);
  const minutes = Math.floor((duration / (1000 * 60)) % 60);
  const hours = Math.floor((duration / (1000 * 60 * 60)) % 24);
  const pad = (num, size = 2) => num.toString().padStart(size, '0');
  return `${pad(hours)}:${pad(minutes)}:${pad(seconds)},${pad(milliseconds, 3)}`;
};

const parseSRT = (data) => {
  if (!data) return [];
  const normalized = data.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
  const blocks = normalized.split('\n\n');
  return blocks.map(block => {
    const lines = block.trim().split('\n');
    if (lines.length < 2) return null;
    const timeLine = lines[1]; 
    if (!timeLine || !timeLine.includes('-->')) return null;
    const startTimeStr = timeLine.split('-->')[0].trim();
    return {
      id: lines[0],
      time: timeLine,
      startTimeMs: timeStringToMs(startTimeStr),
      text: lines.length > 2 ? lines.slice(2).join(' ') : "[BLANK SUBTITLE]"
    };
  }).filter(Boolean);
};

const compressImage = async (file, maxWidth = 800) => {
  return new Promise((resolve) => {
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = (event) => {
      const img = new Image(); 
      img.src = event.target.result;
      img.onload = () => {
        const canvas = document.createElement('canvas');
        let width = img.width;
        let height = img.height;
        if (width > maxWidth) {
          height = (height * maxWidth) / width;
          width = maxWidth;
        }
        canvas.width = width;
        canvas.height = height;
        const ctx = canvas.getContext('2d');
        ctx.drawImage(img, 0, 0, width, height);
        const dataUrl = canvas.toDataURL('image/jpeg', 0.7);
        resolve({
          originalFile: file,
          dataUrl: dataUrl,
          base64: dataUrl.split(',')[1],
          timeMs: filenameToMs(file.name)
        });
      };
      img.onerror = () => { resolve(null); };
    };
    reader.onerror = () => { resolve(null); };
  });
};

const StatusBadge = ({ status }) => {
  if (status === 'match') return <span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800"><CheckCircle className="w-3 h-3 mr-1"/> Match</span>;
  if (status === 'mismatch') return <span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800"><XCircle className="w-3 h-3 mr-1"/> Mismatch</span>;
  return <span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-gray-100 text-gray-800">Pending</span>;
};

const Header = () => (
  <header className="bg-slate-900 text-white p-6 shadow-lg">
    <div className="max-w-6xl mx-auto flex items-center justify-between">
      <div className="flex items-center space-x-3">
        <div className="bg-blue-600 p-2 rounded-lg">
          <FileText className="w-6 h-6 text-white" />
        </div>
        <div>
          <h1 className="text-xl font-bold tracking-tight">Subtitle QC Assistant</h1>
          <p className="text-slate-400 text-sm">Running securely on Hugging Face Spaces</p>
        </div>
      </div>
      <div className="hidden md:block text-xs text-slate-500 text-right">
        Mode: Sequential Sort (Time Ascending)<br/>
        Smart Size-Based Batching (Max 15MB/call)
      </div>
    </div>
  </header>
);

export default function App() {
  const [srtData, setSrtData] = useState([]);
  const [images, setImages] = useState([]);
  const [pairs, setPairs] = useState([]);
  const [isProcessing, setIsProcessing] = useState(false);
  const [processedCount, setProcessedCount] = useState(0); 
  const [error, setError] = useState(null);
  const [loadingMessage, setLoadingMessage] = useState('');
  
  const [isDraggingSrt, setIsDraggingSrt] = useState(false);
  const [isDraggingImages, setIsDraggingImages] = useState(false);

  useEffect(() => {
    if (srtData.length > 0 && images.length > 0) {
      const sortedImages = [...images].sort((a, b) => {
        if (a.timeMs !== null && b.timeMs !== null) return a.timeMs - b.timeMs;
        return a.originalFile.name.localeCompare(b.originalFile.name, undefined, { numeric: true, sensitivity: 'base' });
      });
      const sortedSrt = [...srtData].sort((a, b) => a.startTimeMs - b.startTimeMs);
      const newPairs = sortedImages.map((img, index) => {
        const matchedSubtitle = sortedSrt[index];
        let matchNote = "Sequential Match";
        let timeDiff = 0;
        if (!matchedSubtitle) matchNote = "No SRT Line Available";
        else if (img.timeMs !== null) {
             timeDiff = Math.abs(matchedSubtitle.startTimeMs - img.timeMs);
             if (timeDiff > 2000) matchNote = `Time Gap: ${msToTime(timeDiff)}`;
             else matchNote = "Synced & Sorted";
        }
        return {
          id: index,
          image: img,
          subtitle: matchedSubtitle || { text: "(End of SRT file - no line #"+(index+1)+")", time: "--:--", id: "N/A" },
          matchNote: matchNote,
          timeDiff: timeDiff,
          status: 'pending',
          analysis: null
        };
      });
      setPairs(newPairs);
    }
  }, [srtData, images]);

  const processSrtFile = (file) => {
    if (!file) return;
    const reader = new FileReader();
    reader.onload = (evt) => {
      try {
        const parsed = parseSRT(evt.target.result);
        setSrtData(parsed);
        setPairs([]); setProcessedCount(0);
      } catch (err) { setError("Invalid SRT file format."); }
    };
    reader.readAsText(file);
  };

  const processImageFiles = async (filesArray) => {
    if (filesArray.length === 0) return;
    setLoadingMessage(`Compressing ${filesArray.length} images...`);
    setIsProcessing(true); setError(null);
    try {
      const processedResults = [];
      const chunkSize = 20; 
      for (let i = 0; i < filesArray.length; i += chunkSize) {
          const chunk = filesArray.slice(i, i + chunkSize);
          const results = await Promise.all(chunk.map(file => compressImage(file)));
          processedResults.push(...results);
      }
      const successfulImages = processedResults.filter(Boolean);
      if (successfulImages.length === 0) throw new Error("No valid images could be processed.");
      setImages(successfulImages); setPairs([]); setProcessedCount(0);
    } catch (err) { setError("Failed to process images: " + err.message); } 
    finally { setIsProcessing(false); setLoadingMessage(''); }
  };

  const runAnalysis = async () => {
    if (pairs.length === 0) return;
    setIsProcessing(true); setError(null); setProcessedCount(0);

    const MAX_PAYLOAD_BYTES = 15 * 1024 * 1024; 
    let currentPairs = [...pairs]; 
    let batchQueue = []; 
    let currentBatch = [];
    let currentBatchSize = 0;

    for (let i = 0; i < currentPairs.length; i++) {
        const pair = currentPairs[i];
        const imgSize = pair.image?.base64?.length || 0; 
        const promptOverhead = 1000; 
        if (currentBatchSize + imgSize + promptOverhead > MAX_PAYLOAD_BYTES && currentBatch.length > 0) {
            batchQueue.push(currentBatch);
            currentBatch = [];
            currentBatchSize = 0;
        }
        currentBatch.push(i);
        currentBatchSize += (imgSize + promptOverhead);
    }
    if (currentBatch.length > 0) batchQueue.push(currentBatch);

    try {
      for (let b = 0; b < batchQueue.length; b++) {
        const indices = batchQueue[b];
        const batchSizeBytes = indices.reduce((acc, idx) => acc + (currentPairs[idx].image?.base64?.length || 0), 0);
        const batchSizeMB = (batchSizeBytes / (1024 * 1024)).toFixed(2);
        setLoadingMessage(`Analyzing batch ${b + 1} of ${batchQueue.length} (${indices.length} items, ~${batchSizeMB}MB)...`);

        const contents = [{
          parts: [{ text: `You are a Subtitle Quality Control (QC) bot. I will provide ${indices.length} images and the EXPECTED subtitle text. Return a JSON array strictly following this schema: [{ "index": 0, "detected_text": "...", "match": true/false, "reason": "..." }] Return ONLY the JSON.` }]
        }];

        indices.forEach(idx => {
          const pair = currentPairs[idx];
          if (pair.image && pair.image.base64) {
              contents[0].parts.push({ text: `\n--- Item ${idx} ---\nIndex: ${idx}\nExpected Text: "${pair.subtitle.text}"\nImage:` });
              contents[0].parts.push({ inlineData: { mimeType: "image/jpeg", data: pair.image.base64 } });
          }
        });

        // CALL LOCAL BACKEND INSTEAD OF GOOGLE DIRECTLY
        const response = await fetch('/api/generate', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            contents: contents,
            generationConfig: { responseMimeType: "application/json" }
          })
        });

        if (!response.ok) throw new Error(`Server Error (Batch ${b+1}): ${response.status}`);
        const data = await response.json();
        
        let parsedResults = [];
        try {
            const rawText = data.candidates?.[0]?.content?.parts?.[0]?.text;
            if (!rawText) throw new Error("No text in response");
            parsedResults = JSON.parse(rawText);
        } catch(e) { throw new Error(`Failed to parse AI response for Batch ${b+1}`); }

        currentPairs = currentPairs.map((pair, idx) => {
          const res = parsedResults.find(r => r.index === idx);
          if (res) return { ...pair, status: res.match ? 'match' : 'mismatch', analysis: res };
          return pair;
        });

        setPairs([...currentPairs]);
        setProcessedCount(prev => prev + indices.length);
      }
    } catch (err) { console.error(err); setError("Analysis interrupted: " + err.message); } 
    finally { setIsProcessing(false); setLoadingMessage(''); }
  };

  // Drag & Drop wrappers
  const handleSrtDrop = (e) => { e.preventDefault(); setIsDraggingSrt(false); if(e.dataTransfer.files.length) processSrtFile(e.dataTransfer.files[0]); };
  const handleImgDrop = (e) => { e.preventDefault(); setIsDraggingImages(false); if(e.dataTransfer.files.length) processImageFiles(Array.from(e.dataTransfer.files)); };

  const handleReset = () => { setSrtData([]); setImages([]); setPairs([]); setProcessedCount(0); setError(null); };

  return (
    <div className="min-h-screen bg-slate-50 text-slate-900 font-sans">
      <Header />
      <main className="max-w-6xl mx-auto p-6 space-y-8">
        <section className="grid md:grid-cols-2 gap-6">
          <div 
            className={`border-2 border-dashed rounded-xl p-8 flex flex-col items-center justify-center transition-all ${isDraggingSrt ? 'border-blue-500 bg-blue-50' : 'border-slate-300 bg-white'} ${srtData.length > 0 ? 'border-green-400 bg-green-50' : ''}`}
            onDragOver={(e)=>{e.preventDefault(); setIsDraggingSrt(true)}} onDragLeave={()=>setIsDraggingSrt(false)} onDrop={handleSrtDrop}
          >
            <input type="file" accept=".srt" onChange={(e)=>processSrtFile(e.target.files[0])} className="hidden" id="srt-upload" />
            <label htmlFor="srt-upload" className="cursor-pointer flex flex-col items-center text-center w-full">
               <FileText className={`w-12 h-12 mb-4 ${srtData.length ? 'text-green-600' : 'text-slate-400'}`} />
               <span className="font-semibold text-lg">{srtData.length ? `${srtData.length} Loaded` : 'Drag & Drop SRT'}</span>
            </label>
          </div>
          <div 
            className={`border-2 border-dashed rounded-xl p-8 flex flex-col items-center justify-center transition-all ${isDraggingImages ? 'border-blue-500 bg-blue-50' : 'border-slate-300 bg-white'} ${images.length > 0 ? 'border-green-400 bg-green-50' : ''}`}
            onDragOver={(e)=>{e.preventDefault(); setIsDraggingImages(true)}} onDragLeave={()=>setIsDraggingImages(false)} onDrop={handleImgDrop}
          >
            <input type="file" accept="image/*" multiple onChange={(e)=>processImageFiles(Array.from(e.target.files))} className="hidden" id="img-upload" />
            <label htmlFor="img-upload" className="cursor-pointer flex flex-col items-center text-center w-full">
               {isProcessing ? <Loader2 className="w-12 h-12 mb-4 text-blue-500 animate-spin" /> : <LucideImage className={`w-12 h-12 mb-4 ${images.length ? 'text-green-600' : 'text-slate-400'}`} />}
               <span className="font-semibold text-lg">{images.length ? `${images.length} Loaded` : 'Drag & Drop Images'}</span>
            </label>
          </div>
        </section>

        {isProcessing && <div className="bg-blue-50 border border-blue-200 rounded-lg p-4 flex items-center justify-center space-x-3 text-blue-800 animate-pulse"><Loader2 className="w-5 h-5 animate-spin" /><span className="font-medium">{loadingMessage}</span></div>}
        {error && <div className="bg-red-50 border border-red-200 rounded-lg p-4 flex items-center space-x-3 text-red-800"><AlertCircle className="w-5 h-5" /><span>{error}</span></div>}

        {pairs.length > 0 && (
          <section className="bg-white rounded-xl shadow-sm border border-slate-200 overflow-hidden">
            <div className="p-4 border-b border-slate-200 bg-slate-50 flex justify-between items-center sticky top-0 z-10">
              <h2 className="font-bold text-lg">Review ({pairs.length})</h2>
              <div className="flex space-x-3">
                 <button onClick={handleReset} className="flex items-center space-x-2 px-4 py-2 text-sm font-medium text-slate-600 hover:text-red-600"><Trash2 className="w-4 h-4" /><span>Clear</span></button>
                 <button onClick={runAnalysis} disabled={isProcessing} className={`flex items-center space-x-2 px-6 py-2 rounded-lg font-semibold text-white shadow-md ${isProcessing ? 'bg-slate-400' : 'bg-blue-600 hover:bg-blue-700'}`}>
                   {isProcessing ? <Loader2 className="w-4 h-4 animate-spin" /> : <Play className="w-4 h-4" />}
                   <span>{processedCount > 0 ? 'Continue' : 'Run'}</span>
                 </button>
              </div>
            </div>
            <div className="divide-y divide-slate-100">
              {pairs.map((pair, index) => (
                <div key={index} className={`grid grid-cols-12 gap-4 p-4 ${pair.status === 'mismatch' ? 'bg-red-50/50' : ''}`}>
                  <div className="col-span-12 md:col-span-3 relative aspect-video bg-slate-100 rounded-lg overflow-hidden border border-slate-200">
                      {pair.image?.dataUrl && <img src={pair.image.dataUrl} className="w-full h-full object-contain" />}
                      <div className="absolute top-0 left-0 right-0 bg-black/60 p-1"><div className="text-white text-[10px] truncate font-mono">{index + 1}. {pair.image?.originalFile?.name}</div></div>
                  </div>
                  <div className="col-span-12 md:col-span-4 flex flex-col justify-center">
                    <div className="flex justify-between mb-1"><div className="text-xs font-bold text-slate-400 uppercase">Expected</div><div className="text-[10px] px-2 py-0.5 rounded-full bg-blue-100 text-blue-700">{pair.matchNote}</div></div>
                    <div className="p-3 bg-slate-100 rounded-lg text-slate-700 text-sm">{pair.subtitle.text}</div>
                  </div>
                  <div className="col-span-12 md:col-span-5 flex flex-col justify-center">
                    <div className="flex justify-between mb-1"><div className="text-xs font-bold text-slate-400 uppercase">Actual</div><StatusBadge status={pair.status} /></div>
                    <div className={`p-3 rounded-lg text-sm border min-h-[60px] flex items-center ${pair.status === 'match' ? 'bg-green-50 text-green-800' : pair.status === 'mismatch' ? 'bg-red-50 text-red-800' : 'bg-white'}`}>
                      {pair.analysis ? <div><span className="font-semibold block mb-1">Detected: "{pair.analysis.detected_text}"</span><span className="text-xs opacity-75">{pair.analysis.reason}</span></div> : "Ready..."}
                    </div>
                  </div>
                </div>
              ))}
            </div>
          </section>
        )}
      </main>
    </div>
  );
}