"use client";

import type { ChangeEvent } from "react";
import { useCallback, useEffect, useMemo, useState } from "react";
import {
  FileTextOutlined,
  GlobalOutlined,
  PictureOutlined,
  ProfileOutlined,
  SendOutlined,
} from "@ant-design/icons";
import {
  Divider,
  Form,
  Input,
  Select,
  Space,
  Typography,
  Button,
  Spin,
} from "antd";
import { CmsSectionTitle } from "@/components/admin/CmsSectionTitle";
import { fetchAdminCategoriesList } from "@/providers/data-provider";
import RichEditor from "./RichEditor";

const apiBase = () => process.env.NEXT_PUBLIC_API_BASE_URL ?? "";

type CategoryRow = { id: string; name: string; slug: string };

function useCategories() {
  const [categories, setCategories] = useState<CategoryRow[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    let alive = true;

    const run = async () => {
      const token = localStorage.getItem("accessToken");
      if (!token) {
        if (!alive) return;
        setError("No permission");
        setLoading(false);
        return;
      }

      try {
        const rows = await fetchAdminCategoriesList();
        if (!alive) return;
        setCategories(rows as CategoryRow[]);
      } catch (e: unknown) {
        if (!alive) return;
        const status =
          e && typeof e === "object" && "statusCode" in e
            ? (e as { statusCode?: number }).statusCode
            : undefined;
        if (status === 403) setError("No permission");
        else setError("Could not load categories");
      } finally {
        if (alive) setLoading(false);
      }
    };

    void run();

    return () => {
      alive = false;
    };
  }, []);

  return { categories, loading, error };
}

function absoluteFromApiPath(base: string, urlPath: string) {
  if (!urlPath) return "";
  if (urlPath.startsWith("http://") || urlPath.startsWith("https://")) return urlPath;
  const b = base.replace(/\/$/, "");
  return `${b}${urlPath.startsWith("/") ? urlPath : `/${urlPath}`}`;
}

function FeaturedImageField() {
  const form = Form.useFormInstance();
  const [uploading, setUploading] = useState(false);
  const [uploadError, setUploadError] = useState<string | null>(null);

  const featuredImage = Form.useWatch("featuredImage", form) as string | undefined;
  const previewSrc =
    typeof featuredImage === "string" && featuredImage.trim()
      ? absoluteFromApiPath(apiBase(), featuredImage.trim())
      : "";

  const uploadFile = useCallback(
    async (file: File) => {
      const token = localStorage.getItem("accessToken");
      if (!token) {
        setUploadError("No permission");
        return;
      }

      setUploading(true);
      setUploadError(null);
      try {
        const fd = new FormData();
        fd.append("file", file);
        const res = await fetch(`${apiBase().replace(/\/$/, "")}/media/upload`, {
          method: "POST",
          headers: { Authorization: `Bearer ${token}` },
          body: fd,
        });
        const body = (await res.json().catch(() => ({}))) as { url?: string; message?: string };
        if (!res.ok) {
          throw new Error(typeof body.message === "string" ? body.message : "Upload failed");
        }
        if (!body.url) throw new Error("Upload failed");
        // Keep relative path so it matches Media.url and post save (ensureMediaFromUrl).
        form.setFieldValue("featuredImage", body.url);
      } catch (e) {
        setUploadError(e instanceof Error ? e.message : "Upload failed");
      } finally {
        setUploading(false);
      }
    },
    [form],
  );

  const onFileChange = (e: ChangeEvent<HTMLInputElement>) => {
    const f = e.target.files?.[0];
    e.target.value = "";
    if (f) void uploadFile(f);
  };

  return (
    <Space direction="vertical" className="w-full" size="middle">
      <input
        type="file"
        accept="image/jpeg,image/png,image/gif,image/webp"
        disabled={uploading}
        onChange={onFileChange}
        aria-label="Upload featured image file"
        className="block w-full max-w-md rounded-md border border-[rgba(15,23,42,0.12)] bg-[rgba(15,23,42,0.02)] px-3 py-2 text-sm file:mr-3 file:rounded-md file:border-0 file:bg-white file:px-3 file:py-1.5 file:text-sm"
      />
      {uploading ? (
        <Space>
          <Spin size="small" />
          <Typography.Text type="secondary" className="text-xs">
            Uploading…
          </Typography.Text>
        </Space>
      ) : null}
      {uploadError ? (
        <Typography.Text type="danger" className="text-xs">
          {uploadError}
        </Typography.Text>
      ) : null}
      {previewSrc ? (
        // eslint-disable-next-line @next/next/no-img-element -- minimal preview; remote/API URL
        <img
          src={previewSrc}
          alt="Featured"
          className="max-h-40 max-w-full rounded-lg border border-[rgba(15,23,42,0.1)] bg-[rgba(15,23,42,0.03)] object-contain"
        />
      ) : null}
      <Typography.Text type="secondary" className="text-xs">
        Or paste image URL below
      </Typography.Text>
    </Space>
  );
}

function OgImageField() {
  const form = Form.useFormInstance();
  const [uploading, setUploading] = useState(false);
  const [uploadError, setUploadError] = useState<string | null>(null);

  const ogImage = Form.useWatch("ogImage", form) as string | undefined;
  const previewSrc =
    typeof ogImage === "string" && ogImage.trim()
      ? absoluteFromApiPath(apiBase(), ogImage.trim())
      : "";

  const uploadFile = useCallback(
    async (file: File) => {
      const token = localStorage.getItem("accessToken");
      if (!token) {
        setUploadError("No permission");
        return;
      }

      setUploading(true);
      setUploadError(null);
      try {
        const fd = new FormData();
        fd.append("file", file);
        const res = await fetch(`${apiBase().replace(/\/$/, "")}/media/upload`, {
          method: "POST",
          headers: { Authorization: `Bearer ${token}` },
          body: fd,
        });
        const body = (await res.json().catch(() => ({}))) as { url?: string; message?: string };
        if (!res.ok) {
          throw new Error(typeof body.message === "string" ? body.message : "Upload failed");
        }
        if (!body.url) throw new Error("Upload failed");
        form.setFieldValue("ogImage", body.url);
      } catch (e) {
        setUploadError(e instanceof Error ? e.message : "Upload failed");
      } finally {
        setUploading(false);
      }
    },
    [form],
  );

  const onFileChange = (e: ChangeEvent<HTMLInputElement>) => {
    const f = e.target.files?.[0];
    e.target.value = "";
    if (f) void uploadFile(f);
  };

  return (
    <Space direction="vertical" className="w-full" size="middle">
      <input
        type="file"
        accept="image/jpeg,image/png,image/gif,image/webp"
        disabled={uploading}
        onChange={onFileChange}
        aria-label="Upload Open Graph image file"
        className="block w-full max-w-md rounded-md border border-[rgba(15,23,42,0.12)] bg-[rgba(15,23,42,0.02)] px-3 py-2 text-sm file:mr-3 file:rounded-md file:border-0 file:bg-white file:px-3 file:py-1.5 file:text-sm"
      />
      {uploading ? (
        <Space>
          <Spin size="small" />
          <Typography.Text type="secondary" className="text-xs">
            Uploading…
          </Typography.Text>
        </Space>
      ) : null}
      {uploadError ? (
        <Typography.Text type="danger" className="text-xs">
          {uploadError}
        </Typography.Text>
      ) : null}
      {previewSrc ? (
        // eslint-disable-next-line @next/next/no-img-element -- minimal preview; remote/API URL
        <img
          src={previewSrc}
          alt="Open Graph preview"
          className="max-h-40 max-w-full rounded-lg border border-[rgba(15,23,42,0.1)] bg-[rgba(15,23,42,0.03)] object-contain"
        />
      ) : null}
      <Typography.Text type="secondary" className="text-xs">
        Leave empty to use the featured image for sharing previews.
      </Typography.Text>
    </Space>
  );
}

type PostFormFieldsProps = {
  mode: "create" | "edit";
  /** Current status from server (edit) */
  serverStatus?: string;
  /** Used on edit: status select + publish action */
  canPublish?: boolean;
  onPublish?: () => void | Promise<void>;
  publishing?: boolean;
};

export function PostFormFields({
  mode,
  serverStatus,
  canPublish = false,
  onPublish,
  publishing,
}: PostFormFieldsProps) {
  const { categories, loading: catLoading, error: catError } = useCategories();
  const form = Form.useFormInstance();

  const categoryOptions = useMemo(
    () => categories.map((c) => ({ value: c.id, label: c.name })),
    [categories],
  );

  useEffect(() => {
    if (mode !== "create" || catLoading || catError || categories.length === 0) return;
    const current = form.getFieldValue("categoryId");
    if (!current) {
      form.setFieldValue("categoryId", categories[0]!.id);
    }
  }, [mode, catLoading, catError, categories, form]);

  const slugDisabled = mode === "edit" && serverStatus === "PUBLISHED";

  return (
    <>
      <CmsSectionTitle
        className="mb-7"
        icon={<ProfileOutlined />}
        title="Basic info"
        description="Title, URL slug, and excerpt shown in listings."
      />
      <Form.Item
        label="Title"
        name="title"
        className="!mb-6"
        rules={[{ required: true, message: "Title is required" }]}
      >
        <Input size="large" placeholder="Post title" />
      </Form.Item>

      <Form.Item label="Slug" name="slug" className="!mb-7">
        <Input
          size="large"
          disabled={slugDisabled}
          placeholder={
            slugDisabled
              ? "Slug is locked for published posts"
              : "Leave empty to generate from title"
          }
        />
      </Form.Item>

      <Form.Item label="Excerpt" name="excerpt" className="!mb-6">
        <Input.TextArea rows={3} placeholder="Optional summary for listings" />
      </Form.Item>

      <Divider className="!my-14" />

      <CmsSectionTitle
        className="mb-7"
        icon={<GlobalOutlined />}
        title="SEO"
        description="Optional meta title, description, and social share image. Empty meta fields fall back to the post title and excerpt on the public site."
      />
      <Form.Item
        label="Meta title"
        name="metaTitle"
        className="!mb-6"
        rules={[{ max: 200, message: "Max 200 characters" }]}
      >
        <Input size="large" placeholder="Page title for search and social (optional)" />
      </Form.Item>
      <Form.Item
        label="Meta description"
        name="metaDescription"
        className="!mb-6"
        rules={[{ max: 500, message: "Max 500 characters" }]}
      >
        <Input.TextArea rows={3} placeholder="Search / social description (optional)" />
      </Form.Item>
      <Form.Item label="Open Graph image" className="!mb-0">
        <Space direction="vertical" className="w-full" size="middle">
          <OgImageField />
          <Form.Item name="ogImage" noStyle>
            <Input size="large" placeholder="https://... or /uploads/... (optional)" />
          </Form.Item>
        </Space>
      </Form.Item>

      <Divider className="!my-14" />

      <CmsSectionTitle
        className="mb-7"
        icon={<FileTextOutlined />}
        title="Content"
        description="Main article body for Co Thu Ky Pho."
      />
      <Form.Item
        label="Body"
        name="content"
        className="!mb-16"
        rules={[
          {
            validator: async (_, value) => {
              if (!value || value === "<p></p>") {
                throw new Error("Content is required");
              }
            },
          },
        ]}
      >
        <RichEditor />
      </Form.Item>

      <Divider className="!my-14" />

      <CmsSectionTitle
        className="mb-7"
        icon={<SendOutlined />}
        title="Publishing"
        description="Status, category, and legacy publish action when permitted."
      />

      {mode === "create" ? (
        <Form.Item label="Status" name="status" initialValue="DRAFT" className="!mb-7">
          <Select
            size="large"
            options={[
              { value: "DRAFT", label: "DRAFT" },
              { value: "PUBLISHED", label: "PUBLISHED (requires posts:publish)" },
            ]}
          />
        </Form.Item>
      ) : serverStatus === "DRAFT" && canPublish ? (
        <Form.Item label="Status (on save)" name="status" className="!mb-6">
          <Select
            size="large"
            options={[
              { value: "DRAFT", label: "DRAFT" },
              { value: "PUBLISHED", label: "PUBLISHED" },
            ]}
          />
        </Form.Item>
      ) : serverStatus ? (
        <Form.Item label="Status" className="!mb-7">
          <Typography.Text className="text-[15px]">{serverStatus}</Typography.Text>
        </Form.Item>
      ) : null}

      <Form.Item
        label="Category"
        name="categoryId"
        className="!mb-7"
        rules={[{ required: true, message: "Category is required" }]}
      >
        <Select
          size="large"
          loading={catLoading}
          disabled={!!catError || catLoading}
          placeholder={
            catError ? catError : catLoading ? "Loading…" : "Select category"
          }
          options={categoryOptions}
        />
      </Form.Item>

      {mode === "edit" &&
      serverStatus === "DRAFT" &&
      canPublish &&
      onPublish ? (
        <Form.Item className="!mb-2">
          <Button type="primary" size="large" onClick={() => void onPublish()} loading={publishing}>
            Publish (API)
          </Button>
          <Typography.Paragraph type="secondary" className="mb-0 mt-2 text-xs leading-relaxed">
            Calls POST /posts/:id/publish (same as legacy CMS). Saving still uses PATCH.
          </Typography.Paragraph>
        </Form.Item>
      ) : null}

      <Divider className="!my-14" />

      <CmsSectionTitle
        className="mb-7"
        icon={<PictureOutlined />}
        title="Featured image"
        description="Upload a file or paste an image URL for the post cover."
      />
      <Form.Item label="Image" className="!mb-0">
        <Space direction="vertical" className="w-full" size="middle">
          <FeaturedImageField />
          <Form.Item name="featuredImage" noStyle>
            <Input size="large" placeholder="https://... or /uploads/..." />
          </Form.Item>
        </Space>
      </Form.Item>
    </>
  );
}

export function PostFormLoading() {
  return (
    <div className="flex justify-center py-28 pb-36">
      <Spin size="large" />
    </div>
  );
}
