"use client";

import { useState, useMemo } from "react";
import {
  ArrowDown01,
  CloudUpload,
  Grid2X2,
  Image,
  List,
  Loader2,
  Music,
  Video,
} from "lucide-react";
import type { LucideIcon } from "lucide-react";
import { toast } from "sonner";
import { useEditor } from "@/hooks/use-editor";
import { useFileUpload } from "@/hooks/use-file-upload";
import { useRevealItem } from "@/hooks/use-reveal-item";
import { processMediaAssets } from "@/lib/media/processing";
import { cn } from "@/utils/ui";
import { TIMELINE_CONSTANTS } from "@/constants/timeline-constants";
import type { MediaAsset } from "@/types/assets";
import type { CreateTimelineElement } from "@/types/timeline";
import { Button } from "@/components/ui/button";
import { MediaDragOverlay } from "@/components/editor/assets-panel/drag-overlay";
import {
  ContextMenu,
  ContextMenuContent,
  ContextMenuItem,
  ContextMenuTrigger,
} from "@/components/ui/context-menu";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { DraggableItem } from "@/components/editor/draggable-item";
import {
  Tooltip,
  TooltipContent,
  TooltipProvider,
  TooltipTrigger,
} from "@/components/ui/tooltip";
import { useAssetsPanelStore } from "@/stores/assets-panel-store";

export function MediaView() {
  const editor = useEditor();
  const mediaFiles = editor.media.getAssets();
  const activeProject = editor.project.getActive();

  const { mediaViewMode, setMediaViewMode, highlightMediaId, clearHighlight } =
    useAssetsPanelStore();
  const { highlightedId, registerElement } = useRevealItem(
    highlightMediaId,
    clearHighlight,
  );

  const [isProcessing, setIsProcessing] = useState(false);
  const [progress, setProgress] = useState(0);
  const [sortBy, setSortBy] = useState<"name" | "type" | "duration" | "size">(
    "name",
  );
  const [sortOrder, setSortOrder] = useState<"asc" | "desc">("asc");

  const processFiles = async ({ files }: { files: FileList }) => {
    if (!files || files.length === 0) return;
    if (!activeProject) {
      toast.error("No active project");
      return;
    }

    setIsProcessing(true);
    setProgress(0);
    try {
      const processedAssets = await processMediaAssets({
        files,
        onProgress: (progress: { progress: number }) =>
          setProgress(progress.progress),
      });
      for (const asset of processedAssets) {
        await editor.media.addMediaAsset({
          projectId: activeProject.metadata.id,
          asset,
        });
      }
    } catch (error) {
      console.error("Error processing files:", error);
      toast.error("Failed to process files");
    } finally {
      setIsProcessing(false);
      setProgress(0);
    }
  };

  const { isDragOver, dragProps, openFilePicker, fileInputProps } =
    useFileUpload({
      accept: "image/*,video/*,audio/*",
      multiple: true,
      onFilesSelected: (files) => processFiles({ files }),
    });

  const handleRemove = async ({
    event,
    id,
  }: {
    event: React.MouseEvent;
    id: string;
  }) => {
    event.stopPropagation();

    if (!activeProject) {
      toast.error("No active project");
      return;
    }

    await editor.media.removeMediaAsset({
      projectId: activeProject.metadata.id,
      id,
    });
  };

  const addElementAtTime = ({
    asset,
    startTime,
  }: {
    asset: MediaAsset;
    startTime: number;
  }): boolean => {
    const element = createElementFromMedia({ asset, startTime });
    editor.timeline.insertElement({
      element,
      placement: { mode: "auto" },
    });
    return true;
  };

  const filteredMediaItems = useMemo(() => {
    const filtered = mediaFiles.filter((item) => !item.ephemeral);

    filtered.sort((a, b) => {
      let valueA: string | number;
      let valueB: string | number;

      switch (sortBy) {
        case "name":
          valueA = a.name.toLowerCase();
          valueB = b.name.toLowerCase();
          break;
        case "type":
          valueA = a.type;
          valueB = b.type;
          break;
        case "duration":
          valueA = a.duration || 0;
          valueB = b.duration || 0;
          break;
        case "size":
          valueA = a.file.size;
          valueB = b.file.size;
          break;
        default:
          return 0;
      }

      if (valueA < valueB) return sortOrder === "asc" ? -1 : 1;
      if (valueA > valueB) return sortOrder === "asc" ? 1 : -1;
      return 0;
    });

    return filtered;
  }, [mediaFiles, sortBy, sortOrder]);

  const previewComponents = useMemo(() => {
    const previews = new Map<string, React.ReactNode>();

    filteredMediaItems.forEach((item) => {
      previews.set(item.id, <MediaPreview item={item} />);
    });

    return previews;
  }, [filteredMediaItems]);

  const renderPreview = (item: MediaAsset) => previewComponents.get(item.id);

  return (
    <>
      <input {...fileInputProps} />

      <div
        className={`relative flex h-full flex-col gap-1 transition-colors ${isDragOver ? "bg-accent/30" : ""}`}
        {...dragProps}
      >
        <div className="bg-panel p-3 pb-2">
          <div className="flex items-center gap-2">
            <Button
              variant="foreground"
              onClick={openFilePicker}
              disabled={isProcessing}
              className="w-full"
            >
              {isProcessing ? (
                <Loader2 className="animate-spin" />
              ) : (
                <CloudUpload />
              )}
              <span>Upload</span>
            </Button>
            <div className="flex items-center gap-0">
              <TooltipProvider>
                <Tooltip>
                  <TooltipTrigger asChild>
                    <Button
                      size="icon"
                      variant="text"
                      onClick={() =>
                        setMediaViewMode(
                          mediaViewMode === "grid" ? "list" : "grid",
                        )
                      }
                      disabled={isProcessing}
                      className="items-center justify-center"
                    >
                      {mediaViewMode === "grid" ? (
                        <List strokeWidth={1.5} className="!size-[1.05rem]" />
                      ) : (
                        <Grid2X2
                          strokeWidth={1.5}
                          className="!size-[1.05rem]"
                        />
                      )}
                    </Button>
                  </TooltipTrigger>
                  <TooltipContent>
                    <p>
                      {mediaViewMode === "grid"
                        ? "Switch to list view"
                        : "Switch to grid view"}
                    </p>
                  </TooltipContent>
                  <Tooltip>
                    <DropdownMenu>
                      <TooltipTrigger asChild>
                        <DropdownMenuTrigger asChild>
                          <Button
                            size="icon"
                            variant="text"
                            disabled={isProcessing}
                            className="items-center justify-center"
                          >
                            <ArrowDown01
                              strokeWidth={1.5}
                              className="!size-[1.05rem]"
                            />
                          </Button>
                        </DropdownMenuTrigger>
                      </TooltipTrigger>
                      <DropdownMenuContent align="end">
                        <SortMenuItem
                          label="Name"
                          sortKey="name"
                          currentSortBy={sortBy}
                          currentSortOrder={sortOrder}
                          onSort={({ key }) => {
                            if (sortBy === key) {
                              setSortOrder(
                                sortOrder === "asc" ? "desc" : "asc",
                              );
                            } else {
                              setSortBy(key);
                              setSortOrder("asc");
                            }
                          }}
                        />
                        <SortMenuItem
                          label="Type"
                          sortKey="type"
                          currentSortBy={sortBy}
                          currentSortOrder={sortOrder}
                          onSort={({ key }) => {
                            if (sortBy === key) {
                              setSortOrder(
                                sortOrder === "asc" ? "desc" : "asc",
                              );
                            } else {
                              setSortBy(key);
                              setSortOrder("asc");
                            }
                          }}
                        />
                        <SortMenuItem
                          label="Duration"
                          sortKey="duration"
                          currentSortBy={sortBy}
                          currentSortOrder={sortOrder}
                          onSort={({ key }) => {
                            if (sortBy === key) {
                              setSortOrder(
                                sortOrder === "asc" ? "desc" : "asc",
                              );
                            } else {
                              setSortBy(key);
                              setSortOrder("asc");
                            }
                          }}
                        />
                        <SortMenuItem
                          label="File size"
                          sortKey="size"
                          currentSortBy={sortBy}
                          currentSortOrder={sortOrder}
                          onSort={({ key }) => {
                            if (sortBy === key) {
                              setSortOrder(
                                sortOrder === "asc" ? "desc" : "asc",
                              );
                            } else {
                              setSortBy(key);
                              setSortOrder("asc");
                            }
                          }}
                        />
                      </DropdownMenuContent>
                    </DropdownMenu>
                    <TooltipContent>
                      <p>
                        Sort by {sortBy} (
                        {sortOrder === "asc" ? "ascending" : "descending"})
                      </p>
                    </TooltipContent>
                  </Tooltip>
                </Tooltip>
              </TooltipProvider>
            </div>
          </div>
        </div>

        <div className="scrollbar-thin h-full w-full overflow-y-auto pt-1">
          <div className="w-full flex-1 p-3 pt-0">
            {isDragOver || filteredMediaItems.length === 0 ? (
              <MediaDragOverlay
                isVisible={true}
                isProcessing={isProcessing}
                progress={progress}
                onClick={openFilePicker}
                isEmptyState={filteredMediaItems.length === 0 && !isDragOver}
              />
            ) : mediaViewMode === "grid" ? (
              <GridView
                items={filteredMediaItems}
                renderPreview={renderPreview}
                onRemove={handleRemove}
                onAddToTimeline={addElementAtTime}
                highlightedId={highlightedId}
                registerElement={registerElement}
              />
            ) : (
              <ListView
                items={filteredMediaItems}
                renderPreview={renderPreview}
                onRemove={handleRemove}
                onAddToTimeline={addElementAtTime}
                highlightedId={highlightedId}
                registerElement={registerElement}
              />
            )}
          </div>
        </div>
      </div>
    </>
  );
}

function MediaItemWithContextMenu({
  item,
  children,
  onRemove,
}: {
  item: MediaAsset;
  children: React.ReactNode;
  onRemove: ({ event, id }: { event: React.MouseEvent; id: string }) => void;
}) {
  return (
    <ContextMenu>
      <ContextMenuTrigger>{children}</ContextMenuTrigger>
      <ContextMenuContent>
        <ContextMenuItem>Export clips</ContextMenuItem>
        <ContextMenuItem
          variant="destructive"
          onClick={(event) => onRemove({ event, id: item.id })}
        >
          Delete
        </ContextMenuItem>
      </ContextMenuContent>
    </ContextMenu>
  );
}

function GridView({
  items,
  renderPreview,
  onRemove,
  onAddToTimeline,
  highlightedId,
  registerElement,
}: {
  items: MediaAsset[];
  renderPreview: (item: MediaAsset) => React.ReactNode;
  onRemove: ({ event, id }: { event: React.MouseEvent; id: string }) => void;
  onAddToTimeline: ({
    asset,
    startTime,
  }: {
    asset: MediaAsset;
    startTime: number;
  }) => boolean;
  highlightedId: string | null;
  registerElement: (id: string, element: HTMLElement | null) => void;
}) {
  return (
    <div
      className="grid gap-2"
      style={{
        gridTemplateColumns: "repeat(auto-fill, 160px)",
      }}
    >
      {items.map((item) => (
        <div key={item.id} ref={(el) => registerElement(item.id, el)}>
          <MediaItemWithContextMenu item={item} onRemove={onRemove}>
            <DraggableItem
              name={item.name}
              preview={renderPreview(item)}
              dragData={{
                id: item.id,
                type: "media",
                mediaType: item.type,
                name: item.name,
              }}
              shouldShowPlusOnDrag={false}
              onAddToTimeline={({ currentTime }) =>
                onAddToTimeline({ asset: item, startTime: currentTime })
              }
              isRounded={false}
              variant="card"
              isHighlighted={highlightedId === item.id}
            />
          </MediaItemWithContextMenu>
        </div>
      ))}
    </div>
  );
}

function ListView({
  items,
  renderPreview,
  onRemove,
  onAddToTimeline,
  highlightedId,
  registerElement,
}: {
  items: MediaAsset[];
  renderPreview: (item: MediaAsset) => React.ReactNode;
  onRemove: ({ event, id }: { event: React.MouseEvent; id: string }) => void;
  onAddToTimeline: ({
    asset,
    startTime,
  }: {
    asset: MediaAsset;
    startTime: number;
  }) => boolean;
  highlightedId: string | null;
  registerElement: (id: string, element: HTMLElement | null) => void;
}) {
  return (
    <div className="space-y-1">
      {items.map((item) => (
        <div key={item.id} ref={(el) => registerElement(item.id, el)}>
          <MediaItemWithContextMenu item={item} onRemove={onRemove}>
            <DraggableItem
              name={item.name}
              preview={renderPreview(item)}
              dragData={{
                id: item.id,
                type: "media",
                mediaType: item.type,
                name: item.name,
              }}
              shouldShowPlusOnDrag={false}
              onAddToTimeline={({ currentTime }) =>
                onAddToTimeline({ asset: item, startTime: currentTime })
              }
              variant="compact"
              isHighlighted={highlightedId === item.id}
            />
          </MediaItemWithContextMenu>
        </div>
      ))}
    </div>
  );
}

const formatDuration = ({ duration }: { duration: number }) => {
  const min = Math.floor(duration / 60);
  const sec = Math.floor(duration % 60);
  return `${min}:${sec.toString().padStart(2, "0")}`;
};

function MediaDurationBadge({ duration }: { duration?: number }) {
  if (!duration) return null;

  return (
    <div className="absolute right-1 bottom-1 rounded bg-black/70 px-1 text-xs text-white">
      {formatDuration({ duration })}
    </div>
  );
}

function MediaDurationLabel({ duration }: { duration?: number }) {
  if (!duration) return null;

  return (
    <span className="text-xs opacity-70">{formatDuration({ duration })}</span>
  );
}

function MediaTypePlaceholder({
  icon: Icon,
  label,
  duration,
  variant,
}: {
  icon: LucideIcon;
  label: string;
  duration?: number;
  variant: "muted" | "bordered";
}) {
  const iconClassName = cn("size-6", variant === "bordered" && "mb-1");

  return (
    <div
      className={cn(
        "text-muted-foreground flex size-full flex-col items-center justify-center rounded",
        variant === "muted" ? "bg-muted/30" : "border",
      )}
    >
      <Icon className={iconClassName} aria-hidden="true" />
      <span className="text-xs">{label}</span>
      <MediaDurationLabel duration={duration} />
    </div>
  );
}

function MediaPreview({ item }: { item: MediaAsset }) {
  if (item.type === "image") {
    return (
      <div className="flex size-full items-center justify-center">
        <img
          src={item.url}
          alt={item.name}
          className="max-h-full w-full object-cover"
          loading="lazy"
        />
      </div>
    );
  }

  if (item.type === "video") {
    if (item.thumbnailUrl) {
      return (
        <div className="relative size-full">
          <img
            src={item.thumbnailUrl}
            alt={item.name}
            className="size-full rounded object-cover"
            loading="lazy"
          />
          <div className="absolute inset-0 flex items-center justify-center rounded bg-black/20">
            <Video className="size-6 text-white drop-shadow-md" />
          </div>
          <MediaDurationBadge duration={item.duration} />
        </div>
      );
    }

    return (
      <MediaTypePlaceholder
        icon={Video}
        label="Video"
        duration={item.duration}
        variant="muted"
      />
    );
  }

  if (item.type === "audio") {
    return (
      <MediaTypePlaceholder
        icon={Music}
        label="Audio"
        duration={item.duration}
        variant="bordered"
      />
    );
  }

  return <MediaTypePlaceholder icon={Image} label="Unknown" variant="muted" />;
}

function SortMenuItem({
  label,
  sortKey,
  currentSortBy,
  currentSortOrder,
  onSort,
}: {
  label: string;
  sortKey: "name" | "type" | "duration" | "size";
  currentSortBy: string;
  currentSortOrder: "asc" | "desc";
  onSort: ({ key }: { key: "name" | "type" | "duration" | "size" }) => void;
}) {
  const isActive = currentSortBy === sortKey;
  const arrow = isActive ? (currentSortOrder === "asc" ? "↑" : "↓") : "";

  return (
    <DropdownMenuItem onClick={() => onSort({ key: sortKey })}>
      {label} {arrow}
    </DropdownMenuItem>
  );
}

function createElementFromMedia({
  asset,
  startTime,
}: {
  asset: MediaAsset;
  startTime: number;
}): CreateTimelineElement {
  const duration =
    asset.duration ?? TIMELINE_CONSTANTS.DEFAULT_ELEMENT_DURATION;

  switch (asset.type) {
    case "video":
      return {
        type: "video",
        name: asset.name,
        mediaId: asset.id,
        startTime,
        duration,
        trimStart: 0,
        trimEnd: 0,
        muted: false,
        hidden: false,
        transform: { scale: 1, position: { x: 0, y: 0 }, rotate: 0 },
        opacity: 1,
      };
    case "image":
      return {
        type: "image",
        name: asset.name,
        mediaId: asset.id,
        startTime,
        duration,
        trimStart: 0,
        trimEnd: 0,
        hidden: false,
        transform: { scale: 1, position: { x: 0, y: 0 }, rotate: 0 },
        opacity: 1,
      };
    case "audio":
      return {
        type: "audio",
        sourceType: "upload",
        name: asset.name,
        mediaId: asset.id,
        startTime,
        duration,
        trimStart: 0,
        trimEnd: 0,
        volume: 1,
        muted: false,
      };
    default:
      throw new Error(`Unsupported media type: ${asset.type}`);
  }
}
