"use client";

import { useEffect, useMemo, useState } from "react";
import type { Attachment, FileCategory } from "@slackwsh/contracts";
import { api, ApiError } from "../lib/api";
import { IconClip, IconSearch, IconTrash, IconX } from "./icons";

type SharedFile = Attachment & {
  channelName: string | null;
  channelType: string;
  uploaderName: string;
  messageText: string | null;
};

const FILTERS: Array<{ value: "" | FileCategory; label: string }> = [
  { value: "", label: "All" },
  { value: "image", label: "Images" },
  { value: "video", label: "Video" },
  { value: "audio", label: "Audio" },
  { value: "pdf", label: "PDFs" },
  { value: "document", label: "Documents" },
  { value: "archive", label: "Archives" },
];

function sizeLabel(size: number) {
  if (size < 1024) return `${size} B`;
  if (size < 1024 * 1024) return `${Math.round(size / 1024)} KB`;
  return `${(size / (1024 * 1024)).toFixed(1)} MB`;
}

function FileThumbnail({ file }: { file: SharedFile }) {
  const [url, setUrl] = useState<string | null>(null);
  useEffect(() => {
    if (file.category !== "image" && file.category !== "video") return;
    let cancelled = false;
    api.fileAccess(String(file.workspaceId), file.channelId, file.id)
      .then((result) => { if (!cancelled) setUrl(result.url); })
      .catch(() => undefined);
    return () => { cancelled = true; };
  }, [file]);
  if (file.category === "image" && url) return <img src={url} alt="" />;
  if (file.category === "video" && url) return <video src={url} muted preload="metadata" />;
  return <span className={`files-kind ${file.category}`}><IconClip size={22} /><small>{file.category}</small></span>;
}

export function FilesBrowser({ workspaceId, channelId, compact = false }: { workspaceId: string; channelId?: string | number; compact?: boolean }) {
  const [files, setFiles] = useState<SharedFile[]>([]);
  const [query, setQuery] = useState("");
  const [category, setCategory] = useState<"" | FileCategory>("");
  const [cursor, setCursor] = useState<number | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [gallery, setGallery] = useState<{ file: SharedFile; url: string } | null>(null);
  const imageFiles = useMemo(() => files.filter((file) => file.category === "image"), [files]);

  async function load(append = false, nextCursor?: number | null) {
    setLoading(true);
    setError(null);
    try {
      const result = await api.browseFiles(workspaceId, {
        q: query.trim() || undefined,
        category: category || undefined,
        channelId,
        cursor: append ? nextCursor ?? cursor ?? undefined : undefined,
        limit: compact ? 24 : 36,
      });
      setFiles((current) => append ? [...current, ...result.files] : result.files);
      setCursor(result.nextCursor);
    } catch (err) {
      setError(err instanceof ApiError ? JSON.stringify(err.body) : String(err));
    } finally {
      setLoading(false);
    }
  }

  useEffect(() => {
    const timer = window.setTimeout(() => void load(false), 250);
    return () => window.clearTimeout(timer);
  }, [workspaceId, channelId, query, category]);

  async function open(file: SharedFile) {
    try {
      const { url } = await api.fileAccess(workspaceId, file.channelId, file.id);
      if (file.category === "image") setGallery({ file, url });
      else window.open(url, "_blank", "noopener,noreferrer");
    } catch (err) {
      setError(err instanceof Error ? err.message : String(err));
    }
  }

  async function remove(file: SharedFile) {
    if (!window.confirm(`Delete ${file.name}? This removes it for everyone in the conversation.`)) return;
    try {
      await api.deleteFile(workspaceId, file.channelId, file.id);
      setFiles((current) => current.filter((item) => item.id !== file.id));
      if (gallery?.file.id === file.id) setGallery(null);
    } catch (err) {
      setError(err instanceof ApiError ? JSON.stringify(err.body) : String(err));
    }
  }

  async function moveGallery(delta: number) {
    if (!gallery || imageFiles.length < 2) return;
    const current = imageFiles.findIndex((file) => file.id === gallery.file.id);
    const next = imageFiles[(current + delta + imageFiles.length) % imageFiles.length]!;
    const { url } = await api.fileAccess(workspaceId, next.channelId, next.id);
    setGallery({ file: next, url });
  }

  return (
    <div className={compact ? "files-browser compact" : "files-browser"}>
      <div className="files-toolbar">
        <label className="files-search"><IconSearch size={16} /><input value={query} onChange={(event) => setQuery(event.target.value)} placeholder="Search filename, uploader, or message" /></label>
        <div className="files-filters" role="tablist" aria-label="File type">
          {FILTERS.map((filter) => <button key={filter.value || "all"} type="button" className={category === filter.value ? "active" : ""} onClick={() => setCategory(filter.value)}>{filter.label}</button>)}
        </div>
      </div>
      {error && <p className="error-text">{error}</p>}
      <div className="files-grid">
        {files.map((file) => (
          <article className="files-card" key={file.id}>
            <button className="files-preview" type="button" onClick={() => void open(file)} aria-label={`Open ${file.name}`}><FileThumbnail file={file} /></button>
            <div className="files-card-copy">
              <button type="button" className="files-name" onClick={() => void open(file)}>{file.name}</button>
              <span>{sizeLabel(file.size)} · {file.uploaderName}</span>
              <span>{file.channelType === "private" ? "🔒 " : "#"}{file.channelName ?? "conversation"} · {new Date(file.createdAt).toLocaleDateString()}</span>
            </div>
            <button className="files-delete" type="button" onClick={() => void remove(file)} aria-label={`Delete ${file.name}`} title="Delete file"><IconTrash size={14} /></button>
          </article>
        ))}
      </div>
      {!loading && files.length === 0 && <div className="empty-state">No shared files match this view.</div>}
      {loading && <div className="empty-state">Loading files…</div>}
      {!loading && cursor && <button className="screen-btn files-more" type="button" onClick={() => void load(true, cursor)}>Load more</button>}
      {gallery && (
        <div className="media-gallery-backdrop files-browser-gallery" role="dialog" aria-modal="true" aria-label="Image gallery">
          <section
            className="media-gallery-stage"
            onMouseDown={(event) => event.target === event.currentTarget && setGallery(null)}
          >
            <button className="media-gallery-close" type="button" onClick={() => setGallery(null)} aria-label="Close gallery"><IconX /></button>
            <div className="media-gallery-title">{gallery.file.name}</div>
            {imageFiles.length > 1 && <button className="media-gallery-nav prev" type="button" onClick={() => void moveGallery(-1)} aria-label="Previous image">‹</button>}
            <figure className="media-gallery-figure">
              <img src={gallery.url} alt={gallery.file.name} />
              <figcaption>{gallery.file.name} · {imageFiles.findIndex((file) => file.id === gallery.file.id) + 1} of {imageFiles.length}</figcaption>
            </figure>
            {imageFiles.length > 1 && <button className="media-gallery-nav next" type="button" onClick={() => void moveGallery(1)} aria-label="Next image">›</button>}
          </section>
        </div>
      )}
    </div>
  );
}
