"use client";

import clsx from "clsx";
import { useRef, useState } from "react";
import { FileAudio, FileText, FileVideo, Loader2, UploadCloud, X } from "lucide-react";
import { uploadWithProgress, ApiError } from "@/lib/api";
import type { BotAsset, MediaType } from "@/lib/types";

export const ACCEPT: Record<MediaType, string> = {
  image: "image/jpeg,image/png",
  video: "video/mp4,video/3gpp",
  audio: "audio/aac,audio/amr,audio/mpeg,audio/mp4,audio/ogg",
  document: ".pdf,.txt,.doc,.docx,.xls,.xlsx,.ppt,.pptx",
};
export const KIND_HINT: Record<MediaType, string> = {
  image: "JPEG or PNG, up to 5 MB",
  video: "MP4 or 3GP (H.264 video, AAC audio), up to 16 MB",
  audio: "AAC, AMR, MP3, M4A or OGG (Opus), up to 16 MB",
  document: "PDF, Word, Excel, PowerPoint or text, up to 100 MB",
};

export const formatSize = (n: number) => (n < 1024 * 1024 ? `${Math.max(1, Math.round(n / 1024))} KB` : `${(n / 1024 / 1024).toFixed(1)} MB`);

/** Uploads a file for a bot reply or flow step, with progress and WhatsApp's type and size rules. */
export function AssetUpload({ kind, asset, onChange, error, id }: { kind: MediaType; asset: BotAsset | null; onChange: (a: BotAsset | null) => void; error?: string; id?: string }) {
  const input = useRef<HTMLInputElement>(null);
  const abort = useRef<AbortController | null>(null);
  const [progress, setProgress] = useState<{ name: string; value: number } | null>(null);
  const [localError, setLocalError] = useState<string | null>(null);
  const [dragging, setDragging] = useState(false);

  async function send(file: File) {
    setLocalError(null);
    const form = new FormData();
    form.append("kind", kind);
    form.append("file", file);
    abort.current = new AbortController();
    setProgress({ name: file.name, value: 0 });
    try {
      const r = await uploadWithProgress<{ data: BotAsset }>("/bot-assets", form, (v) => setProgress({ name: file.name, value: v }), abort.current.signal);
      onChange(r.data);
    } catch (e) {
      if (!(e instanceof ApiError && e.code === "aborted")) setLocalError((e as Error).message);
    } finally {
      setProgress(null);
      abort.current = null;
    }
  }

  const shown = localError ?? error;
  const Icon = kind === "video" ? FileVideo : kind === "audio" ? FileAudio : FileText;

  if (asset && !progress) {
    return (
      <div>
        <div className="flex items-center gap-3 rounded-lg border border-teal/30 bg-teal-soft/60 p-2.5">
          {asset.kind === "image" ? (
            // eslint-disable-next-line @next/next/no-img-element
            <img src={asset.url} alt="" className="size-12 shrink-0 rounded object-cover" />
          ) : (
            <span className="grid size-12 shrink-0 place-items-center rounded bg-white text-teal">
              <Icon className="size-5" />
            </span>
          )}
          <div className="min-w-0 flex-1">
            <p className="truncate text-sm font-medium">{asset.filename}</p>
            <p className="text-xs text-muted">
              {formatSize(asset.size)} <span className="text-teal">Uploaded</span>
            </p>
          </div>
          <button type="button" onClick={() => onChange(null)} className="rounded p-1.5 text-muted hover:bg-white hover:text-brick" aria-label={`Remove ${asset.filename}`}>
            <X className="size-4" />
          </button>
        </div>
        {shown && <p className="mt-1.5 text-[13px] text-brick">{shown}</p>}
      </div>
    );
  }

  return (
    <div>
      <div
        onDragOver={(e) => {
          e.preventDefault();
          setDragging(true);
        }}
        onDragLeave={() => setDragging(false)}
        onDrop={(e) => {
          e.preventDefault();
          setDragging(false);
          const f = e.dataTransfer.files?.[0];
          if (f) send(f);
        }}
        className={clsx("relative rounded-lg border-2 border-dashed px-4 py-5 text-center transition-colors", shown ? "border-brick/50" : dragging ? "border-teal bg-teal-soft/40" : "border-line")}
      >
        {progress ? (
          <div className="space-y-2" aria-live="polite">
            <div className="flex items-center justify-between gap-3 text-sm">
              <span className="flex min-w-0 items-center gap-2">
                <Loader2 className="size-4 shrink-0 animate-spin text-teal" />
                <span className="truncate">{progress.name}</span>
              </span>
              <span className="tabular text-muted">{Math.round(progress.value * 100)}%</span>
            </div>
            <div className="h-1.5 overflow-hidden rounded-full bg-paper">
              <div className="h-full bg-teal transition-[width]" style={{ width: `${progress.value * 100}%` }} role="progressbar" aria-valuenow={Math.round(progress.value * 100)} aria-valuemin={0} aria-valuemax={100} />
            </div>
            <button type="button" onClick={() => abort.current?.abort()} className="text-[13px] font-medium text-muted hover:text-brick">
              Cancel
            </button>
          </div>
        ) : (
          <>
            <UploadCloud className="mx-auto size-6 text-muted" />
            <p className="mt-1.5 text-sm">
              <button type="button" onClick={() => input.current?.click()} className="font-medium text-teal hover:underline" id={id}>
                Choose {kind === "image" ? "an image" : `a ${kind}`}
              </button>{" "}
              or drop it here
            </p>
            <p className="mt-0.5 text-xs text-muted">{KIND_HINT[kind]}</p>
          </>
        )}
        <input
          ref={input}
          type="file"
          accept={ACCEPT[kind]}
          className="sr-only"
          tabIndex={-1}
          aria-label={`Upload ${kind}`}
          onChange={(e) => {
            const f = e.target.files?.[0];
            e.target.value = "";
            if (f) send(f);
          }}
        />
      </div>
      {shown && <p className="mt-1.5 text-[13px] text-brick">{shown}</p>}
    </div>
  );
}
