/**
 * AttachmentUploader - multi-file drop zone
 *
 * Extends the hand-rolled single-file zone at
 * `pages/student/Assignments.tsx:612-640` with:
 *   - multi-file input
 *   - HTML5 drag-and-drop
 *   - mime allowlist matching the backend controller
 *   - 20 MB per-file / 10 files per-batch caps
 *   - inline "Uploading X of Y…" progress
 *   - optional caption field
 *
 * On success: calls `onUploaded(rows)` so the parent can refresh
 * its own list (e.g. a table re-fetch). The store's cache is also
 * refreshed internally.
 */

import { useCallback, useRef, useState } from 'react';
import {
  Upload, X, FileText, AlertCircle, Loader2, CheckCircle2, File as FileIcon,
} from 'lucide-react';
import { useAttachmentStore } from '../store/attachments';
import type { Attachment } from '../store/attachments';
import { ProgressBar } from './ui/ProgressBar';

interface AttachmentUploaderProps {
  parent_kind: string;
  parent_id: number;
  onUploaded?: (rows: Attachment[]) => void;
  allowCaption?: boolean;
  maxFiles?: number;
  maxBytesPerFile?: number;
  className?: string;
  compact?: boolean;
  captionPrefix?: string; // Prepend prefix to captions
}

// Mirrors server/src/controllers/attachments.controller.js:34-47
const ALLOWED_MIMES: Record<string, string> = {
  'application/pdf': 'PDF',
  'application/msword': 'DOC',
  'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'DOCX',
  'application/vnd.ms-excel': 'XLS',
  'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'XLSX',
  'application/vnd.ms-powerpoint': 'PPT',
  'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'PPTX',
  'image/png': 'PNG',
  'image/jpeg': 'JPG',
  'image/gif': 'GIF',
  'application/zip': 'ZIP',
  'text/plain': 'TXT',
  'video/mp4': 'MP4',
  'video/webm': 'WEBM',
  'video/ogg': 'OGV',
  'video/quicktime': 'MOV',
  'audio/mpeg': 'MP3',
  'audio/wav': 'WAV',
  'audio/ogg': 'OGA',
  'audio/mp4': 'M4A',
  'audio/x-m4a': 'M4A',
};

const EXT_TO_MIME: Record<string, string> = {
  pdf: 'PDF',
  doc: 'DOC',
  docx: 'DOCX',
  xls: 'XLS',
  xlsx: 'XLSX',
  ppt: 'PPT',
  pptx: 'PPTX',
  png: 'PNG',
  jpg: 'JPG',
  jpeg: 'JPG',
  gif: 'GIF',
  zip: 'ZIP',
  txt: 'TXT',
  mp4: 'MP4',
  webm: 'WEBM',
  ogv: 'OGV',
  mov: 'MOV',
  mp3: 'MP3',
  wav: 'WAV',
  ogg: 'OGA',
  m4a: 'M4A',
};

const DEFAULT_MAX_FILES = 10;
const DEFAULT_MAX_BYTES = 20 * 1024 * 1024; // 20 MB

function humanList(values: string[]): string {
  if (values.length === 0) return '';
  if (values.length === 1) return values[0];
  if (values.length === 2) return `${values[0]} & ${values[1]}`;
  return `${values.slice(0, -1).join(', ')} & ${values[values.length - 1]}`;
}

function formatSize(bytes: number): string {
  if (bytes < 1024) return `${bytes} B`;
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
}

export function AttachmentUploader({
  parent_kind,
  parent_id,
  onUploaded,
  allowCaption = false,
  maxFiles = DEFAULT_MAX_FILES,
  maxBytesPerFile = DEFAULT_MAX_BYTES,
  className = '',
  compact = false,
  captionPrefix,
}: AttachmentUploaderProps) {
  const { upload, uploading, uploadProgress, error, clearError } = useAttachmentStore();
  const [pending, setPending] = useState<File[]>([]);
  const [caption, setCaption] = useState('');
  const [validationError, setValidationError] = useState<string | null>(null);
  const [success, setSuccess] = useState<string | null>(null);
  const [dragOver, setDragOver] = useState(false);
  const inputRef = useRef<HTMLInputElement>(null);

  const allowedExt = humanList(Object.values(ALLOWED_MIMES));

  const validate = (files: File[]): { ok: true; files: File[] } | { ok: false; message: string } => {
    if (files.length === 0) return { ok: false, message: 'No files selected.' };
    if (pending.length + files.length > maxFiles) {
      return { ok: false, message: `Too many files — max ${maxFiles} per batch (already have ${pending.length}, adding ${files.length}).` };
    }
    for (const f of files) {
      if (f.size > maxBytesPerFile) {
        return { ok: false, message: `${f.name} is ${formatSize(f.size)} — max ${formatSize(maxBytesPerFile)} per file.` };
      }
      const ext = f.name.split('.').pop()?.toLowerCase();
      const isMimeAllowed = ALLOWED_MIMES[f.type] || (ext && EXT_TO_MIME[ext]);
      if (!isMimeAllowed) {
        return {
          ok: false,
          message: `${f.name} is not a supported type. Allowed: ${allowedExt}.`,
        };
      }
    }
    return { ok: true, files };
  };

  const addFiles = useCallback((incoming: File[]) => {
    setValidationError(null);
    setSuccess(null);
    const v = validate(incoming);
    if (!v.ok) {
      setValidationError(v.message);
      return;
    }
    setPending(prev => [...prev, ...v.files]);
  }, [maxFiles, maxBytesPerFile]);

  const onInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const files = Array.from(e.target.files || []);
    addFiles(files);
    // Reset the input so picking the same file again re-fires onChange
    if (inputRef.current) inputRef.current.value = '';
  };

  const onDrop = (e: React.DragEvent<HTMLDivElement>) => {
    e.preventDefault();
    setDragOver(false);
    if (uploading) return;
    const files = Array.from(e.dataTransfer.files || []);
    addFiles(files);
  };

  const onDragOver = (e: React.DragEvent<HTMLDivElement>) => {
    e.preventDefault();
    if (!uploading) setDragOver(true);
  };

  const onDragLeave = () => setDragOver(false);

  const removePending = (idx: number) => {
    setPending(prev => prev.filter((_, i) => i !== idx));
  };

  const handleUpload = async () => {
    if (pending.length === 0 || uploading) return;
    setValidationError(null);
    setSuccess(null);
    try {
      const finalCaption = captionPrefix
        ? `${captionPrefix}:${caption.trim()}`
        : caption.trim();
      const created = await upload(parent_kind, parent_id, pending, finalCaption || undefined);
      setPending([]);
      setCaption('');
      setSuccess(`${created.length} file${created.length === 1 ? '' : 's'} uploaded.`);
      if (onUploaded) onUploaded(created);
    } catch {
      // store surfaces the error via `error`
    }
  };

  return (
    <div
      className={`bg-card border border-border rounded-2xl ${className}`}
      aria-busy={uploading}
    >
      {allowCaption && (
        <div className="p-4 border-b border-border">
          <label
            htmlFor="attachment-caption"
            className="block text-[0.65rem] font-black uppercase tracking-[3px] text-text-muted mb-1.5"
          >
            Caption (optional)
          </label>
          <input
            id="attachment-caption"
            type="text"
            value={caption}
            onChange={e => setCaption(e.target.value)}
            disabled={uploading}
            placeholder="e.g. Marked Script — June Test"
            maxLength={140}
            className="w-full px-4 py-2.5 bg-muted/50 border border-border rounded-xl text-sm font-medium focus:ring-4 focus:ring-primary/5 focus:bg-card focus:border-primary transition-all placeholder:text-text-muted/40"
          />
        </div>
      )}

      <div
        role="button"
        tabIndex={0}
        onClick={() => !uploading && inputRef.current?.click()}
        onKeyDown={(e) => {
          if (!uploading && (e.key === 'Enter' || e.key === ' ')) {
            e.preventDefault();
            inputRef.current?.click();
          }
        }}
        onDrop={onDrop}
        onDragOver={onDragOver}
        onDragLeave={onDragLeave}
        className={`${compact ? 'p-5' : 'p-8'} text-center transition-all cursor-pointer group ${
          dragOver ? 'bg-blue-50/40' : 'hover:bg-blue-50/10'
        } ${uploading ? 'opacity-60 pointer-events-none' : ''}`}
        style={{
          borderTop: allowCaption ? 'none' : undefined,
        }}
      >
        <input
          ref={inputRef}
          type="file"
          multiple
          onChange={onInputChange}
          disabled={uploading}
          accept={Object.keys(ALLOWED_MIMES).join(',')}
          className="hidden"
        />
        <div className="flex flex-col items-center gap-2">
          {uploading ? (
            <div className="w-full max-w-md px-4 py-2">
              <ProgressBar
                value={uploadProgress?.percent ?? (uploadProgress && uploadProgress.total > 0 ? Math.round((uploadProgress.current / uploadProgress.total) * 100) : 50)}
                label={
                  uploadProgress
                    ? `Uploading file ${uploadProgress.current} of ${uploadProgress.total}`
                    : 'Uploading attachments...'
                }
                sublabel={uploadProgress?.fileName ? `Processing ${uploadProgress.fileName}` : undefined}
                showValue
                aria-label="Attachment upload progress"
              />
            </div>
          ) : (
            <>
              <Upload className={`text-text-muted/30 group-hover:text-primary transition-colors ${compact ? '' : ''}`} size={compact ? 22 : 32} />
              <p className="text-sm font-bold text-text-muted group-hover:text-text transition-colors">
                {dragOver
                  ? 'Drop to add'
                  : 'Tap to select files or drag them here'}
              </p>
              {!compact && (
                <p className="text-[0.6rem] font-bold text-text-muted/50 uppercase tracking-widest">
                  Max {maxFiles} files · {formatSize(maxBytesPerFile)} each · {allowedExt}
                </p>
              )}
            </>
          )}
        </div>
      </div>

      {(validationError || error) && (
        <div className="flex items-start gap-2 p-3 mx-3 mb-3 text-sm rounded-xl bg-rose-50 border border-rose-200 text-rose-700">
          <AlertCircle size={16} className="mt-0.5 shrink-0" />
          <div className="flex-1 break-words">{validationError || error}</div>
          <button
            type="button"
            onClick={() => { setValidationError(null); clearError(); }}
            className="text-rose-500 hover:text-rose-700 text-xs font-bold uppercase tracking-widest"
          >
            Dismiss
          </button>
        </div>
      )}

      {success && (
        <div className="flex items-start gap-2 p-3 mx-3 mb-3 text-sm rounded-xl bg-emerald-50 border border-emerald-200 text-emerald-700">
          <CheckCircle2 size={16} className="mt-0.5 shrink-0" />
          <div className="flex-1">{success}</div>
        </div>
      )}

      {pending.length > 0 && (
        <div className="border-t border-border p-3 space-y-2">
          <ul className="space-y-1.5 max-h-48 overflow-y-auto custom-scrollbar">
            {pending.map((f, idx) => (
              <li
                key={`${f.name}-${idx}`}
                className="flex items-center gap-2 px-3 py-2 rounded-xl bg-muted/50"
              >
                <FileIcon size={16} className="text-text-muted shrink-0" />
                <div className="min-w-0 flex-1">
                  <div className="text-sm font-semibold text-text truncate">{f.name}</div>
                  <div className="text-[0.7rem] text-text-muted font-medium">{formatSize(f.size)} · {ALLOWED_MIMES[f.type] || f.type || 'unknown'}</div>
                </div>
                <button
                  type="button"
                  onClick={() => removePending(idx)}
                  disabled={uploading}
                  className="p-1 rounded-lg text-text-muted hover:text-rose-500 hover:bg-rose-50 transition-colors disabled:opacity-50"
                  title="Remove from batch"
                >
                  <X size={14} />
                </button>
              </li>
            ))}
          </ul>
          <div className="flex items-center justify-end gap-2 pt-1">
            <button
              type="button"
              onClick={() => { setPending([]); setValidationError(null); }}
              disabled={uploading}
              className="px-4 py-2 text-xs font-bold uppercase tracking-widest text-text-muted hover:text-text rounded-xl transition-colors disabled:opacity-50"
            >
              Clear
            </button>
            <button
              type="button"
              onClick={handleUpload}
              disabled={uploading}
              className="px-5 py-2 bg-primary hover:bg-primary-dark text-white rounded-xl text-xs font-black uppercase tracking-widest transition-all shadow-md active:scale-95 disabled:opacity-50 disabled:grayscale flex items-center gap-2"
            >
              {uploading ? <Loader2 size={14} className="animate-spin" /> : <FileText size={14} />}
              Upload {pending.length}
            </button>
          </div>
        </div>
      )}
    </div>
  );
}

export default AttachmentUploader;
