import { useEffect, useRef, useState } from 'react';
import { UploadCloud, CheckCircle, AlertCircle, Loader2, X, Clock, Minus, AlertTriangle, Trash2 } from 'lucide-react';
import { processSourceText } from '../../lib/extractionPipeline';
import { pb } from '../../lib/pb';
import * as db from '../../lib/db';
import { useBeforeUnload } from '../../hooks/useBeforeUnload';
import Card from '../ui/Card';
import Button from '../ui/Button';

// ── Main Component ────────────────────────────────────────────────────────────

const UploadZone = ({ onUploadComplete }) => {
  const [isDragging, setIsDragging] = useState(false);
  const [queue, setQueue]           = useState([]);
  const [processingId, setProcessingId] = useState(null);
  const [rejectedNote, setRejectedNote] = useState(null);
  const [orphanedSources, setOrphanedSources] = useState([]);

  const fileInputRef = useRef(null);
  const abortRef     = useRef(null);

  // ── Tab-close guard ─────────────────────────────────────────────────────────
  useBeforeUnload(processingId !== null);

  // ── Detect orphaned sources on mount ────────────────────────────────────────
  useEffect(() => {
    db.getOrphanedSources().then(setOrphanedSources);
  }, []);

  const handleResetOrphan = async (id) => {
    await db.updateSourceStatus(id, 'failed', 'Interrupted — tab was closed during extraction');
    setOrphanedSources((prev) => prev.filter((s) => s.id !== id));
    if (onUploadComplete) onUploadComplete();
  };

  const handleDeleteOrphan = async (id) => {
    await db.deleteSource(id);
    setOrphanedSources((prev) => prev.filter((s) => s.id !== id));
    if (onUploadComplete) onUploadComplete();
  };

  // ── Processing loop ────────────────────────────────────────────────────────

  useEffect(() => {
    if (processingId !== null) return;
    const next = queue.find((q) => q.status === 'pending');
    if (!next) return;

    setProcessingId(next.id);
    setQueue((q) => q.map((item) => item.id === next.id ? { ...item, status: 'processing' } : item));

    const controller = new AbortController();
    abortRef.current = controller;

    // Poll for progress updates from PocketBase while processing
    let progressInterval;
    let lastSourceId = null;

    next.file.text()
      .then(async (text) => {
        // Start processing — get source ID from the pipeline to poll progress
        const resultPromise = processSourceText(text, next.name, { signal: controller.signal });

        // Find the newly created source record to track its progress
        const sources = await db.getSources();
        const sourceRec = sources.find(s => s.name === next.name && s.status === 'processing');
        if (sourceRec) {
          lastSourceId = sourceRec.id;
          progressInterval = setInterval(async () => {
            try {
              const updated = await pb.collection('sources').getOne(lastSourceId);
              if (updated.progress) {
                setQueue((q) => q.map((item) =>
                  item.id === next.id
                    ? { ...item, progress: updated.progress }
                    : item
                ));
              }
            } catch { /* source may have been deleted */ }
          }, 2000);
        }

        return resultPromise;
      })
      .then(() => {
        setQueue((q) => q.map((item) => item.id === next.id ? { ...item, status: 'done', progress: null } : item));
        if (onUploadComplete) onUploadComplete();
      })
      .catch((err) => {
        const isCancelled = err?.name === 'AbortError';
        let errorMsg = err?.message || 'Unknown error';
        if (err?.name === 'LLMTruncatedError') {
          errorMsg = 'File is too large for the AI context window. Please split into smaller chunks.';
        } else if (err?.name === 'LLMValidationError') {
          errorMsg = 'AI output was malformed (not JSON). Please try again.';
        }
        
        setQueue((q) => q.map((item) =>
          item.id === next.id
            ? { ...item, status: isCancelled ? 'cancelled' : 'failed', error: isCancelled ? null : errorMsg, progress: null }
            : item
        ));
      })
      .finally(() => {
        if (progressInterval) clearInterval(progressInterval);
        abortRef.current = null;
        setProcessingId(null);
      });
  }, [processingId, queue, onUploadComplete]);

  // ── File ingestion ─────────────────────────────────────────────────────────

  const addFiles = (fileList) => {
    const accepted = [];
    let rejected = 0;
    for (const file of fileList) {
      if (file.type === 'text/plain' || file.name.endsWith('.md')) {
        accepted.push({ id: crypto.randomUUID(), file, name: file.name, status: 'pending', error: null, progress: null });
      } else {
        rejected++;
      }
    }
    if (accepted.length > 0) setQueue((q) => [...q, ...accepted]);
    if (rejected > 0) {
      setRejectedNote(`${rejected} file${rejected > 1 ? 's' : ''} skipped — only .txt and .md are supported.`);
      setTimeout(() => setRejectedNote(null), 4000);
    }
  };

  const cancelCurrent = () => {
    abortRef.current?.abort(new DOMException('Cancelled by user', 'AbortError'));
  };

  const clearDone = () => {
    setQueue((q) => q.filter((item) => item.status === 'pending' || item.status === 'processing'));
  };

  // ── Drag & drop / browse ───────────────────────────────────────────────────

  const handleDragOver  = (e) => { e.preventDefault(); setIsDragging(true); };
  const handleDragLeave = ()  => setIsDragging(false);

  const handleDrop = (e) => {
    e.preventDefault();
    setIsDragging(false);
    if (e.dataTransfer.files?.length > 0) addFiles(e.dataTransfer.files);
  };

  const handleFileSelect = (e) => {
    if (e.target.files?.length > 0) {
      addFiles(e.target.files);
      e.target.value = '';
    }
  };

  // ── Derived state ──────────────────────────────────────────────────────────

  const hasDone = queue.some((q) => ['done', 'failed', 'cancelled'].includes(q.status));

  // ── Render ─────────────────────────────────────────────────────────────────

  return (
    <div className="space-y-4">

      {/* ─── Orphaned Sources Warning ─── */}
      {orphanedSources.length > 0 && (
        <Card className="border-2 border-amber-300 bg-amber-50/50">
          <div className="flex items-start gap-3 mb-3">
            <AlertTriangle size={20} className="text-amber-600 flex-shrink-0 mt-0.5" />
            <div>
              <p className="font-medium text-amber-800">Interrupted extraction{orphanedSources.length > 1 ? 's' : ''} detected</p>
              <p className="text-sm text-amber-700 mt-1">
                {orphanedSources.length === 1
                  ? 'A source extraction was interrupted (tab closed or browser crashed). You can delete it and re-upload the file.'
                  : `${orphanedSources.length} source extractions were interrupted. Delete and re-upload the files to retry.`}
              </p>
            </div>
          </div>
          <div className="space-y-2">
            {orphanedSources.map((s) => (
              <div key={s.id} className="flex items-center justify-between gap-3 px-3 py-2 rounded-[var(--r-sm)] bg-white/60 text-sm">
                <div className="flex-1">
                  <span className="font-medium">{s.name}</span>
                  {s.progress && (
                    <span className="text-amber-600 ml-2 text-xs">
                      (stopped at chunk {s.progress.current}/{s.progress.total})
                    </span>
                  )}
                </div>
                <div className="flex items-center gap-2">
                  <button
                    onClick={() => handleResetOrphan(s.id)}
                    className="text-xs text-amber-700 hover:text-amber-900 underline"
                  >
                    Mark failed
                  </button>
                  <button
                    onClick={() => handleDeleteOrphan(s.id)}
                    className="p-1.5 text-red-500 hover:text-red-600 hover:bg-red-50 rounded-[var(--r-sm)] transition-colors"
                    title="Delete"
                  >
                    <Trash2 size={14} />
                  </button>
                </div>
              </div>
            ))}
          </div>
        </Card>
      )}

      {/* ─── Drag & Drop Zone ─── */}
      <Card
        className={`border-2 border-dashed transition-colors flex flex-col items-center justify-center py-10 cursor-pointer ${
          isDragging ? 'border-teal bg-teal/5' : 'border-bg-warm bg-bg-warm/20'
        }`}
        onDragOver={handleDragOver}
        onDragLeave={handleDragLeave}
        onDrop={handleDrop}
        onClick={() => fileInputRef.current?.click()}
      >
        <UploadCloud size={40} className="text-teal/40 mb-3" />
        <p className="font-medium">Drag files here</p>
        <p className="text-sm text-fg-muted mb-4">Supports .txt and .md · max 5 MB each</p>
        <Button variant="outline" type="button">Browse files</Button>
        <input
          type="file"
          ref={fileInputRef}
          onChange={handleFileSelect}
          accept=".txt,.md"
          multiple
          className="hidden"
        />
      </Card>

      {/* ─── Rejected file note ─── */}
      {rejectedNote && (
        <p className="text-xs text-amber-600">{rejectedNote}</p>
      )}

      {/* ─── Queue ─── */}
      {queue.length > 0 && (
        <div className="space-y-1">
          {queue.map((item) => (
            <div
              key={item.id}
              className="flex items-center gap-3 px-3 py-2 rounded-[var(--r-sm)] bg-bg-warm/30 text-sm"
            >
              <StatusIcon status={item.status} />
              <span className="flex-1 truncate text-fg">{item.name}</span>
              <span className="text-xs text-fg-muted shrink-0">
                {item.status === 'processing' && (
                  item.progress
                    ? `Chunk ${item.progress.current + 1}/${item.progress.total}`
                    : 'Starting…'
                )}
                {item.status === 'pending'    && 'Pending'}
                {item.status === 'done'       && 'Done'}
                {item.status === 'cancelled'  && 'Cancelled'}
                {item.status === 'failed'     && (
                  <span className="text-red-500" title={item.error}>Failed</span>
                )}
              </span>
              {item.status === 'processing' && (
                <button
                  type="button"
                  onClick={cancelCurrent}
                  className="text-red-500 hover:text-red-600 shrink-0"
                  title="Cancel"
                >
                  <X size={14} />
                </button>
              )}
            </div>
          ))}

          {hasDone && (
            <div className="flex justify-end pt-1">
              <button
                type="button"
                onClick={clearDone}
                className="text-xs text-fg-muted hover:text-fg underline"
              >
                Clear done / failed
              </button>
            </div>
          )}
        </div>
      )}
    </div>
  );
};

// ── Status icon ───────────────────────────────────────────────────────────────

const StatusIcon = ({ status }) => {
  if (status === 'processing') return <Loader2 size={14} className="text-teal animate-spin shrink-0" />;
  if (status === 'done')       return <CheckCircle size={14} className="text-teal shrink-0" />;
  if (status === 'failed')     return <AlertCircle size={14} className="text-red-500 shrink-0" />;
  if (status === 'cancelled')  return <Minus size={14} className="text-fg-muted shrink-0" />;
  return <Clock size={14} className="text-fg-muted shrink-0" />;
};

export default UploadZone;
