import { zodResolver } from "@hookform/resolvers/zod";
import { Eye, ImagePlus, Save, Send, Trash2, UploadCloud } from "lucide-react";
import { useEffect, useMemo, useRef, useState } from "react";
import { useMutation, useQuery } from "@tanstack/react-query";
import { useForm } from "react-hook-form";
import { useNavigate } from "react-router-dom";
import { z } from "zod";
import { contentApi, directoriesApi } from "../../shared/api/endpoints";
import { toList } from "../../shared/api/normalize";
import { queryKeys } from "../../shared/api/queryKeys";
import { Button } from "../../shared/ui/Button";
import { Input, Select, Textarea } from "../../shared/ui/Field";
import { ErrorState, Skeleton } from "../../shared/ui/States";
import { cn } from "../../shared/lib/cn";

const schema = z.object({
  title: z.string().min(10, "Минимум 10 символов"),
  excerpt: z.string().min(30, "Минимум 30 символов"),
  content: z.string().min(80, "Добавьте более подробный текст"),
  category: z.string().min(1, "Выберите категорию"),
  type: z.string().min(1, "Выберите тип"),
  tags: z.string().optional(),
});

const materialTypes = [
  ["news", "Новость"],
  ["article", "Статья"],
  ["research", "Исследование"],
  ["announcement", "Объявление"],
  ["video", "Видео"],
];

function formatFileSize(size) {
  if (size < 1024 * 1024) return `${Math.round(size / 1024)} КБ`;
  return `${(size / 1024 / 1024).toFixed(1)} МБ`;
}

function normalizeCategories(payload) {
  return toList(payload).map((item) => {
    if (typeof item === "string") {
      return { id: item, label: item, value: item };
    }

    return {
      id: item.id ?? item.slug ?? item.name,
      label: item.name ?? item.title ?? item.label,
      value: item.slug ?? item.id ?? item.name,
    };
  });
}

function makePayload(data, photos, status) {
  const tags = data.tags
    ? data.tags.split(",").map((tag) => tag.trim()).filter(Boolean)
    : [];
  const payload = { ...data, tags, status };

  if (photos.length === 0) return payload;

  const formData = new FormData();
  Object.entries(payload).forEach(([key, value]) => {
    if (Array.isArray(value)) {
      value.forEach((item) => formData.append(`${key}[]`, item));
    } else {
      formData.append(key, value ?? "");
    }
  });
  photos.forEach((file) => formData.append("photos", file));
  return formData;
}

export function MaterialFormPage() {
  const [notice, setNotice] = useState("");
  const [dragActive, setDragActive] = useState(false);
  const [photos, setPhotos] = useState([]);
  const [submitStatus, setSubmitStatus] = useState("moderation");
  const inputRef = useRef(null);
  const navigate = useNavigate();
  const categoriesQuery = useQuery({
    queryKey: queryKeys.categories,
    queryFn: directoriesApi.categories,
  });
  const categories = normalizeCategories(categoriesQuery.data);
  const createMaterial = useMutation({
    mutationFn: ({ data, status }) => contentApi.create(makePayload(data, photos, status)),
    onSuccess: (_, variables) => {
      setNotice(variables.status === "draft" ? "Черновик сохранен." : "Материал отправлен на модерацию.");
      navigate("/cabinet/editor");
    },
  });
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm({
    resolver: zodResolver(schema),
    defaultValues: {
      type: "article",
      category: "",
      title: "",
      excerpt: "",
      content: "",
      tags: "",
    },
  });

  const previews = useMemo(
    () => photos.map((file) => ({ file, url: URL.createObjectURL(file) })),
    [photos],
  );

  useEffect(
    () => () => {
      previews.forEach(({ url }) => URL.revokeObjectURL(url));
    },
    [previews],
  );

  const addPhotos = (fileList) => {
    const imageFiles = Array.from(fileList).filter((file) => file.type.startsWith("image/"));
    setPhotos((current) => {
      const known = new Set(current.map((file) => `${file.name}-${file.size}-${file.lastModified}`));
      const next = imageFiles.filter((file) => !known.has(`${file.name}-${file.size}-${file.lastModified}`));
      return [...current, ...next].slice(0, 8);
    });
  };

  const removePhoto = (fileToRemove) => {
    setPhotos((current) => current.filter((file) => file !== fileToRemove));
  };

  const submit = (data) => {
    createMaterial.mutate({ data, status: submitStatus });
  };

  return (
    <div className="max-w-5xl">
      <p className="text-xs font-bold uppercase tracking-widest text-accent">Новая публикация</p>
      <h1 className="mt-2 font-serif text-4xl">Создание материала</h1>
      <form className="mt-8 grid gap-6 lg:grid-cols-[minmax(0,4fr)_minmax(14rem,1fr)]" onSubmit={handleSubmit(submit)}>
        <div className="grid gap-5 rounded-lg border border-line bg-surface p-5 sm:p-7">
          <div className="grid gap-5 sm:grid-cols-2">
            <Select label="Тип материала" {...register("type")}>
              {materialTypes.map(([value, label]) => (
                <option key={value} value={value}>{label}</option>
              ))}
            </Select>
            <div>
              {categoriesQuery.isLoading ? (
                <Skeleton className="h-16" />
              ) : categoriesQuery.isError ? (
                <ErrorState retry={() => categoriesQuery.refetch()} />
              ) : (
                <Select label="Категория" error={errors.category?.message} {...register("category")}>
                  <option value="">Выберите категорию</option>
                  {categories.map((item) => (
                    <option key={item.id} value={item.value}>{item.label}</option>
                  ))}
                </Select>
              )}
            </div>
          </div>
          <Input label="Заголовок" error={errors.title?.message} {...register("title")} />
          <Textarea label="Краткое описание" error={errors.excerpt?.message} {...register("excerpt")} />
          <Textarea
            className="min-h-80 font-mono text-sm"
            label="Содержание в Markdown"
            error={errors.content?.message}
            placeholder={"## Подзаголовок\n\nТекст материала..."}
            {...register("content")}
          />
          <Input label="Теги" placeholder="наука, кампус, студенты" {...register("tags")} />

          <section>
            <p className="text-sm font-semibold text-ink">Фотографии и обложка</p>
            <label
              className={cn(
                "mt-2 flex cursor-pointer flex-col items-center justify-center rounded-lg border border-dashed bg-paper p-8 text-center transition",
                dragActive
                  ? "border-dstu-menu-accent bg-dstu-menu-accent/10 text-ink"
                  : "border-line text-muted hover:border-primary hover:text-ink",
              )}
              onDragEnter={(event) => {
                event.preventDefault();
                setDragActive(true);
              }}
              onDragOver={(event) => event.preventDefault()}
              onDragLeave={(event) => {
                if (!event.currentTarget.contains(event.relatedTarget)) setDragActive(false);
              }}
              onDrop={(event) => {
                event.preventDefault();
                setDragActive(false);
                addPhotos(event.dataTransfer.files);
              }}
            >
              <input
                ref={inputRef}
                className="sr-only"
                type="file"
                accept="image/*"
                multiple
                onChange={(event) => addPhotos(event.target.files ?? [])}
              />
              <UploadCloud className="size-8 text-primary" />
              <span className="mt-3 font-semibold text-ink">Перетащите фотографии сюда</span>
              <span className="mt-1 text-sm">или нажмите, чтобы выбрать файлы</span>
              <span className="mt-3 inline-flex items-center gap-2 text-xs text-muted">
                <ImagePlus className="size-4" /> JPG, PNG, WEBP, до 8 файлов
              </span>
            </label>

            {previews.length > 0 && (
              <div className="mt-4 grid gap-3 sm:grid-cols-2">
                {previews.map(({ file, url }, index) => (
                  <div key={`${file.name}-${file.lastModified}`} className="flex gap-3 rounded-lg border border-line bg-white p-3 dark:bg-paper">
                    <img src={url} alt="" className="size-20 rounded-md object-cover" />
                    <div className="min-w-0 flex-1">
                      <p className="truncate text-sm font-semibold text-ink">
                        {index === 0 ? "Обложка: " : ""}
                        {file.name}
                      </p>
                      <p className="mt-1 text-xs text-muted">{formatFileSize(file.size)}</p>
                      <button
                        type="button"
                        className="mt-3 inline-flex items-center gap-1 text-xs font-semibold text-danger hover:text-danger/80"
                        onClick={() => removePhoto(file)}
                      >
                        <Trash2 className="size-3.5" /> Удалить
                      </button>
                    </div>
                  </div>
                ))}
              </div>
            )}
          </section>
        </div>
        <aside>
          <div className="sticky top-24 grid gap-3 rounded-lg border border-line bg-surface p-5">
            <h2 className="font-serif text-xl">Публикация</h2>
            <p className="text-sm leading-6 text-muted">
              Материал будет сохранен через `/api/content`. При наличии фотографий запрос уйдет как FormData.
            </p>
            <Button
              type="submit"
              variant="secondary"
              loading={createMaterial.isPending && submitStatus === "draft"}
              icon={<Save className="size-4" />}
              onClick={() => setSubmitStatus("draft")}
            >
              Сохранить черновик
            </Button>
            <Button type="button" variant="ghost" icon={<Eye className="size-4" />}>
              Предпросмотр
            </Button>
            <Button
              type="submit"
              loading={createMaterial.isPending && submitStatus === "moderation"}
              icon={<Send className="size-4" />}
              onClick={() => setSubmitStatus("moderation")}
            >
              На модерацию
            </Button>
            {notice && <p className="text-sm font-semibold text-success">{notice}</p>}
            {createMaterial.isError && <p className="text-sm font-semibold text-danger">Не удалось сохранить материал.</p>}
          </div>
        </aside>
      </form>
    </div>
  );
}
