"use client";

import { useEffect, useState } from "react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { BottomToast, type BottomToastNotice } from "@/components/bottom-toast";
import { MarkdownPreview } from "@/components/markdown-preview";
import { NotionBlogContentEditor } from "@/components/notion-blog-content-editor";
import {
  BLOG_CONTENT_IDEAS,
  contentIdeaMarkdown,
  relatedBlogPosts,
  type BlogContentIdea,
} from "@/lib/blog-growth";
import type {
  GoogleSearchConsoleBlogPostStatus,
  GoogleSearchConsoleBlogStatus,
} from "@/lib/google-search-console";
import type { MarketingBlogPost, MarketingBlogStatus } from "@/lib/marketing-blog";

type BloggerPostStatus = {
  editUrl: string;
  externalPostId: string;
  externalUrl: string | null;
  lastError: string | null;
  postId: number;
  sourceChanged: boolean;
  status: "draft" | "failed" | "published";
  syncedAt: string | null;
};

type BloggerIntegrationStatus = {
  accountEmail: string | null;
  blogId: string | null;
  blogName: string;
  blogUrl: string | null;
  configured: boolean;
  connected: boolean;
  permissionGranted: boolean;
  post: BloggerPostStatus | null;
};

type WordPressPostStatus = {
  editUrl: string;
  externalPostId: string;
  externalUrl: string | null;
  lastError: string | null;
  postId: number;
  sourceChanged: boolean;
  status: "draft" | "failed" | "published";
  syncedAt: string | null;
};

type WordPressIntegrationStatus = {
  blogName: string;
  blogUrl: string | null;
  configured: boolean;
  connected: boolean;
  post: WordPressPostStatus | null;
  site: string | null;
};

type EditorDraft = {
  contentMarkdown: string;
  excerpt: string;
  id: number | null;
  keywordsText: string;
  seoDescription: string;
  slug: string;
  status: MarketingBlogStatus;
  title: string;
};

type BlogApiResponse = {
  message?: string;
  ok?: boolean;
  post?: MarketingBlogPost;
};

type BlogInspectionApiResponse = {
  message?: string;
  ok?: boolean;
  status?: GoogleSearchConsoleBlogPostStatus | null;
};

type BloggerApiResponse = {
  message?: string;
  ok?: boolean;
  status?: BloggerIntegrationStatus;
};

type WordPressApiResponse = {
  message?: string;
  ok?: boolean;
  status?: WordPressIntegrationStatus;
};

function formatDate(value: string | null) {
  if (!value) return "임시저장";

  return new Intl.DateTimeFormat("ko-KR", {
    year: "numeric",
    month: "2-digit",
    day: "2-digit",
  }).format(new Date(value));
}

function formatInspectionDate(value: string | null) {
  if (!value) return "확인 전";

  return new Intl.DateTimeFormat("ko-KR", {
    month: "2-digit",
    day: "2-digit",
    hour: "2-digit",
    minute: "2-digit",
  }).format(new Date(value));
}

function getIndexBadge(
  status: GoogleSearchConsoleBlogPostStatus | undefined,
  published: boolean,
) {
  if (!published) {
    return { className: "bg-[#f3f5f8] text-[#7c879b]", label: "미발행" };
  }

  if (status?.inspectionError) {
    return { className: "bg-[#fff1f1] text-[#c84d4d]", label: "확인 오류" };
  }

  if (status?.isIndexed) {
    return { className: "bg-[#edf9f1] text-[#278a50]", label: "색인됨" };
  }

  const coverageState = status?.coverageState?.toLocaleLowerCase("ko-KR") ?? "";

  if (
    coverageState.includes("알려지지 않은 url") ||
    coverageState.includes("unknown to google")
  ) {
    return { className: "bg-[#f3f5f8] text-[#687992]", label: "발견 대기" };
  }

  if (
    coverageState.includes("현재 색인이 생성되지 않음") ||
    coverageState.includes("currently not indexed")
  ) {
    return { className: "bg-[#fff7e8] text-[#a96d13]", label: "미색인" };
  }

  if (
    coverageState.includes("대체 페이지") ||
    coverageState.includes("alternate page") ||
    coverageState.includes("중복") ||
    coverageState.includes("duplicate") ||
    coverageState.includes("리디렉션이 포함된 페이지") ||
    coverageState.includes("page with redirect")
  ) {
    return { className: "bg-[#f3f5f8] text-[#687992]", label: "색인 제외" };
  }

  if (
    coverageState.includes("오류") ||
    coverageState.includes("차단") ||
    coverageState.includes("noindex") ||
    coverageState.includes("error") ||
    coverageState.includes("blocked")
  ) {
    return { className: "bg-[#fff1f1] text-[#c84d4d]", label: "확인 필요" };
  }

  if (status?.indexVerdict === "NEUTRAL") {
    return { className: "bg-[#fff7e8] text-[#a96d13]", label: "미색인" };
  }

  if (status?.indexVerdict === "FAIL") {
    return { className: "bg-[#fff1f1] text-[#c84d4d]", label: "확인 필요" };
  }

  return { className: "bg-[#f3f5f8] text-[#7c879b]", label: "확인 전" };
}

function toDraft(post: MarketingBlogPost): EditorDraft {
  return {
    contentMarkdown: post.contentMarkdown,
    excerpt: post.excerpt,
    id: post.id,
    keywordsText: post.keywords.join(", "),
    seoDescription: post.seoDescription,
    slug: post.slug,
    status: post.status,
    title: post.title,
  };
}

function makeNewDraft(): EditorDraft {
  return {
    contentMarkdown: "",
    excerpt: "",
    id: null,
    keywordsText: "",
    seoDescription: "",
    slug: `new-blog-${new Date().toISOString().slice(0, 10).replaceAll("-", "")}`,
    status: "draft",
    title: "새 블로그 글 제목",
  };
}

function makeSearchQueryDraft(query: string): EditorDraft {
  const normalizedQuery = query.trim();
  const queryKey = Array.from(normalizedQuery).reduce(
    (hash, character) => ((hash * 31) + (character.codePointAt(0) ?? 0)) >>> 0,
    0,
  ).toString(36);

  return {
    contentMarkdown: [
      `${normalizedQuery}을 검색한 사용자가 실제로 해결하려는 문제와 필요한 준비 사항을 먼저 설명합니다.`,
      "",
      "## 먼저 확인할 것",
      "",
      "현재 설정과 오류 메시지에서 확인해야 할 항목을 구체적으로 정리합니다.",
      "",
      "## 해결 순서",
      "",
      "실제 화면과 값 기준으로 따라 할 수 있는 순서를 작성합니다.",
      "",
      "## 해결 후 검증",
      "",
      "외부 환경에서 결과가 정상인지 확인하는 방법을 안내합니다.",
      "",
      "## 자주 묻는 질문",
      "",
      "같은 문제를 겪는 사용자가 반복해서 묻는 질문과 답변을 추가합니다.",
    ].join("\n"),
    excerpt: `${normalizedQuery} 문제의 원인과 확인 순서, 해결 후 검증 방법을 정리합니다.`,
    id: null,
    keywordsText: normalizedQuery,
    seoDescription: `${normalizedQuery} 문제를 실제 설정값과 오류 사례를 기준으로 점검하고 해결하는 방법을 안내합니다.`,
    slug: `search-guide-${new Date().toISOString().slice(0, 10).replaceAll("-", "")}-${queryKey}`,
    status: "draft",
    title: `${normalizedQuery} 해결 가이드`,
  };
}

function makeImageDraftToken() {
  return crypto.randomUUID();
}

function editorFieldClass() {
  return "h-10 w-full border-b border-[#dfe7f3] bg-transparent px-0 text-[14px] text-[#1a2943] outline-none transition-colors placeholder:text-[#9aa8bd] focus:border-[#8eb5ff]";
}

function ContentPlanner({
  onStart,
  posts,
}: {
  onStart: (idea: BlogContentIdea) => void;
  posts: MarketingBlogPost[];
}) {
  const clusters = Array.from(new Set(BLOG_CONTENT_IDEAS.map((idea) => idea.cluster)));

  return (
    <div className="min-h-[620px]">
      <div className="border-b border-[#edf1f6] px-3 py-5 sm:px-4 lg:px-8">
        <p className="text-[12px] font-semibold text-[#2f6df6]">CONTENT CLUSTERS</p>
        <h2 className="mt-2 text-[24px] font-semibold tracking-[-0.055em] text-[#16233c] sm:text-[30px]">검색 의도별 발행 계획</h2>
        <p className="mt-2 max-w-[720px] text-[13px] leading-6 text-[#70819d]">
          DNS 문제 해결, 업종별 운영, 서비스 전환 주제를 묶어 발행합니다. 이미 작성된 URL은 상태만 표시하고 중복 초안을 만들지 않습니다.
        </p>
      </div>
      {clusters.map((cluster) => {
        const ideas = BLOG_CONTENT_IDEAS.filter((idea) => idea.cluster === cluster);
        const completedCount = ideas.filter((idea) => posts.some((post) => post.slug === idea.slug)).length;

        return (
          <section key={cluster} className="border-b border-[#edf1f6]">
            <div className="flex items-center justify-between gap-3 border-b border-[#f1f4f9] px-3 py-3 sm:px-4 lg:px-8">
              <h3 className="text-[14px] font-semibold text-[#263a5a]">{cluster}</h3>
              <span className="text-[11px] font-semibold text-[#7b8ba3]">{completedCount} / {ideas.length} 발행 준비</span>
            </div>
            <div className="divide-y divide-[#f1f4f9]">
              {ideas.map((idea) => {
                const existing = posts.find((post) => post.slug === idea.slug);

                return (
                  <article key={idea.slug} className="grid gap-4 px-3 py-4 sm:grid-cols-[minmax(0,1fr)_auto] sm:items-center sm:px-4 lg:px-8">
                    <div className="min-w-0">
                      <div className="flex flex-wrap items-center gap-2">
                        <h4 className="text-[14px] font-semibold tracking-[-0.025em] text-[#30425f]">{idea.title}</h4>
                        {existing ? (
                          <span className={`rounded-full px-2 py-1 text-[10px] font-semibold ${
                            existing.status === "published"
                              ? "bg-[#edf9f1] text-[#278a50]"
                              : "bg-[#f3f5f8] text-[#687992]"
                          }`}>
                            {existing.status === "published" ? "발행됨" : "임시저장"}
                          </span>
                        ) : null}
                      </div>
                      <p className="mt-1.5 text-[12px] leading-5 text-[#7183a1]">{idea.description}</p>
                      <p className="mt-1.5 truncate text-[10px] text-[#99a5b6]">/blog/{idea.slug}</p>
                    </div>
                    <button
                      type="button"
                      disabled={Boolean(existing)}
                      onClick={() => onStart(idea)}
                      className="om-btn-secondary h-9 rounded-[9px] px-3 text-[11px] shadow-none disabled:cursor-not-allowed disabled:text-[#a6b1c2]"
                    >
                      {existing ? "작성 완료" : "구조화 초안 만들기"}
                    </button>
                  </article>
                );
              })}
            </div>
          </section>
        );
      })}
    </div>
  );
}

export function KavenixBlogEditor({
  initialPostId,
  initialQuery,
  posts: initialPosts,
  searchConsole,
}: {
  initialPostId?: number | null;
  initialQuery?: string;
  posts: MarketingBlogPost[];
  searchConsole: GoogleSearchConsoleBlogStatus | null;
}) {
  const router = useRouter();
  const initialSelectedPost = initialPostId
    ? initialPosts.find((post) => post.id === initialPostId)
    : null;
  const [posts, setPosts] = useState(initialPosts);
  const [searchStatuses, setSearchStatuses] = useState(searchConsole?.posts ?? []);
  const [draft, setDraft] = useState<EditorDraft>(() =>
    initialQuery
      ? makeSearchQueryDraft(initialQuery)
      : initialSelectedPost
        ? toDraft(initialSelectedPost)
        : initialPosts[0]
          ? toDraft(initialPosts[0])
          : makeNewDraft()
  );
  const [imageDraftToken, setImageDraftToken] = useState(makeImageDraftToken);
  const [bloggerStatus, setBloggerStatus] = useState<BloggerIntegrationStatus | null>(null);
  const [bloggerLoading, setBloggerLoading] = useState(false);
  const [wordpressStatus, setWordpressStatus] = useState<WordPressIntegrationStatus | null>(null);
  const [wordpressLoading, setWordpressLoading] = useState(false);
  const [isEditorFullWidth, setIsEditorFullWidth] = useState(false);
  const [isImageUploading, setIsImageUploading] = useState(false);
  const [isPlanningOpen, setIsPlanningOpen] = useState(false);
  const [isPreviewOpen, setIsPreviewOpen] = useState(false);
  const [inspectingPostId, setInspectingPostId] = useState<number | null>(null);
  const [saving, setSaving] = useState(false);
  const [toast, setToast] = useState<BottomToastNotice | null>(null);

  useEffect(() => {
    if (!toast) return undefined;
    const timer = window.setTimeout(() => setToast(null), 4200);
    return () => window.clearTimeout(timer);
  }, [toast]);

  useEffect(() => {
    const frame = window.requestAnimationFrame(() => {
      setIsEditorFullWidth(window.localStorage.getItem("official-blog-editor-full-width") === "true");
    });
    return () => window.cancelAnimationFrame(frame);
  }, []);

  useEffect(() => {
    const controller = new AbortController();
    const query = draft.id ? `?postId=${draft.id}` : "";

    void fetch(`/api/admin/blogger${query}`, {
      cache: "no-store",
      signal: controller.signal,
    })
      .then(async (response) => {
        const result = await response.json().catch(() => null) as BloggerApiResponse | null;

        if (!response.ok || !result?.ok || !result.status) {
          throw new Error(result?.message || "Blogger 연결 상태를 불러오지 못했습니다.");
        }

        setBloggerStatus(result.status);
      })
      .catch((error) => {
        if (error instanceof DOMException && error.name === "AbortError") return;
        setBloggerStatus(null);
      });

    return () => controller.abort();
  }, [draft.id]);

  useEffect(() => {
    const controller = new AbortController();
    const query = draft.id ? `?postId=${draft.id}` : "";

    void fetch(`/api/admin/wordpress${query}`, {
      cache: "no-store",
      signal: controller.signal,
    })
      .then(async (response) => {
        const result = await response.json().catch(() => null) as WordPressApiResponse | null;

        if (!response.ok || !result?.ok || !result.status) {
          throw new Error(result?.message || "WordPress.com 연결 상태를 불러오지 못했습니다.");
        }

        setWordpressStatus(result.status);
      })
      .catch((error) => {
        if (error instanceof DOMException && error.name === "AbortError") return;
        setWordpressStatus(null);
      });

    return () => controller.abort();
  }, [draft.id]);

  function toggleEditorWidth() {
    setIsEditorFullWidth((current) => {
      const next = !current;
      window.localStorage.setItem("official-blog-editor-full-width", String(next));
      return next;
    });
  }

  function updateDraft<Key extends keyof EditorDraft>(key: Key, value: EditorDraft[Key]) {
    setDraft((current) => ({ ...current, [key]: value }));
  }

  function selectPost(post: MarketingBlogPost) {
    setDraft(toDraft(post));
    setImageDraftToken(makeImageDraftToken());
    setIsPlanningOpen(false);
    setIsPreviewOpen(false);
  }

  function startNewPost() {
    setDraft(makeNewDraft());
    setImageDraftToken(makeImageDraftToken());
    setIsPlanningOpen(false);
    setIsPreviewOpen(false);
  }

  function startContentIdea(idea: BlogContentIdea) {
    setDraft({
      contentMarkdown: contentIdeaMarkdown(idea),
      excerpt: idea.description,
      id: null,
      keywordsText: idea.keywords.join(", "),
      seoDescription: idea.description,
      slug: idea.slug,
      status: "draft",
      title: idea.title,
    });
    setImageDraftToken(makeImageDraftToken());
    setIsPlanningOpen(false);
    setIsPreviewOpen(false);
  }

  const internalLinkSuggestions = relatedBlogPosts(
    {
      contentMarkdown: draft.contentMarkdown,
      id: draft.id ?? -1,
      keywords: draft.keywordsText.split(",").map((keyword) => keyword.trim()).filter(Boolean),
      title: draft.title,
    },
    posts,
  );

  function appendInternalLinks() {
    const links = internalLinkSuggestions.filter((post) =>
      !draft.contentMarkdown.includes(`/blog/${post.slug}`)
    );

    if (links.length === 0) {
      setToast({ tone: "error", message: "이미 추천 내부 링크가 본문에 반영되어 있습니다." });
      return;
    }

    updateDraft(
      "contentMarkdown",
      [
        draft.contentMarkdown.trimEnd(),
        "",
        "## 함께 확인할 가이드",
        "",
        ...links.map((post) => `- [${post.title}](/blog/${post.slug})`),
      ].join("\n"),
    );
    setToast({ tone: "success", message: `${links.length}개의 관련 글 링크를 본문에 반영했습니다.` });
  }

  async function save(nextStatus: MarketingBlogStatus) {
    setSaving(true);

    try {
      const payload = {
        contentMarkdown: draft.contentMarkdown,
        excerpt: draft.excerpt,
        imageDraftToken,
        keywords: draft.keywordsText,
        seoDescription: draft.seoDescription,
        slug: draft.slug,
        status: nextStatus,
        title: draft.title,
      };
      const response = await fetch(
        draft.id ? `/api/kavenix/blog/${draft.id}` : "/api/kavenix/blog",
        {
          method: draft.id ? "PATCH" : "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(payload),
        },
      );
      const result = (await response.json().catch(() => null)) as BlogApiResponse | null;

      if (!response.ok || !result?.ok || !result.post) {
        throw new Error(result?.message || "블로그 글 저장 중 오류가 발생했습니다.");
      }

      const savedPost = result.post;
      setPosts((current) => {
        const existingIndex = current.findIndex((post) => post.id === savedPost.id);
        return existingIndex < 0
          ? [savedPost, ...current]
          : current.map((post) => post.id === savedPost.id ? savedPost : post);
      });
      setDraft(toDraft(savedPost));
      setImageDraftToken(makeImageDraftToken());
      setToast({ tone: "success", message: result.message || "저장했습니다." });
      router.refresh();
    } catch (error) {
      setToast({
        tone: "error",
        message: error instanceof Error ? error.message : "블로그 글 저장 중 오류가 발생했습니다.",
      });
    } finally {
      setSaving(false);
    }
  }

  async function deletePost() {
    if (!draft.id || !window.confirm("이 블로그 글을 삭제할까요? 공개된 글은 즉시 주소에서 사라집니다.")) {
      return;
    }

    const deletedPostId = draft.id;
    setSaving(true);

    try {
      const response = await fetch(`/api/kavenix/blog/${deletedPostId}`, { method: "DELETE" });
      const result = (await response.json().catch(() => null)) as BlogApiResponse | null;

      if (!response.ok || !result?.ok) {
        throw new Error(result?.message || "블로그 글 삭제 중 오류가 발생했습니다.");
      }

      const remainingPosts = posts.filter((post) => post.id !== deletedPostId);
      setPosts(remainingPosts);
      setDraft(remainingPosts[0] ? toDraft(remainingPosts[0]) : makeNewDraft());
      setToast({ tone: "success", message: result.message || "블로그 글을 삭제했습니다." });
      router.refresh();
    } catch (error) {
      setToast({
        tone: "error",
        message: error instanceof Error ? error.message : "블로그 글 삭제 중 오류가 발생했습니다.",
      });
    } finally {
      setSaving(false);
    }
  }

  async function inspectSearchStatus() {
    if (!draft.id || draft.status !== "published") {
      setToast({ tone: "error", message: "발행된 게시글만 색인 상태를 확인할 수 있습니다." });
      return;
    }

    if (!searchConsole?.connected) {
      setToast({ tone: "error", message: "Search Console 계정을 먼저 연결해주세요." });
      return;
    }

    setInspectingPostId(draft.id);

    try {
      const response = await fetch("/api/kavenix/search-console/inspect-blog", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ postId: draft.id }),
      });
      const result = (await response.json().catch(() => null)) as BlogInspectionApiResponse | null;

      if (!response.ok || !result?.ok || !result.status) {
        throw new Error(result?.message || "색인 상태 확인에 실패했습니다.");
      }

      const inspectedStatus = result.status;
      setSearchStatuses((current) => [
        ...current.filter((status) => status.postId !== inspectedStatus.postId),
        inspectedStatus,
      ]);
      setToast({
        tone: inspectedStatus.inspectionError ? "error" : "success",
        message: inspectedStatus.inspectionError
          || (inspectedStatus.isIndexed
            ? "Google 색인 상태를 확인했습니다."
            : inspectedStatus.coverageState || "아직 Google 색인이 확인되지 않습니다."),
      });
    } catch (error) {
      setToast({
        tone: "error",
        message: error instanceof Error ? error.message : "색인 상태 확인에 실패했습니다.",
      });
    } finally {
      setInspectingPostId(null);
    }
  }

  async function runBloggerAction(action: "publish" | "sync") {
    if (!draft.id) return;

    setBloggerLoading(true);

    try {
      const response = await fetch("/api/admin/blogger", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ action, postId: draft.id }),
      });
      const result = await response.json().catch(() => null) as BloggerApiResponse | null;

      if (!response.ok || !result?.ok || !result.status) {
        throw new Error(result?.message || "Blogger 처리 중 오류가 발생했습니다.");
      }

      setBloggerStatus(result.status);
      setToast({ tone: "success", message: result.message || "Blogger 처리를 완료했습니다." });
    } catch (error) {
      setToast({
        tone: "error",
        message: error instanceof Error ? error.message : "Blogger 처리 중 오류가 발생했습니다.",
      });
    } finally {
      setBloggerLoading(false);
    }
  }

  async function runWordPressAction(action: "publish" | "sync") {
    if (!draft.id) return;

    setWordpressLoading(true);

    try {
      const response = await fetch("/api/admin/wordpress", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ action, postId: draft.id }),
      });
      const result = await response.json().catch(() => null) as WordPressApiResponse | null;

      if (!response.ok || !result?.ok || !result.status) {
        throw new Error(result?.message || "WordPress.com 처리 중 오류가 발생했습니다.");
      }

      setWordpressStatus(result.status);
      setToast({ tone: "success", message: result.message || "WordPress.com 처리를 완료했습니다." });
    } catch (error) {
      setToast({
        tone: "error",
        message: error instanceof Error ? error.message : "WordPress.com 처리 중 오류가 발생했습니다.",
      });
    } finally {
      setWordpressLoading(false);
    }
  }

  const published = draft.status === "published";
  const selectedSearchStatus = draft.id
    ? searchStatuses.find((status) => status.postId === draft.id)
    : undefined;
  const selectedIndexBadge = getIndexBadge(selectedSearchStatus, published);

  return (
    <div className="min-w-0 bg-white">
      <div className="grid min-h-[680px] lg:grid-cols-[280px_minmax(0,1fr)]">
        <aside className="min-w-0 border-b border-[#edf1f6] lg:border-b-0 lg:border-r">
          <div className="flex items-center justify-between gap-3 border-b border-[#edf1f6] px-3 py-3 sm:px-4 lg:px-5">
            <p className="text-[14px] font-semibold tracking-[-0.03em] text-[#16233c]">전체 글 {posts.length}</p>
            <div className="flex items-center gap-1.5">
              <button
                type="button"
                onClick={() => setIsPlanningOpen(true)}
                className={`h-8 rounded-[9px] px-2.5 text-[11px] font-semibold transition-colors ${
                  isPlanningOpen
                    ? "bg-[#edf4ff] text-[#2f6df6]"
                    : "text-[#70819d] hover:bg-[#f4f7fb] hover:text-[#2f6df6]"
                }`}
              >
                콘텐츠 기획
              </button>
              <button type="button" onClick={startNewPost} className="om-btn-primary h-8 rounded-[9px] px-3 text-[12px] shadow-none">새 글</button>
            </div>
          </div>
          <div className="max-h-[380px] overflow-y-auto lg:max-h-[calc(100vh-255px)]">
            {posts.map((post) => {
              const active = post.id === draft.id;
              const searchStatus = searchStatuses.find((status) => status.postId === post.id);
              const indexBadge = getIndexBadge(searchStatus, post.status === "published");

              return (
                <button
                  key={post.id}
                  type="button"
                  onClick={() => selectPost(post)}
                  className={`block w-full border-b border-[#edf1f6] px-3 py-3 text-left transition-colors sm:px-4 lg:px-5 ${
                    active ? "bg-[#f6f9ff]" : "hover:bg-[#fafcff]"
                  }`}
                >
                  <div className="flex items-center justify-between gap-3">
                    <span className={`text-[11px] font-semibold ${post.status === "published" ? "text-[#2f6df6]" : "text-[#8492a8]"}`}>
                      {post.status === "published" ? "발행됨" : "임시저장"}
                    </span>
                    <time className="shrink-0 text-[11px] text-[#8b98ab]">{formatDate(post.publishedAt ?? post.updatedAt)}</time>
                  </div>
                  <p className="mt-1.5 truncate text-[13px] font-semibold tracking-[-0.03em] text-[#30425f]">{post.title}</p>
                  <p className="mt-1 truncate text-[11px] text-[#8391a6]">/blog/{post.slug}</p>
                  <div className="mt-2 flex flex-wrap items-center gap-x-2 gap-y-1 text-[10px] text-[#75849b]">
                    <span className={`rounded-full px-2 py-1 font-semibold ${indexBadge.className}`}>
                      {indexBadge.label}
                    </span>
                    {post.status === "published" ? (
                      <>
                        <span>노출 {searchStatus?.impressions ?? 0}</span>
                        <span>클릭 {searchStatus?.clicks ?? 0}</span>
                        <span>
                          평균 {searchStatus?.impressions ? `${searchStatus.position.toFixed(1)}위` : "-"}
                        </span>
                      </>
                    ) : null}
                  </div>
                </button>
              );
            })}
          </div>
        </aside>

        <section className="min-w-0">
          <div className="flex flex-wrap items-center justify-between gap-2 border-b border-[#edf1f6] px-3 py-3 sm:px-4 lg:px-5">
            {isPlanningOpen ? (
              <div className="flex items-center gap-2">
                <button
                  type="button"
                  onClick={() => setIsPlanningOpen(false)}
                  className="inline-flex h-8 items-center gap-1.5 rounded-[9px] px-2.5 text-[12px] font-semibold text-[#71819a] transition-colors hover:bg-[#f4f7fb] hover:text-[#2f6df6]"
                >
                  <span aria-hidden="true">←</span>
                  글 작성으로 돌아가기
                </button>
              </div>
            ) : (
              <>
                <div className="flex items-center gap-2">
                  <button type="button" onClick={() => setIsPreviewOpen(false)} className={`h-8 rounded-[9px] px-2.5 text-[12px] font-semibold transition-colors ${!isPreviewOpen ? "bg-[#edf4ff] text-[#2f6df6]" : "text-[#71819a] hover:text-[#2f6df6]"}`}>작성</button>
                  <button type="button" onClick={() => setIsPreviewOpen(true)} className={`h-8 rounded-[9px] px-2.5 text-[12px] font-semibold transition-colors ${isPreviewOpen ? "bg-[#edf4ff] text-[#2f6df6]" : "text-[#71819a] hover:text-[#2f6df6]"}`}>미리보기</button>
                  {!isPreviewOpen ? (
                    <button
                      type="button"
                      onClick={toggleEditorWidth}
                      aria-pressed={isEditorFullWidth}
                      className="inline-flex h-8 items-center gap-1.5 rounded-[9px] px-2.5 text-[12px] font-semibold text-[#71819a] transition-colors hover:bg-[#f4f7fb] hover:text-[#2f6df6]"
                      title={isEditorFullWidth ? "본문을 기본 너비로 보기" : "본문을 전체 너비로 보기"}
                    >
                      <span aria-hidden="true">{isEditorFullWidth ? "↔" : "⇥"}</span>
                      <span className="hidden sm:inline">{isEditorFullWidth ? "전체 너비" : "기본 너비"}</span>
                    </button>
                  ) : null}
                  {published && draft.id ? (
                    <a href={`/blog/${draft.slug}`} target="_blank" rel="noreferrer" className="ml-1 text-[12px] font-semibold text-[#70819d] hover:text-[#2f6df6]">공개 글 열기 ↗</a>
                  ) : null}
                </div>
                <div className="flex items-center gap-2">
                  {draft.id ? <button type="button" onClick={deletePost} disabled={saving || isImageUploading} className="h-8 px-1 text-[12px] font-semibold text-[#8c9ab1] transition-colors hover:text-[#d24b63] disabled:cursor-not-allowed">삭제</button> : null}
                  <button type="button" onClick={() => save("draft")} disabled={saving || isImageUploading} className="om-btn-secondary h-8 rounded-[9px] px-3 text-[12px] shadow-none">{isImageUploading ? "이미지 업로드 중..." : saving ? "저장 중..." : "임시저장"}</button>
                  <button type="button" onClick={() => save("published")} disabled={saving || isImageUploading} className="om-btn-primary h-8 rounded-[9px] px-3 text-[12px] shadow-none">{isImageUploading ? "이미지 업로드 중..." : saving ? "저장 중..." : published ? "발행 내용 저장" : "발행하기"}</button>
                </div>
              </>
            )}
          </div>

          {isPlanningOpen ? (
            <ContentPlanner posts={posts} onStart={startContentIdea} />
          ) : (
            <>
          {draft.id ? (
            <div className="flex flex-wrap items-center justify-between gap-3 border-b border-[#edf1f6] px-3 py-2.5 sm:px-4 lg:px-5">
              <div className="flex min-w-0 flex-wrap items-center gap-x-3 gap-y-2 text-[11px] text-[#71819a]">
                <span className={`rounded-full px-2.5 py-1 font-semibold ${selectedIndexBadge.className}`}>
                  {selectedIndexBadge.label}
                </span>
                {published ? (
                  <>
                    <span className="font-semibold text-[#526784]">사이트맵 자동 포함</span>
                    <span className={selectedSearchStatus?.googleDetectedSitemap ? "text-[#278a50]" : "text-[#a96d13]"}>
                      {selectedSearchStatus?.googleDetectedSitemap
                        ? "Google 사이트맵 감지"
                        : "Google 사이트맵 감지 대기"}
                    </span>
                    <span>노출 {selectedSearchStatus?.impressions ?? 0}</span>
                    <span>클릭 {selectedSearchStatus?.clicks ?? 0}</span>
                    <span>
                      평균 순위 {selectedSearchStatus?.impressions ? `${selectedSearchStatus.position.toFixed(1)}위` : "-"}
                    </span>
                    <span title={selectedSearchStatus?.coverageState ?? undefined}>
                      최근 확인 {formatInspectionDate(selectedSearchStatus?.lastInspectedAt ?? null)}
                    </span>
                  </>
                ) : (
                  <span>발행하면 `/sitemap.xml`에 자동으로 포함됩니다.</span>
                )}
              </div>
              {published ? (
                <div className="flex shrink-0 items-center gap-2">
                  {selectedSearchStatus?.inspectionResultLink ? (
                    <a
                      href={selectedSearchStatus.inspectionResultLink}
                      target="_blank"
                      rel="noreferrer"
                      className="text-[11px] font-semibold text-[#647792] hover:text-[#2f6df6]"
                    >
                      Search Console 상세 ↗
                    </a>
                  ) : null}
                  <button
                    type="button"
                    onClick={inspectSearchStatus}
                    disabled={inspectingPostId === draft.id || !searchConsole?.connected}
                    className="h-8 rounded-[9px] px-2.5 text-[11px] font-semibold text-[#2f6df6] transition-colors hover:bg-[#edf4ff] disabled:cursor-not-allowed disabled:text-[#a6b1c2]"
                  >
                    {inspectingPostId === draft.id ? "확인 중..." : "색인 상태 확인"}
                  </button>
                </div>
              ) : null}
            </div>
          ) : null}

          <div className="flex flex-col gap-3 border-b border-[#edf1f6] px-3 py-3 sm:flex-row sm:items-center sm:justify-between sm:px-4 lg:px-5">
            <div className="min-w-0">
              <div className="flex flex-wrap items-center gap-2">
                <span className="text-[12px] font-semibold text-[#30425f]">Blogger</span>
                {bloggerStatus?.permissionGranted ? (
                  <span className="text-[11px] font-semibold text-[#278a50]">발행 연결됨</span>
                ) : (
                  <span className="text-[11px] font-semibold text-[#a96d13]">권한 연결 필요</span>
                )}
                {bloggerStatus?.post ? (
                  <span className={`text-[11px] font-semibold ${
                    bloggerStatus.post.status === "published"
                      ? "text-[#2f6df6]"
                      : bloggerStatus.post.status === "failed"
                        ? "text-[#c84d4d]"
                        : "text-[#71819a]"
                  }`}>
                    {bloggerStatus.post.status === "published"
                      ? bloggerStatus.post.sourceChanged ? "공개됨 · 동기화 필요" : "공개됨"
                      : bloggerStatus.post.status === "failed" ? "처리 실패" : "초안 저장됨"}
                  </span>
                ) : null}
              </div>
              <p className="mt-1 truncate text-[11px] text-[#8492a8]">
                {bloggerStatus?.blogUrl
                  ? `${bloggerStatus.blogName} · ${bloggerStatus.blogUrl}`
                  : "오피셜메일 글을 40~60% 분량으로 재구성해 외부 초안으로 보냅니다."}
              </p>
              {bloggerStatus?.post?.lastError ? (
                <p className="mt-1 truncate text-[11px] text-[#c84d4d]">{bloggerStatus.post.lastError}</p>
              ) : null}
            </div>
            <div className="flex shrink-0 flex-wrap items-center gap-2">
              {bloggerStatus?.blogUrl ? (
                <a href={bloggerStatus.blogUrl} target="_blank" rel="noreferrer" className="text-[11px] font-semibold text-[#71819a] hover:text-[#2f6df6]">블로그 열기 ↗</a>
              ) : null}
              {!bloggerStatus?.permissionGranted ? (
                <Link href="/api/integrations/google-search-console/authorize?returnTo=blog" className="om-btn-secondary inline-flex h-8 items-center rounded-[9px] px-3 text-[11px] shadow-none">Google 발행 권한 연결</Link>
              ) : draft.id && published ? (
                <>
                  {bloggerStatus.post ? (
                    <a href={bloggerStatus.post.externalUrl || bloggerStatus.post.editUrl} target="_blank" rel="noreferrer" className="text-[11px] font-semibold text-[#71819a] hover:text-[#2f6df6]">{bloggerStatus.post.status === "published" ? "외부 글 열기" : "초안 편집"} ↗</a>
                  ) : null}
                  <button type="button" onClick={() => runBloggerAction("sync")} disabled={bloggerLoading} className="om-btn-secondary h-8 rounded-[9px] px-3 text-[11px] shadow-none disabled:cursor-not-allowed">{bloggerLoading ? "처리 중..." : bloggerStatus.post ? "내용 동기화" : "Blogger 초안 만들기"}</button>
                  {bloggerStatus.post?.status !== "published" ? (
                    <button type="button" onClick={() => runBloggerAction("publish")} disabled={bloggerLoading} className="om-btn-primary h-8 rounded-[9px] px-3 text-[11px] shadow-none disabled:cursor-not-allowed">Blogger에 공개</button>
                  ) : null}
                </>
              ) : (
                <span className="text-[11px] text-[#8a98ad]">내부 글 발행 후 초안을 만들 수 있습니다.</span>
              )}
            </div>
          </div>

          <div className="flex flex-col gap-3 border-b border-[#edf1f6] px-3 py-3 sm:flex-row sm:items-center sm:justify-between sm:px-4 lg:px-5">
            <div className="min-w-0">
              <div className="flex flex-wrap items-center gap-2">
                <span className="text-[12px] font-semibold text-[#30425f]">WordPress.com</span>
                {wordpressStatus?.connected ? (
                  <span className="text-[11px] font-semibold text-[#278a50]">발행 연결됨</span>
                ) : (
                  <span className="text-[11px] font-semibold text-[#a96d13]">
                    {wordpressStatus?.configured ? "연결 확인 필요" : "서버 설정 필요"}
                  </span>
                )}
                {wordpressStatus?.post ? (
                  <span className={`text-[11px] font-semibold ${
                    wordpressStatus.post.status === "published"
                      ? "text-[#2f6df6]"
                      : wordpressStatus.post.status === "failed"
                        ? "text-[#c84d4d]"
                        : "text-[#71819a]"
                  }`}>
                    {wordpressStatus.post.status === "published"
                      ? wordpressStatus.post.sourceChanged ? "공개됨 · 동기화 필요" : "공개됨"
                      : wordpressStatus.post.status === "failed" ? "처리 실패" : "초안 저장됨"}
                  </span>
                ) : null}
              </div>
              <p className="mt-1 truncate text-[11px] text-[#8492a8]">
                {wordpressStatus?.blogUrl
                  ? `${wordpressStatus.blogName} · ${wordpressStatus.blogUrl}`
                  : "WordPress.com 정보 블로그에 재구성한 글과 원문 링크를 보냅니다."}
              </p>
              {wordpressStatus?.post?.lastError ? (
                <p className="mt-1 truncate text-[11px] text-[#c84d4d]">{wordpressStatus.post.lastError}</p>
              ) : null}
            </div>
            <div className="flex shrink-0 flex-wrap items-center gap-2">
              {wordpressStatus?.blogUrl ? (
                <a href={wordpressStatus.blogUrl} target="_blank" rel="noreferrer" className="text-[11px] font-semibold text-[#71819a] hover:text-[#2f6df6]">블로그 열기 ↗</a>
              ) : null}
              {wordpressStatus?.connected && draft.id && published ? (
                <>
                  {wordpressStatus.post ? (
                    <a href={wordpressStatus.post.externalUrl || wordpressStatus.post.editUrl} target="_blank" rel="noreferrer" className="text-[11px] font-semibold text-[#71819a] hover:text-[#2f6df6]">{wordpressStatus.post.status === "published" ? "외부 글 열기" : "초안 편집"} ↗</a>
                  ) : null}
                  <button type="button" onClick={() => runWordPressAction("sync")} disabled={wordpressLoading} className="om-btn-secondary h-8 rounded-[9px] px-3 text-[11px] shadow-none disabled:cursor-not-allowed">{wordpressLoading ? "처리 중..." : wordpressStatus.post ? "내용 동기화" : "WordPress 초안 만들기"}</button>
                  {wordpressStatus.post?.status !== "published" ? (
                    <button type="button" onClick={() => runWordPressAction("publish")} disabled={wordpressLoading} className="om-btn-primary h-8 rounded-[9px] px-3 text-[11px] shadow-none disabled:cursor-not-allowed">WordPress에 공개</button>
                  ) : null}
                </>
              ) : wordpressStatus?.connected ? (
                <span className="text-[11px] text-[#8a98ad]">내부 글 발행 후 초안을 만들 수 있습니다.</span>
              ) : (
                <span className="text-[11px] text-[#8a98ad]">서버 환경변수의 WordPress 연결 정보를 확인해 주세요.</span>
              )}
            </div>
          </div>

          {isPreviewOpen ? (
            <div className="min-h-[620px] px-3 py-6 sm:px-4 lg:px-8 lg:py-9">
              <div className="mx-auto max-w-[820px]">
                <p className="text-[12px] font-semibold text-[#2f6df6]">미리보기</p>
                <h2 className="mt-3 text-[30px] font-semibold leading-[1.18] tracking-[-0.07em] text-[#16233c] sm:text-[42px]">{draft.title}</h2>
                <p className="mt-5 text-[16px] leading-8 text-[#667792]">{draft.seoDescription || draft.excerpt || "검색 결과에 표시할 설명을 입력하세요."}</p>
                <div className="mt-11 border-t border-[#e7edf7] pt-2"><MarkdownPreview content={draft.contentMarkdown} /></div>
              </div>
            </div>
          ) : (
            <div className="px-3 py-4 sm:px-4 lg:px-5">
              <div className="grid gap-x-8 gap-y-4 border-b border-[#edf1f6] pb-5 lg:grid-cols-2">
                <label className="block min-w-0"><span className="text-[12px] font-semibold text-[#7183a1]">글 제목</span><input value={draft.title} onChange={(event) => updateDraft("title", event.target.value)} className={editorFieldClass()} placeholder="검색어가 자연스럽게 들어간 제목" /></label>
                <label className="block min-w-0"><span className="text-[12px] font-semibold text-[#7183a1]">URL 슬러그</span><div className="flex items-center gap-1 border-b border-[#dfe7f3] focus-within:border-[#8eb5ff]"><span className="shrink-0 text-[13px] text-[#8b99ad]">/blog/</span><input value={draft.slug} onChange={(event) => updateDraft("slug", event.target.value.toLowerCase().replace(/\s+/g, "-"))} className="h-10 min-w-0 flex-1 bg-transparent text-[14px] text-[#1a2943] outline-none placeholder:text-[#9aa8bd]" placeholder="company-email-guide" /></div></label>
                <label className="block min-w-0 lg:col-span-2"><span className="text-[12px] font-semibold text-[#7183a1]">검색 설명</span><input value={draft.seoDescription} onChange={(event) => updateDraft("seoDescription", event.target.value)} className={editorFieldClass()} placeholder="검색 결과와 공유 카드에 보일 1~2문장 설명" /></label>
                <label className="block min-w-0 lg:col-span-2"><span className="text-[12px] font-semibold text-[#7183a1]">검색 키워드</span><input value={draft.keywordsText} onChange={(event) => updateDraft("keywordsText", event.target.value)} className={editorFieldClass()} placeholder="기업메일, 회사 이메일 주소 만들기, 도메인 메일" /></label>
                <label className="block min-w-0 lg:col-span-2"><span className="text-[12px] font-semibold text-[#7183a1]">목록 요약</span><input value={draft.excerpt} onChange={(event) => updateDraft("excerpt", event.target.value)} className={editorFieldClass()} placeholder="비워두면 본문에서 자동으로 요약합니다." /></label>
              </div>

              <div className="flex flex-col justify-between gap-3 border-b border-[#edf1f6] py-4 sm:flex-row sm:items-center">
                <div className="min-w-0">
                  <p className="text-[12px] font-semibold text-[#526784]">내부 링크 추천</p>
                  <p className="mt-1 truncate text-[11px] text-[#8492a8]">
                    {internalLinkSuggestions.length > 0
                      ? internalLinkSuggestions.map((post) => post.title).join(" · ")
                      : "연결할 발행 글이 더 쌓이면 자동으로 추천합니다."}
                  </p>
                </div>
                <button
                  type="button"
                  onClick={appendInternalLinks}
                  disabled={internalLinkSuggestions.length === 0}
                  className="om-btn-secondary h-9 shrink-0 rounded-[9px] px-3 text-[11px] shadow-none disabled:cursor-not-allowed disabled:text-[#a6b1c2]"
                >
                  추천 링크 본문에 반영
                </button>
              </div>

              <NotionBlogContentEditor
                draftToken={imageDraftToken}
                fullWidth={isEditorFullWidth}
                onImageUploadStatusChange={setIsImageUploading}
                value={draft.contentMarkdown}
                onChange={(value) => updateDraft("contentMarkdown", value)}
              />
            </div>
          )}
            </>
          )}
        </section>
      </div>

      {toast ? <BottomToast notice={toast} onClose={() => setToast(null)} /> : null}
    </div>
  );
}
