"use client";

import { useEffect, useState } from "react";
import { Controller, useForm } from "react-hook-form";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import dynamic from "next/dynamic";
import { useDropzone } from "react-dropzone";
import { CloudUploadIcon, TimesIcon } from "@/components/icons";
import Image from "next/image";
import { htmlToString } from "@/utils/global";
import { close, error, loading, successToast } from "@/config/swal";
import { useRouter } from "next/navigation";
import Link from "next/link";
import Cookies from "js-cookie";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import {
  createArticle,
  createArticleSchedule,
  uploadArticleFile,
  uploadArticleThumbnail,
} from "@/service/article";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { Badge } from "@/components/ui/badge";
import {
  type ArticleContentKind,
  ARTICLE_KIND_LABEL,
  ARTICLE_KIND_TO_TYPE_ID,
  articleListPath,
} from "@/constants/article-content-types";

const CustomEditor = dynamic(
  () => import("@/components/editor/custom-editor"),
  { ssr: false },
);

interface FileWithPreview extends File {
  preview?: string;
}

const schema = z.object({
  title: z.string().min(2, "Title is required"),
  slug: z.string().min(2, "Slug is required"),
  description: z.string().min(2, "Description is required"),
  tags: z.array(z.string()).min(1, "Add at least one tag"),
});

type FormValues = z.infer<typeof schema>;

function removeImgTags(htmlString: string) {
  const parser = new DOMParser();
  const doc = parser.parseFromString(String(htmlString), "text/html");
  doc.querySelectorAll("img").forEach((img) => img.remove());
  return doc.body.innerHTML;
}

function mediaAcceptForKind(kind: ArticleContentKind): Record<string, string[]> {
  switch (kind) {
    case "image":
      return { "image/*": [".png", ".jpg", ".jpeg", ".gif", ".webp"] };
    case "video":
      return { "video/*": [".mp4", ".webm", ".mov"] };
    case "audio":
      return { "audio/*": [".mp3", ".wav", ".mpeg", ".ogg"] };
    default:
      return {};
  }
}

type Props = { contentKind: ArticleContentKind };

export default function CreateArticleForm({ contentKind }: Props) {
  const MySwal = withReactContent(Swal);
  const router = useRouter();
  const username = Cookies.get("username")?.trim() || "Editor";

  const typeId = ARTICLE_KIND_TO_TYPE_ID[contentKind];
  const label = ARTICLE_KIND_LABEL[contentKind];
  const requireMedia = contentKind !== "text";
  const listHref = articleListPath(contentKind);

  const [files, setFiles] = useState<FileWithPreview[]>([]);
  const [tagInput, setTagInput] = useState("");
  const [thumbnailImg, setThumbnailImg] = useState<File[]>([]);
  const [selectedMainImage, setSelectedMainImage] = useState<number | null>(null);
  const [filesValidation, setFileValidation] = useState("");
  const [thumbnailValidation, setThumbnailValidation] = useState("");
  const [status, setStatus] = useState<"publish" | "draft" | "scheduled">("publish");
  const [isScheduled, setIsScheduled] = useState(false);
  const [startDateValue, setStartDateValue] = useState<Date | undefined>();
  const [startTimeValue, setStartTimeValue] = useState("");

  const dropAccept = mediaAcceptForKind(contentKind);

  const { getRootProps, getInputProps } = useDropzone({
    onDrop: (acceptedFiles) => {
      setFiles((prev) => [...prev, ...acceptedFiles.map((f) => Object.assign(f))]);
    },
    multiple: true,
    accept: Object.keys(dropAccept).length ? dropAccept : undefined,
    disabled: !requireMedia,
  });

  const form = useForm<FormValues>({
    resolver: zodResolver(schema),
    defaultValues: { title: "", slug: "", description: "", tags: [] },
  });
  const {
    control,
    handleSubmit,
    formState: { errors },
    setValue,
    watch,
    setError,
    clearErrors,
  } = form;

  const watchTitle = watch("title");
  useEffect(() => {
    const slug = watchTitle
      .toLowerCase()
      .trim()
      .replace(/[^\w\s-]/g, "")
      .replace(/\s+/g, "-");
    setValue("slug", slug);
  }, [watchTitle, setValue]);

  const onSubmit = async (values: FormValues) => {
    if (requireMedia && files.length < 1) {
      setFileValidation("Upload at least one media file");
      return;
    }
    setFileValidation("");
    setThumbnailValidation("");

    const ok = await MySwal.fire({
      title: "Save article?",
      icon: "question",
      showCancelButton: true,
    });
    if (!ok.isConfirmed) return;
    await save(values);
  };

  const save = async (values: FormValues) => {
    loading();
    try {
      const plain = htmlToString(removeImgTags(values.description));
      const html = removeImgTags(values.description);

      const formData: Record<string, unknown> = {
        title: values.title,
        typeId,
        slug: values.slug,
        categoryIds: "",
        tags: values.tags.join(","),
        description: plain,
        htmlDescription: html,
        aiArticleId: null,
        customCreatorName: username,
        source: "internal",
        isDraft: status === "draft",
        isPublish: status === "publish",
      };

      const response = await createArticle(formData);
      if ((response as any)?.error) {
        error((response as any).message);
        return;
      }
      const articleId = (response as any)?.data?.data?.id as number | undefined;
      if (!articleId) {
        error("Could not read new article id");
        return;
      }

      if (files.length > 0) {
        for (const file of files) {
          const fd = new FormData();
          fd.append("file", file);
          const up = await uploadArticleFile(String(articleId), fd);
          if ((up as any)?.error) {
            error((up as any)?.message ?? "File upload failed");
            return;
          }
        }
      }

      if (thumbnailImg.length > 0 || (selectedMainImage && files.length >= selectedMainImage)) {
        const fd = new FormData();
        if (thumbnailImg.length > 0) {
          fd.append("files", thumbnailImg[0]);
        } else if (selectedMainImage) {
          fd.append("files", files[selectedMainImage - 1]);
        }
        const tr = await uploadArticleThumbnail(String(articleId), fd);
        if ((tr as any)?.error) {
          error((tr as any)?.message ?? "Thumbnail upload failed");
          return;
        }
      }

      if (status === "scheduled" && startDateValue) {
        const [h, m] = startTimeValue ? startTimeValue.split(":").map(Number) : [0, 0];
        const d = new Date(startDateValue);
        d.setHours(h, m, 0, 0);
        const formatted = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(
          d.getDate(),
        ).padStart(2, "0")} ${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(
          2,
          "0",
        )}:00`;
        await createArticleSchedule({ id: articleId, date: formatted });
      }

      close();
      const publicUrl = `${window.location.protocol}//${window.location.host}/news/detail/${articleId}-${values.slug}`;
      MySwal.fire({ title: "Saved", icon: "success" }).then(() => {
        router.push(listHref);
        successToast("Article URL", publicUrl);
      });
    } finally {
      close();
    }
  };

  const renderPreview = (file: FileWithPreview) => {
    if (file.type.startsWith("image")) {
      return (
        <Image
          width={48}
          height={48}
          alt={file.name}
          src={URL.createObjectURL(file)}
          className="rounded border p-0.5"
        />
      );
    }
    return (
      <div className="flex h-12 w-12 items-center justify-center rounded border bg-slate-100 text-[10px] px-1 text-center">
        {file.name.slice(0, 8)}…
      </div>
    );
  };

  return (
    <form className="flex flex-col lg:flex-row gap-8 text-black" onSubmit={handleSubmit(onSubmit)}>
      <div className="w-full lg:w-[65%] bg-white rounded-lg p-8 flex flex-col gap-1 shadow-sm">
        <p className="text-xs font-medium text-blue-600 uppercase tracking-wide">
          New {label} article
        </p>
        <p className="text-sm text-slate-500 mb-2">
          Tags are used for organization. Categories and creator type are not used for News & Article.
        </p>

        <p className="text-sm">Title</p>
        <Controller
          control={control}
          name="title"
          render={({ field }) => (
            <Input
              placeholder="Article title"
              className="h-14 px-4 text-xl"
              {...field}
            />
          )}
        />
        {errors.title && <p className="text-red-500 text-sm">{errors.title.message}</p>}

        <p className="text-sm mt-3">Slug</p>
        <Controller
          control={control}
          name="slug"
          render={({ field }) => (
            <Input className="w-full border rounded-lg" {...field} />
          )}
        />
        {errors.slug && <p className="text-red-500 text-sm">{errors.slug.message}</p>}

        <p className="text-sm mt-3">Description</p>
        <Controller
          control={control}
          name="description"
          render={({ field: { onChange, value } }) => (
            <CustomEditor onChange={onChange} initialData={value} />
          )}
        />
        {errors.description && (
          <p className="text-red-500 text-sm">{errors.description.message}</p>
        )}

        {requireMedia && (
          <>
            <p className="text-sm mt-4 font-medium">Media files ({label})</p>
            <div {...getRootProps({ className: "dropzone cursor-pointer" })}>
              <input {...getInputProps()} />
              <div className="w-full text-center border-dashed border-2 rounded-md py-12 flex flex-col items-center border-slate-200">
                <CloudUploadIcon size={48} className="text-slate-300" />
                <p className="mt-2 text-slate-700">Drop files here or click to upload</p>
                <p className="text-xs text-slate-400 mt-1">
                  {contentKind === "image" && "Images: jpg, png, webp…"}
                  {contentKind === "video" && "Video: mp4, webm…"}
                  {contentKind === "audio" && "Audio: mp3, wav…"}
                </p>
              </div>
            </div>
            {filesValidation && <p className="text-red-500 text-sm">{filesValidation}</p>}

            {files.length > 0 && (
              <div className="space-y-3 mt-2">
                {files.map((file, index) => (
                  <div
                    key={`${file.name}-${index}`}
                    className="flex justify-between border px-3 py-3 rounded-md items-center"
                  >
                    <div className="flex gap-3 items-center">
                      {renderPreview(file)}
                      <div>
                        <div className="text-sm">{file.name}</div>
                        {file.type.startsWith("image") && (
                          <label className="flex items-center gap-2 text-xs mt-1 cursor-pointer">
                            <input
                              type="radio"
                              name="thumb"
                              checked={selectedMainImage === index + 1}
                              onChange={() => setSelectedMainImage(index + 1)}
                            />
                            Use as thumbnail
                          </label>
                        )}
                      </div>
                    </div>
                    <Button
                      type="button"
                      variant="ghost"
                      size="icon"
                      className="rounded-full"
                      onClick={() => {
                        setFiles((prev) => prev.filter((_, i) => i !== index));
                        setSelectedMainImage(null);
                      }}
                    >
                      <TimesIcon />
                    </Button>
                  </div>
                ))}
                <Button type="button" size="sm" variant="outline" onClick={() => setFiles([])}>
                  Clear all files
                </Button>
              </div>
            )}
          </>
        )}
      </div>

      <div className="w-full lg:w-[35%] flex flex-col gap-6">
        <div className="bg-white rounded-lg p-6 shadow-sm flex flex-col gap-3">
          <p className="text-sm font-medium">Thumbnail</p>
          <p className="text-xs text-slate-500">
            {requireMedia
              ? "Optional separate image, or pick a gallery image as thumbnail above."
              : "Optional cover image for listings."}
          </p>
          <input
            type="file"
            accept="image/*"
            className="text-sm"
            onChange={(e) => {
              const f = e.target.files?.[0];
              setThumbnailImg(f ? [f] : []);
              e.target.value = "";
            }}
          />
          {(thumbnailImg.length > 0 || (selectedMainImage && files[selectedMainImage - 1])) && (
            <div className="relative w-40">
              <img
                src={
                  thumbnailImg[0]
                    ? URL.createObjectURL(thumbnailImg[0])
                    : URL.createObjectURL(files[selectedMainImage! - 1])
                }
                alt=""
                className="rounded border w-full"
              />
              <Button
                type="button"
                size="sm"
                variant="ghost"
                className="absolute -top-2 -right-2"
                onClick={() => {
                  setThumbnailImg([]);
                  setSelectedMainImage(null);
                }}
              >
                ×
              </Button>
            </div>
          )}
          {thumbnailValidation && <p className="text-red-500 text-xs">{thumbnailValidation}</p>}

          <p className="text-sm font-medium pt-2">Tags</p>
          <Controller
            control={control}
            name="tags"
            render={({ field: { value } }) => (
              <div>
                <div className="flex flex-wrap gap-1 mb-2">
                  {value.map((item, index) => (
                    <Badge key={`${item}-${index}`} variant="secondary" className="gap-1">
                      {item}
                      <button
                        type="button"
                        className="text-red-600"
                        onClick={() => {
                          const next = value.filter((t) => t !== item);
                          if (next.length === 0) {
                            setError("tags", { message: "At least one tag" });
                          } else {
                            clearErrors("tags");
                            setValue("tags", next as [string, ...string[]]);
                          }
                        }}
                      >
                        ×
                      </button>
                    </Badge>
                  ))}
                </div>
                <Textarea
                  placeholder="Type a tag and press Enter"
                  value={tagInput}
                  onChange={(e) => setTagInput(e.target.value)}
                  onKeyDown={(e) => {
                    if (e.key === "Enter") {
                      e.preventDefault();
                      const t = tagInput.trim();
                      if (t) {
                        setValue("tags", [...value, t]);
                        setTagInput("");
                        clearErrors("tags");
                      }
                    }
                  }}
                  className="min-h-[80px]"
                />
              </div>
            )}
          />
          {errors.tags && <p className="text-red-500 text-sm">{errors.tags.message}</p>}

          <div className="flex items-center gap-2 pt-2">
            <input
              type="checkbox"
              id="sched"
              checked={isScheduled}
              onChange={(e) => setIsScheduled(e.target.checked)}
            />
            <label htmlFor="sched" className="text-sm">
              Schedule publish
            </label>
          </div>
          {isScheduled && (
            <div className="flex flex-col gap-2">
              <Input
                type="date"
                onChange={(e) => setStartDateValue(e.target.value ? new Date(e.target.value) : undefined)}
              />
              <Input type="time" value={startTimeValue} onChange={(e) => setStartTimeValue(e.target.value)} />
            </div>
          )}
        </div>

        <div className="flex flex-wrap justify-end gap-2">
          <Button
            type="submit"
            className="bg-blue-600"
            onClick={() => (isScheduled ? setStatus("scheduled") : setStatus("publish"))}
            disabled={isScheduled && !startDateValue}
          >
            {isScheduled ? "Schedule" : "Publish"}
          </Button>
          <Button type="submit" variant="secondary" onClick={() => setStatus("draft")}>
            Save draft
          </Button>
          <Link href={listHref}>
            <Button type="button" variant="outline">
              Back
            </Button>
          </Link>
        </div>
      </div>
    </form>
  );
}
