"use client";

import { useState, useCallback } from "react";
import { Upload, X, Image as ImageIcon, Film, Scissors } from "lucide-react";
import { CropModal } from "./CropModal";

const ACCEPTED_IMAGE_TYPES = ["image/jpeg", "image/png", "image/gif", "image/webp"];
const ACCEPTED_VIDEO_TYPES = ["video/mp4", "video/quicktime"];
const ACCEPTED_TYPES = [...ACCEPTED_IMAGE_TYPES, ...ACCEPTED_VIDEO_TYPES];
const MAX_IMAGE_SIZE = 5 * 1024 * 1024; // 5MB
const MAX_VIDEO_SIZE = 512 * 1024 * 1024; // 512MB
const MAX_IMAGES = 4;
const MAX_VIDEOS = 1;
const MAX_VIDEO_DURATION = 140; // seconds

/** Aspect ratios outside this range trigger the auto-suggest banner */
const AR_MIN = 0.8;
const AR_MAX = 2.5;

interface MediaUploadProps {
  files: File[];
  onChange: (files: File[]) => void;
}

function isImageFile(f: File) {
  return ACCEPTED_IMAGE_TYPES.includes(f.type);
}

function isVideoFile(f: File) {
  return ACCEPTED_VIDEO_TYPES.includes(f.type);
}

function getVideoDuration(file: File): Promise<number> {
  return new Promise((resolve, reject) => {
    const video = document.createElement("video");
    video.preload = "metadata";
    video.onloadedmetadata = () => {
      URL.revokeObjectURL(video.src);
      resolve(video.duration);
    };
    video.onerror = () => reject(new Error("Could not read video metadata"));
    video.src = URL.createObjectURL(file);
  });
}

/** Check the natural aspect ratio of an image file */
function getImageAspectRatio(file: File): Promise<number> {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.onload = () => {
      URL.revokeObjectURL(img.src);
      resolve(img.naturalWidth / img.naturalHeight);
    };
    img.onerror = () => reject(new Error("Could not read image"));
    img.src = URL.createObjectURL(file);
  });
}

export function MediaUpload({ files, onChange }: MediaUploadProps) {
  const [isDragging, setIsDragging] = useState(false);
  const [error, setError] = useState("");
  const [cropIndex, setCropIndex] = useState<number | null>(null);
  const [cropDefaultPreset, setCropDefaultPreset] = useState(0);
  const [suggestCropIndex, setSuggestCropIndex] = useState<number | null>(null);

  const existingImages = files.filter(isImageFile);
  const existingVideos = files.filter(isVideoFile);
  const hasVideo = existingVideos.length > 0;
  const hasImages = existingImages.length > 0;

  const validateAndAdd = useCallback(
    async (newFiles: File[]) => {
      setError("");
      const toAdd: File[] = [];

      for (const file of newFiles) {
        // Type check
        if (!ACCEPTED_TYPES.includes(file.type)) {
          const ext = file.name.split(".").pop()?.toUpperCase() ?? "unknown";
          if (file.type === "image/heic" || file.name.endsWith(".heic")) {
            setError("HEIC not supported. Convert to JPG first.");
          } else {
            setError(`Unsupported file type (${ext}). Use JPG, PNG, GIF, WEBP, MP4, or MOV.`);
          }
          return;
        }

        const isVideo = isVideoFile(file);
        const isImage = isImageFile(file);

        // Size check
        if (isImage && file.size > MAX_IMAGE_SIZE) {
          setError(`Image too large (${(file.size / 1024 / 1024).toFixed(1)}MB). Twitter max is 5MB per image.`);
          return;
        }
        if (isVideo && file.size > MAX_VIDEO_SIZE) {
          setError(`Video too large (${(file.size / 1024 / 1024).toFixed(0)}MB). Twitter max is 512MB.`);
          return;
        }

        // Mixing check
        if (isVideo && (hasImages || toAdd.some(isImageFile))) {
          setError("Cannot mix video and images in a single tweet.");
          return;
        }
        if (isImage && (hasVideo || toAdd.some(isVideoFile))) {
          setError("Cannot mix video and images in a single tweet.");
          return;
        }

        // Count check
        if (isImage) {
          const totalImages = existingImages.length + toAdd.filter(isImageFile).length + 1;
          if (totalImages > MAX_IMAGES) {
            setError(`Twitter allows max ${MAX_IMAGES} images per post.`);
            return;
          }
        }
        if (isVideo) {
          const totalVideos = existingVideos.length + toAdd.filter(isVideoFile).length + 1;
          if (totalVideos > MAX_VIDEOS) {
            setError("Twitter allows only 1 video per post.");
            return;
          }
        }

        // Video duration check (client-side)
        if (isVideo) {
          try {
            const duration = await getVideoDuration(file);
            if (duration > MAX_VIDEO_DURATION) {
              const mins = Math.floor(duration / 60);
              const secs = Math.floor(duration % 60);
              setError(`Video too long (${mins}:${String(secs).padStart(2, "0")}). Twitter max is 2:20 (140 seconds).`);
              return;
            }
          } catch {
            // Allow if we can't check duration
          }
        }

        toAdd.push(file);
      }

      if (toAdd.length > 0) {
        const nextFiles = [...files, ...toAdd];
        onChange(nextFiles);

        // Auto-suggest crop for images with unusual aspect ratios
        for (const file of toAdd) {
          if (isImageFile(file)) {
            try {
              const ar = await getImageAspectRatio(file);
              if (ar < AR_MIN || ar > AR_MAX) {
                const idx = nextFiles.indexOf(file);
                if (idx !== -1) {
                  setSuggestCropIndex(idx);
                }
                break; // Only suggest for the first odd-ratio image
              }
            } catch {
              // Ignore if we can't read the image
            }
          }
        }
      }
    },
    [files, onChange, existingImages.length, existingVideos.length, hasVideo, hasImages]
  );

  const handleDrop = useCallback(
    (e: React.DragEvent) => {
      e.preventDefault();
      setIsDragging(false);
      const dropped = Array.from(e.dataTransfer.files);
      validateAndAdd(dropped);
    },
    [validateAndAdd]
  );

  const handleFileInput = (e: React.ChangeEvent<HTMLInputElement>) => {
    if (e.target.files) {
      validateAndAdd(Array.from(e.target.files));
      e.target.value = "";
    }
  };

  const removeFile = (index: number) => {
    setError("");
    if (suggestCropIndex === index) setSuggestCropIndex(null);
    onChange(files.filter((_, i) => i !== index));
  };

  const openCrop = (index: number, presetIndex = 0) => {
    setCropDefaultPreset(presetIndex);
    setCropIndex(index);
  };

  const handleCropApply = (croppedFile: File) => {
    if (cropIndex === null) return;
    const updated = [...files];
    updated[cropIndex] = croppedFile;
    onChange(updated);
    setCropIndex(null);
    if (suggestCropIndex === cropIndex) setSuggestCropIndex(null);
  };

  const atImageLimit = hasImages && existingImages.length >= MAX_IMAGES;
  const atVideoLimit = hasVideo;
  const uploadDisabled = atImageLimit || atVideoLimit;

  return (
    <div className="space-y-2">
      <div
        onDragOver={(e) => { e.preventDefault(); setIsDragging(true); }}
        onDragLeave={() => setIsDragging(false)}
        onDrop={handleDrop}
        className={`flex flex-col items-center justify-center rounded-xl border-2 border-dashed p-6 min-h-[120px] transition-colors cursor-pointer ${
          uploadDisabled
            ? "border-gray-100 bg-gray-50 opacity-60 cursor-default"
            : isDragging
              ? "border-blue-400 bg-blue-50"
              : "border-gray-200 hover:border-gray-300 hover:bg-gray-50"
        }`}
      >
        <Upload className={`mb-2 h-6 w-6 ${isDragging ? "text-blue-500" : "text-gray-400"}`} />
        <p className="text-sm text-gray-500 text-center">
          {uploadDisabled ? (
            hasVideo ? "Video attached (1 max)" : `${existingImages.length}/${MAX_IMAGES} images attached`
          ) : (
            <>
              Drop files here, or{" "}
              <label className="cursor-pointer text-blue-600 hover:text-blue-700 font-medium">
                browse
                <input
                  type="file"
                  className="hidden"
                  multiple
                  accept={ACCEPTED_TYPES.join(",")}
                  onChange={handleFileInput}
                />
              </label>
            </>
          )}
        </p>
        <p className="text-xs text-gray-400 mt-1">
          JPG, PNG, GIF, WEBP (5MB) · MP4, MOV (512MB, 2:20 max) · Max 4 images or 1 video
        </p>
      </div>

      {error && (
        <div className="rounded-md bg-red-50 border border-red-200 px-3 py-2 text-xs text-red-700">
          {error}
        </div>
      )}

      {/* Auto-suggest crop banner */}
      {suggestCropIndex !== null && files[suggestCropIndex] && (
        <div className="flex items-center gap-2 rounded-md bg-amber-50 border border-amber-200 px-3 py-2 text-xs text-amber-800">
          <span>This image may not display well on Twitter.</span>
          <button
            type="button"
            onClick={() => openCrop(suggestCropIndex, 0)}
            className="font-medium text-amber-900 underline hover:text-amber-700"
          >
            Crop it?
          </button>
          <button
            type="button"
            onClick={() => setSuggestCropIndex(null)}
            className="ml-auto text-amber-400 hover:text-amber-600"
          >
            <X className="h-3.5 w-3.5" />
          </button>
        </div>
      )}

      {files.length > 0 && (
        <div className="grid grid-cols-2 sm:grid-cols-4 gap-2">
          {files.map((file, i) => (
            <div key={i} className="relative group rounded-lg border overflow-hidden aspect-square">
              {file.type.startsWith("image/") ? (
                // eslint-disable-next-line @next/next/no-img-element
                <img
                  src={URL.createObjectURL(file)}
                  alt={file.name}
                  className="h-full w-full object-cover"
                />
              ) : (
                <div className="flex h-full w-full items-center justify-center bg-gray-100">
                  <Film className="h-6 w-6 text-gray-400" />
                  <span className="absolute bottom-1 text-[9px] text-gray-500 font-medium">VIDEO</span>
                </div>
              )}
              {/* Action buttons overlay */}
              <div className="absolute top-0.5 right-0.5 flex gap-0.5 opacity-100 sm:opacity-0 sm:group-hover:opacity-100 transition-opacity">
                {file.type.startsWith("image/") && (
                  <button
                    type="button"
                    onClick={() => openCrop(i)}
                    className="rounded-full bg-black/60 p-0.5 text-white hover:bg-black/80"
                    title="Crop image"
                  >
                    <Scissors className="h-3 w-3" />
                  </button>
                )}
                <button
                  type="button"
                  onClick={() => removeFile(i)}
                  className="rounded-full bg-black/60 p-0.5 text-white hover:bg-black/80"
                >
                  <X className="h-3 w-3" />
                </button>
              </div>
            </div>
          ))}
        </div>
      )}

      {/* Crop modal */}
      {cropIndex !== null && files[cropIndex] && (
        <CropModal
          file={files[cropIndex]}
          defaultPresetIndex={cropDefaultPreset}
          onApply={handleCropApply}
          onCancel={() => setCropIndex(null)}
        />
      )}
    </div>
  );
}
