"use client";

import { useEffect, useEffectEvent, useState, type FormEvent } from "react";
import { CloseIconButton } from "@/components/close-icon-button";
import { MailAddressBookToolsPanel, type ContactToolsMode } from "@/components/mail-address-book-tools-panel";
import type { MailboxContact, MailboxContactSettings } from "@/lib/official-mail";

const CONTACTS_PER_PAGE = 30;

type EditorState = {
  autoMoveFolder: string;
  companyName: string;
  contactId: number | null;
  displayName: string;
  email: string;
  groupName: string;
  isVip: boolean;
  memo: string;
  moveExistingMessages: boolean;
  phone: string;
};

const EMPTY_EDITOR: EditorState = {
  autoMoveFolder: "",
  companyName: "",
  contactId: null,
  displayName: "",
  email: "",
  groupName: "",
  isVip: false,
  memo: "",
  moveExistingMessages: false,
  phone: ""
};

function editorFromContact(contact: MailboxContact): EditorState {
  return {
    autoMoveFolder: contact.autoMoveFolderSystemName ?? "",
    companyName: contact.companyName,
    contactId: contact.id,
    displayName: contact.displayName,
    email: contact.email,
    groupName: contact.groupName,
    isVip: contact.isVip,
    memo: contact.memo,
    moveExistingMessages: false,
    phone: contact.phone
  };
}

export function MailAddressBookPanel() {
  const [settings, setSettings] = useState<MailboxContactSettings | null>(null);
  const [loading, setLoading] = useState(true);
  const [saving, setSaving] = useState(false);
  const [query, setQuery] = useState("");
  const [group, setGroup] = useState("all");
  const [page, setPage] = useState(1);
  const [editor, setEditor] = useState<EditorState | null>(null);
  const [deleteTarget, setDeleteTarget] = useState<MailboxContact | null>(null);
  const [notice, setNotice] = useState<{ message: string; tone: "error" | "success" } | null>(null);
  const [selectedEmails, setSelectedEmails] = useState<Set<string>>(new Set());
  const [toolsMode, setToolsMode] = useState<ContactToolsMode | null>(null);
  const [addingGroup, setAddingGroup] = useState(false);
  const [newGroupName, setNewGroupName] = useState("");
  const [bulkGroupName, setBulkGroupName] = useState("");
  const [bulkDeleteOpen, setBulkDeleteOpen] = useState(false);

  const loadSettings = async () => {
    setLoading(true);
    try {
      const response = await fetch("/api/mailbox/contacts", { cache: "no-store" });
      const payload = (await response.json().catch(() => null)) as {
        message?: string;
        ok?: boolean;
        settings?: MailboxContactSettings;
      } | null;
      if (!response.ok || !payload?.ok || !payload.settings) throw new Error(payload?.message ?? "주소록을 불러오지 못했습니다.");
      setSettings(payload.settings);
    } catch (error) {
      setNotice({ message: error instanceof Error ? error.message : "주소록을 불러오지 못했습니다.", tone: "error" });
    } finally {
      setLoading(false);
    }
  };

  const loadSettingsEffect = useEffectEvent(loadSettings);

  useEffect(() => {
    const timer = window.setTimeout(() => void loadSettingsEffect(), 0);
    return () => window.clearTimeout(timer);
  }, []);

  useEffect(() => {
    if (!notice) return;
    const removeTimer = window.setTimeout(() => setNotice(null), 3_050);
    return () => window.clearTimeout(removeTimer);
  }, [notice]);

  const filteredContacts = (settings?.contacts ?? []).filter((contact) => {
    if (group === "__unnamed") {
      if (contact.isSaved) return false;
    } else if (group === "__vip") {
      if (!contact.isVip) return false;
    } else if (group === "__recent") {
      // Recent contacts are sorted below instead of filtered.
    } else if (group !== "all" && contact.groupName !== group) {
      return false;
    }
    const normalizedQuery = query.trim().toLowerCase();
    if (!normalizedQuery) return true;
    return [contact.displayName, contact.email, contact.companyName, contact.phone, contact.groupName]
      .join(" ")
      .toLowerCase()
      .includes(normalizedQuery);
  });
  const visibleContacts = group === "__recent"
    ? [...filteredContacts].sort((left, right) => Date.parse(right.updatedAt) - Date.parse(left.updatedAt))
    : filteredContacts;
  const pageCount = Math.max(1, Math.ceil(visibleContacts.length / CONTACTS_PER_PAGE));
  const currentPage = Math.min(page, pageCount);
  const pagedContacts = visibleContacts.slice(
    (currentPage - 1) * CONTACTS_PER_PAGE,
    currentPage * CONTACTS_PER_PAGE
  );
  const savedContactCount = settings?.contacts.filter((contact) => contact.isSaved).length ?? 0;
  const unnamedContactCount = settings?.contacts.filter((contact) => !contact.isSaved).length ?? 0;
  const vipContactCount = settings?.contacts.filter((contact) => contact.isVip).length ?? 0;
  const selectedContacts = (settings?.contacts ?? []).filter((contact) => selectedEmails.has(contact.email));
  const pageEmails = pagedContacts.map((contact) => contact.email);
  const allPageSelected = pageEmails.length > 0 && pageEmails.every((email) => selectedEmails.has(email));

  const submitContact = async (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    if (!editor || saving) return;
    setSaving(true);
    setNotice(null);
    try {
      const response = await fetch("/api/mailbox/contacts", {
        body: JSON.stringify(editor),
        headers: { "Content-Type": "application/json" },
        method: "POST"
      });
      const payload = (await response.json().catch(() => null)) as { contact?: MailboxContact; message?: string; ok?: boolean } | null;
      if (!response.ok || !payload?.ok || !payload.contact) throw new Error(payload?.message ?? "주소록을 저장하지 못했습니다.");
      setSettings((current) => {
        if (!current) return current;
        const next = current.contacts.filter((contact) => contact.email !== payload.contact!.email);
        next.push(payload.contact!);
        next.sort((a, b) => Number(b.isVip) - Number(a.isVip) || (a.displayName || a.email).localeCompare(b.displayName || b.email, "ko"));
        return {
          ...current,
          contacts: next,
          groups: [...new Set(next.map((contact) => contact.groupName).filter(Boolean))].sort((a, b) => a.localeCompare(b, "ko"))
        };
      });
      setEditor(null);
      setNotice({ message: "주소록을 저장했습니다.", tone: "success" });
    } catch (error) {
      setNotice({ message: error instanceof Error ? error.message : "주소록을 저장하지 못했습니다.", tone: "error" });
    } finally {
      setSaving(false);
    }
  };

  const deleteContact = async () => {
    if (!deleteTarget?.id || saving) return;
    setSaving(true);
    try {
      const response = await fetch("/api/mailbox/contacts", {
        body: JSON.stringify({ contactIds: [deleteTarget.id] }),
        headers: { "Content-Type": "application/json" },
        method: "DELETE"
      });
      const payload = (await response.json().catch(() => null)) as { message?: string; ok?: boolean } | null;
      if (!response.ok || !payload?.ok) throw new Error(payload?.message ?? "주소록을 삭제하지 못했습니다.");
      setDeleteTarget(null);
      setNotice({ message: "주소록에서 삭제했습니다.", tone: "success" });
      await loadSettings();
    } catch (error) {
      setNotice({ message: error instanceof Error ? error.message : "주소록을 삭제하지 못했습니다.", tone: "error" });
    } finally {
      setSaving(false);
    }
  };

  const toggleContactVip = async (contact: MailboxContact) => {
    const nextVip = !contact.isVip;
    setSettings((current) => current ? {
      ...current,
      contacts: current.contacts.map((item) => item.email === contact.email ? { ...item, isVip: nextVip } : item)
    } : current);
    try {
      const response = await fetch("/api/mailbox/contacts", {
        body: JSON.stringify({ ...editorFromContact(contact), isVip: nextVip }),
        headers: { "Content-Type": "application/json" },
        method: "POST"
      });
      const payload = (await response.json().catch(() => null)) as { contact?: MailboxContact; message?: string; ok?: boolean } | null;
      if (!response.ok || !payload?.ok || !payload.contact) throw new Error(payload?.message ?? "VIP 설정을 변경하지 못했습니다.");
      setSettings((current) => current ? {
        ...current,
        contacts: current.contacts.map((item) => item.email === contact.email ? payload.contact! : item)
      } : current);
      setNotice({ message: nextVip ? "중요 연락처로 지정했습니다." : "중요 연락처 지정을 해제했습니다.", tone: "success" });
    } catch (error) {
      setSettings((current) => current ? {
        ...current,
        contacts: current.contacts.map((item) => item.email === contact.email ? { ...item, isVip: contact.isVip } : item)
      } : current);
      setNotice({ message: error instanceof Error ? error.message : "VIP 설정을 변경하지 못했습니다.", tone: "error" });
    }
  };

  const createGroup = async (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    const groupName = newGroupName.trim();
    if (!groupName || saving) return;
    setSaving(true);
    try {
      const response = await fetch("/api/mailbox/contacts/tools", {
        body: JSON.stringify({ action: "group-create", groupName }),
        headers: { "Content-Type": "application/json" },
        method: "POST"
      });
      const payload = (await response.json().catch(() => null)) as { message?: string; ok?: boolean } | null;
      if (!response.ok || !payload?.ok) throw new Error(payload?.message ?? "그룹을 만들지 못했습니다.");
      setSettings((current) => current ? {
        ...current,
        groups: [...new Set([...current.groups, groupName])].sort((left, right) => left.localeCompare(right, "ko"))
      } : current);
      setGroup(groupName);
      setNewGroupName("");
      setAddingGroup(false);
      setNotice({ message: `그룹 '${groupName}'을 만들었습니다.`, tone: "success" });
    } catch (error) {
      setNotice({ message: error instanceof Error ? error.message : "그룹을 만들지 못했습니다.", tone: "error" });
    } finally {
      setSaving(false);
    }
  };

  const bulkUpdateContacts = async (operation: "delete" | "group" | "vip", options?: { groupName?: string; isVip?: boolean }) => {
    if (selectedContacts.length === 0 || saving) return;
    setSaving(true);
    try {
      const response = await fetch("/api/mailbox/contacts/tools", {
        body: JSON.stringify({ action: "bulk", contacts: selectedContacts, operation, ...options }),
        headers: { "Content-Type": "application/json" },
        method: "POST"
      });
      const payload = (await response.json().catch(() => null)) as { message?: string; ok?: boolean } | null;
      if (!response.ok || !payload?.ok) throw new Error(payload?.message ?? "선택한 연락처를 처리하지 못했습니다.");
      const count = selectedContacts.length;
      setSelectedEmails(new Set());
      setBulkDeleteOpen(false);
      setBulkGroupName("");
      await loadSettings();
      setNotice({
        message: operation === "delete"
          ? `${count}개의 연락처를 삭제했습니다.`
          : operation === "group"
            ? `${count}개의 연락처를 '${options?.groupName}' 그룹으로 이동했습니다.`
            : `${count}개의 중요 연락처 설정을 변경했습니다.`,
        tone: "success"
      });
    } catch (error) {
      setNotice({ message: error instanceof Error ? error.message : "선택한 연락처를 처리하지 못했습니다.", tone: "error" });
    } finally {
      setSaving(false);
    }
  };

  const handleToolsChanged = async (message: string) => {
    await loadSettings();
    setSelectedEmails(new Set());
    setNotice({ message, tone: "success" });
  };

  return (
    <div className="om-setup-content">
      <div className="om-setup-header">
        <div className="om-setup-header-copy">
          <h1 className="om-page-title text-[#16233c]">주소록 설정</h1>
          <p className="om-setup-header-description">메일에서 확인된 주소를 한곳에서 관리하고 이름, 그룹, VIP와 자동분류를 등록합니다.</p>
        </div>
      </div>

      {notice ? <div role="status" className={`om-floating-notice fixed top-20 right-4 z-[210] max-w-[min(360px,calc(100vw-32px))] rounded-[12px] border px-4 py-3 text-[13px] shadow-[0_16px_40px_rgba(20,38,72,0.16)] sm:right-6 ${notice.tone === "success" ? "border-[#cdebdc] bg-[#f2fbf6] text-[#23724b]" : "border-[#f3d4db] bg-[#fff5f7] text-[#ad4257]"}`}>{notice.message}</div> : null}

      <section className="om-setup-panel min-h-[560px] overflow-hidden bg-white">
        <div className="grid min-h-[560px] lg:grid-cols-[210px_minmax(0,1fr)]">
          <aside className="border-b border-[#e7edf7] bg-[#fbfcfe] lg:border-r lg:border-b-0">
            <div className="grid grid-cols-2 gap-2 border-b border-[#e7edf7] p-3">
              <button type="button" onClick={() => setEditor({ ...EMPTY_EDITOR })} className="om-btn-primary h-9 cursor-pointer rounded-[9px] px-2 text-[12px]">연락처 추가</button>
              <button type="button" onClick={() => { setAddingGroup(true); setNewGroupName(""); }} className="om-btn-secondary h-9 cursor-pointer rounded-[9px] px-2 text-[12px]">그룹 추가</button>
            </div>
            <nav aria-label="주소록 분류" className="grid grid-cols-2 gap-1 p-3 text-[13px] lg:block lg:space-y-1">
              {[
                { count: null, id: "__recent", label: "최근등록" },
                { count: vipContactCount, id: "__vip", label: "중요" },
                { count: settings?.contacts.length ?? 0, id: "all", label: "전체 연락처" },
                { count: unnamedContactCount, id: "__unnamed", label: "이름 없는 연락처" }
              ].map((item) => (
                <button key={item.id} type="button" onClick={() => { setGroup(item.id); setPage(1); }} className={`flex w-full cursor-pointer items-center justify-between rounded-[8px] px-3 py-2 text-left transition-colors ${group === item.id ? "bg-[#eaf2ff] font-semibold text-[#2f6df6]" : "text-[#475a77] hover:bg-white hover:text-[#2f6df6]"}`}>
                  <span>{item.label}</span>
                  {item.count === null ? null : <span className="text-[11px] text-[#7f90aa]">{item.count.toLocaleString("ko-KR")}</span>}
                </button>
              ))}
              <div className="col-span-2 mt-2 border-t border-[#e7edf7] pt-3 lg:mt-4">
                <p className="px-3 pb-1 text-[11px] font-semibold text-[#8a98ad]">그룹</p>
                {addingGroup ? <form onSubmit={(event) => void createGroup(event)} className="mx-2 mb-2 flex items-center gap-1"><input autoFocus maxLength={120} value={newGroupName} onChange={(event) => setNewGroupName(event.target.value)} onKeyDown={(event) => { if (event.key === "Escape") setAddingGroup(false); }} placeholder="새 그룹 이름" className="h-8 min-w-0 flex-1 rounded-[7px] border border-[#a9c2f8] bg-white px-2 text-[12px] outline-none" /><button type="submit" disabled={saving || !newGroupName.trim()} className="h-8 cursor-pointer rounded-[7px] bg-[#2f6df6] px-2 text-[11px] font-semibold text-white disabled:opacity-40">추가</button></form> : null}
                {(settings?.groups ?? []).length === 0 && !addingGroup ? <button type="button" onClick={() => setAddingGroup(true)} className="w-full cursor-pointer px-3 py-2 text-left text-[11px] text-[#9aa7ba] hover:text-[#2f6df6]">등록된 그룹 없음</button> : (settings?.groups ?? []).map((item) => (
                  <button key={item} type="button" onClick={() => { setGroup(item); setPage(1); }} className={`flex w-full cursor-pointer items-center justify-between rounded-[8px] px-3 py-2 text-left transition-colors ${group === item ? "bg-[#eaf2ff] font-semibold text-[#2f6df6]" : "text-[#475a77] hover:bg-white hover:text-[#2f6df6]"}`}>
                    <span className="truncate">{item}</span>
                    <span className="text-[11px] text-[#7f90aa]">{settings?.contacts.filter((contact) => contact.groupName === item).length ?? 0}</span>
                  </button>
                ))}
              </div>
              <div className="col-span-2 mt-2 border-t border-[#e7edf7] pt-3 lg:mt-4">
                <p className="px-3 pb-1 text-[11px] font-semibold text-[#8a98ad]">주소록 도구</p>
                {[
                  { id: "file" as const, label: "파일에서 불러오기" },
                  { id: "mail" as const, label: "메일에서 불러오기" },
                  { id: "export" as const, label: "연락처 저장 · 내보내기" },
                  { id: "duplicates" as const, label: "중복 연락처 정리" }
                ].map((item) => <button key={item.id} type="button" onClick={() => setToolsMode(item.id)} className={`flex w-full cursor-pointer items-center rounded-[8px] px-3 py-2 text-left text-[12px] transition-colors ${toolsMode === item.id ? "bg-[#eaf2ff] font-semibold text-[#2f6df6]" : "text-[#596b88] hover:bg-white hover:text-[#2f6df6]"}`}>{item.label}</button>)}
              </div>
            </nav>
          </aside>

          {toolsMode && settings ? <MailAddressBookToolsPanel mode={toolsMode} onChanged={handleToolsChanged} onClose={() => setToolsMode(null)} onError={(message) => setNotice({ message, tone: "error" })} selectedContacts={selectedContacts} settings={settings} /> : <div className="min-w-0">
            {selectedContacts.length > 0 ? <div className="flex flex-wrap items-center gap-2 border-b border-[#dfe8f8] bg-[#f4f8ff] px-3 py-2.5 sm:px-4">
              <span className="mr-1 text-[12px] font-semibold text-[#2f6df6]">{selectedContacts.length.toLocaleString("ko-KR")}개 선택</span>
              <button type="button" onClick={() => setBulkDeleteOpen(true)} className="h-8 cursor-pointer rounded-[7px] border border-[#d6e1f2] bg-white px-3 text-[12px] text-[#a55161] hover:border-[#efbbc5]">삭제</button>
              <button type="button" disabled={saving} onClick={() => void bulkUpdateContacts("vip", { isVip: true })} className="h-8 cursor-pointer rounded-[7px] border border-[#d6e1f2] bg-white px-3 text-[12px] text-[#435573] hover:text-[#2f6df6] disabled:opacity-50">중요 지정</button>
              <button type="button" disabled={saving} onClick={() => void bulkUpdateContacts("vip", { isVip: false })} className="h-8 cursor-pointer rounded-[7px] border border-[#d6e1f2] bg-white px-3 text-[12px] text-[#435573] hover:text-[#2f6df6] disabled:opacity-50">중요 해제</button>
              <select aria-label="선택 연락처 그룹 이동" value={bulkGroupName} onChange={(event) => { const value = event.target.value; setBulkGroupName(value); if (value) void bulkUpdateContacts("group", { groupName: value }); }} disabled={saving || (settings?.groups.length ?? 0) === 0} className="h-8 cursor-pointer rounded-[7px] border border-[#d6e1f2] bg-white px-2 text-[12px] text-[#435573] disabled:opacity-50"><option value="">그룹 이동</option>{(settings?.groups ?? []).map((item) => <option key={item} value={item}>{item}</option>)}</select>
              <button type="button" onClick={() => setToolsMode("export")} className="h-8 cursor-pointer rounded-[7px] border border-[#d6e1f2] bg-white px-3 text-[12px] text-[#435573] hover:text-[#2f6df6]">내보내기</button>
              <button type="button" onClick={() => setSelectedEmails(new Set())} className="ml-auto h-8 cursor-pointer px-2 text-[12px] text-[#74839b] hover:text-[#2f6df6]">선택 해제</button>
            </div> : null}
            <div className="flex flex-col gap-3 border-b border-[#e7edf7] px-3 py-3 sm:flex-row sm:items-center sm:justify-between sm:px-4">
              <div className="relative w-full max-w-[360px]">
                <input value={query} onChange={(event) => { setQuery(event.target.value); setPage(1); }} placeholder="연락처 검색" className="h-9 w-full rounded-[8px] border border-[#d8e1ef] bg-white px-3 pr-9 text-[13px] outline-none placeholder:text-[12px] placeholder:text-[#9aa7ba] focus:border-[#8fb2ff]" />
                <svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" className="pointer-events-none absolute top-1/2 right-3 h-4 w-4 -translate-y-1/2 text-[#8c9ab0]"><circle cx="11" cy="11" r="6" /><path d="m16 16 4 4" /></svg>
              </div>
              <div className="flex items-center gap-3 text-[12px] text-[#6f809a]">
                <span><strong className="font-semibold text-[#2f6df6]">{visibleContacts.length.toLocaleString("ko-KR")}</strong>명 표시</span>
                <span>이름 등록 {savedContactCount.toLocaleString("ko-KR")} / {settings?.limit ?? 5000}</span>
              </div>
            </div>

            <div className="hidden grid-cols-[34px_38px_minmax(105px,1fr)_minmax(180px,1.6fr)_minmax(90px,.8fr)_80px_86px] border-b border-[#e7edf7] bg-[#fafbfd] px-3 text-[12px] font-medium text-[#687991] md:grid">
              <label className="flex items-center justify-center py-2.5"><input aria-label="현재 페이지 전체 선택" type="checkbox" checked={allPageSelected} onChange={(event) => { const next = new Set(selectedEmails); for (const email of pageEmails) { if (event.target.checked) next.add(email); else next.delete(email); } setSelectedEmails(next); }} className="h-4 w-4 accent-[#2f6df6]" /></label><span className="py-2.5 text-center">중요</span><span className="py-2.5">이름</span><span className="py-2.5">이메일</span><span className="py-2.5">회사·소속</span><span className="py-2.5">그룹</span><span className="py-2.5 text-right">관리</span>
            </div>

            {loading ? (
              <div className="px-4 py-20 text-center text-[13px] text-[#8290a7]">주소록을 불러오고 있습니다.</div>
            ) : visibleContacts.length === 0 ? (
              <div className="px-4 py-20 text-center"><p className="text-[14px] font-semibold text-[#344767]">표시할 주소가 없습니다.</p><p className="mt-2 text-[12px] text-[#8290a7]">검색어나 주소록 분류를 바꿔보세요.</p></div>
            ) : (
              <>
                <div className="hidden divide-y divide-[#edf2f7] md:block">
                  {pagedContacts.map((contact) => (
                    <div key={contact.email} className="grid grid-cols-[34px_38px_minmax(105px,1fr)_minmax(180px,1.6fr)_minmax(90px,.8fr)_80px_86px] items-center px-3 text-[12px] transition-colors hover:bg-[#f8fbff]">
                      <label className="flex items-center justify-center py-2.5"><input aria-label={`${contact.displayName || contact.email} 선택`} type="checkbox" checked={selectedEmails.has(contact.email)} onChange={(event) => { const next = new Set(selectedEmails); if (event.target.checked) next.add(contact.email); else next.delete(contact.email); setSelectedEmails(next); }} className="h-4 w-4 accent-[#2f6df6]" /></label>
                      <button type="button" aria-label={contact.isVip ? "중요 연락처 해제" : "중요 연락처 지정"} onClick={() => void toggleContactVip(contact)} className={`cursor-pointer py-2.5 text-center text-[17px] transition-colors hover:text-[#ffb000] ${contact.isVip ? "text-[#ffb000]" : "text-[#c7d0dd]"}`}>★</button>
                      <button type="button" onClick={() => setEditor(editorFromContact(contact))} className="min-w-0 cursor-pointer truncate py-2.5 pr-3 text-left font-semibold text-[#263754] hover:text-[#2f6df6]">{contact.displayName || "이름없음"}</button>
                      <span className="min-w-0 truncate py-2.5 pr-3 text-[#445674]">{contact.email}</span>
                      <span className="min-w-0 truncate py-2.5 pr-3 text-[#6f809a]">{contact.companyName || "-"}</span>
                      <span className="min-w-0 truncate py-2.5 pr-2 text-[#6f809a]">{contact.groupName || "-"}</span>
                      <span className="flex justify-end gap-1 py-2"><button type="button" onClick={() => setEditor(editorFromContact(contact))} className="cursor-pointer px-1.5 py-1 font-semibold text-[#2f6df6]">{contact.isSaved ? "수정" : "등록"}</button>{contact.isSaved ? <button type="button" onClick={() => setDeleteTarget(contact)} className="cursor-pointer px-1.5 py-1 text-[#a55161]">삭제</button> : null}</span>
                    </div>
                  ))}
                </div>
                <div className="divide-y divide-[#edf2f7] md:hidden">
                  {pagedContacts.map((contact) => (
                    <div key={contact.email} className="flex items-start gap-3 px-4 py-3 hover:bg-[#f8fbff]">
                      <label className="mt-1 flex items-center"><input aria-label={`${contact.displayName || contact.email} 선택`} type="checkbox" checked={selectedEmails.has(contact.email)} onChange={(event) => { const next = new Set(selectedEmails); if (event.target.checked) next.add(contact.email); else next.delete(contact.email); setSelectedEmails(next); }} className="h-4 w-4 accent-[#2f6df6]" /></label>
                      <button type="button" aria-label={contact.isVip ? "중요 연락처 해제" : "중요 연락처 지정"} onClick={() => void toggleContactVip(contact)} className={`mt-0.5 cursor-pointer text-[16px] transition-colors hover:text-[#ffb000] ${contact.isVip ? "text-[#ffb000]" : "text-[#c7d0dd]"}`}>★</button>
                      <button type="button" onClick={() => setEditor(editorFromContact(contact))} className="min-w-0 flex-1 cursor-pointer text-left"><span className="block truncate text-[13px] font-semibold text-[#263754]">{contact.displayName || "이름없음"}</span><span className="mt-0.5 block truncate text-[12px] text-[#6f809a]">{contact.email}</span><span className="mt-1 block text-[11px] text-[#8a98ad]">{[contact.companyName, contact.groupName].filter(Boolean).join(" · ") || (contact.isSaved ? "등록된 연락처" : "이름 미등록")}</span></button>
                    </div>
                  ))}
                </div>
              </>
            )}
            {!loading && visibleContacts.length > CONTACTS_PER_PAGE ? <div className="flex items-center justify-center gap-3 border-t border-[#edf2fb] px-4 py-3"><button type="button" disabled={currentPage <= 1} onClick={() => setPage((value) => Math.max(1, value - 1))} className="h-8 cursor-pointer rounded-[8px] border border-[#dbe5f7] px-3 text-[12px] text-[#40516e] disabled:cursor-default disabled:opacity-40">이전</button><span className="text-[12px] font-medium text-[#60728e]">{currentPage} / {pageCount}</span><button type="button" disabled={currentPage >= pageCount} onClick={() => setPage((value) => Math.min(pageCount, value + 1))} className="h-8 cursor-pointer rounded-[8px] border border-[#dbe5f7] px-3 text-[12px] text-[#40516e] disabled:cursor-default disabled:opacity-40">다음</button></div> : null}
          </div>}
        </div>
      </section>

      {editor ? (
        <div className="fixed inset-0 z-[180] flex items-center justify-center bg-[rgba(12,18,31,0.42)] px-4 py-6 backdrop-blur-[3px]" onClick={() => !saving && setEditor(null)}>
          <form onSubmit={submitContact} className="max-h-full w-full max-w-[640px] overflow-y-auto rounded-[22px] bg-white shadow-[0_30px_80px_rgba(15,28,58,0.22)]" onClick={(event) => event.stopPropagation()}>
            <div className="flex items-start justify-between border-b border-[#edf2fb] px-5 py-4">
              <div><h2 className="text-[18px] font-semibold text-[#16233c]">{editor.contactId ? "주소록 수정" : editor.email ? "이름 등록" : "주소록 추가"}</h2><p className="mt-1 text-[12px] text-[#74839b]">메일에서 확인된 주소에 이름과 그룹을 등록하고 필요하면 자동분류를 연결합니다.</p></div>
              <CloseIconButton ariaLabel="주소록 편집 닫기" onClick={() => !saving && setEditor(null)} />
            </div>
            <div className="grid gap-4 px-5 py-5 sm:grid-cols-2">
              <label className="block"><span className="mb-1.5 block text-[12px] font-semibold text-[#4f6284]">이름</span><input value={editor.displayName} onChange={(event) => setEditor({ ...editor, displayName: event.target.value })} className="h-10 w-full rounded-[10px] border border-[#dbe5f7] px-3 text-[13px] outline-none focus:border-[#9cbcff]" /></label>
              <label className="block"><span className="mb-1.5 block text-[12px] font-semibold text-[#4f6284]">이메일 *</span><input required type="email" value={editor.email} onChange={(event) => setEditor({ ...editor, email: event.target.value })} className="h-10 w-full rounded-[10px] border border-[#dbe5f7] px-3 text-[13px] outline-none focus:border-[#9cbcff]" /></label>
              <label className="block"><span className="mb-1.5 block text-[12px] font-semibold text-[#4f6284]">회사</span><input value={editor.companyName} onChange={(event) => setEditor({ ...editor, companyName: event.target.value })} className="h-10 w-full rounded-[10px] border border-[#dbe5f7] px-3 text-[13px] outline-none focus:border-[#9cbcff]" /></label>
              <label className="block"><span className="mb-1.5 block text-[12px] font-semibold text-[#4f6284]">연락처</span><input value={editor.phone} onChange={(event) => setEditor({ ...editor, phone: event.target.value })} className="h-10 w-full rounded-[10px] border border-[#dbe5f7] px-3 text-[13px] outline-none focus:border-[#9cbcff]" /></label>
              <label className="block"><span className="mb-1.5 block text-[12px] font-semibold text-[#4f6284]">그룹</span><input list="contact-groups" value={editor.groupName} onChange={(event) => setEditor({ ...editor, groupName: event.target.value })} placeholder="예: 거래처" className="h-10 w-full rounded-[10px] border border-[#dbe5f7] px-3 text-[13px] outline-none placeholder:text-[12px] focus:border-[#9cbcff]" /><datalist id="contact-groups">{(settings?.groups ?? []).map((item) => <option key={item} value={item} />)}</datalist></label>
              <label className="block"><span className="mb-1.5 block text-[12px] font-semibold text-[#4f6284]">자동분류 메일함</span><select value={editor.autoMoveFolder} onChange={(event) => setEditor({ ...editor, autoMoveFolder: event.target.value })} className="h-10 w-full cursor-pointer rounded-[10px] border border-[#dbe5f7] bg-white px-3 text-[13px] outline-none"><option value="">사용 안 함</option>{(settings?.folders ?? []).map((folder) => <option key={folder.id} value={folder.systemName}>{folder.name}</option>)}</select></label>
              <label className="sm:col-span-2"><span className="mb-1.5 block text-[12px] font-semibold text-[#4f6284]">메모</span><textarea rows={3} value={editor.memo} onChange={(event) => setEditor({ ...editor, memo: event.target.value })} className="w-full resize-none rounded-[10px] border border-[#dbe5f7] px-3 py-2 text-[13px] outline-none focus:border-[#9cbcff]" /></label>
              <label className="flex cursor-pointer items-center gap-2 text-[12px] text-[#40516e]"><input type="checkbox" checked={editor.isVip} onChange={(event) => setEditor({ ...editor, isVip: event.target.checked })} className="h-4 w-4 accent-[#2f6df6]" />VIP로 지정</label>
              {editor.autoMoveFolder ? <label className="flex cursor-pointer items-center gap-2 text-[12px] text-[#40516e]"><input type="checkbox" checked={editor.moveExistingMessages} onChange={(event) => setEditor({ ...editor, moveExistingMessages: event.target.checked })} className="h-4 w-4 accent-[#2f6df6]" />이미 받은 메일도 함께 이동</label> : null}
            </div>
            <div className="flex justify-end gap-2 border-t border-[#edf2fb] px-5 py-4"><button type="button" disabled={saving} onClick={() => setEditor(null)} className="om-btn-secondary h-10 cursor-pointer rounded-[11px] px-4 text-[13px]">취소</button><button type="submit" disabled={saving} className="om-btn-primary h-10 cursor-pointer rounded-[11px] px-5 text-[13px] disabled:opacity-60">{saving ? "저장 중..." : "저장"}</button></div>
          </form>
        </div>
      ) : null}

      {deleteTarget ? (
        <div className="fixed inset-0 z-[190] flex items-center justify-center bg-[rgba(12,18,31,0.42)] px-4 backdrop-blur-[3px]" onClick={() => !saving && setDeleteTarget(null)}>
          <div className="w-full max-w-[420px] rounded-[20px] bg-white p-6 text-center shadow-[0_30px_80px_rgba(15,28,58,0.22)]" onClick={(event) => event.stopPropagation()}>
            <h2 className="text-[18px] font-semibold text-[#16233c]">주소록에서 삭제할까요?</h2><p className="mt-2 text-[13px] leading-6 text-[#71819b]">{deleteTarget.displayName || deleteTarget.email}의 주소록 정보와 연결된 자동분류 규칙이 삭제됩니다.</p>
            <div className="mt-6 flex justify-center gap-2"><button type="button" onClick={() => setDeleteTarget(null)} className="om-btn-secondary h-10 cursor-pointer rounded-[11px] px-4 text-[13px]">취소</button><button type="button" onClick={() => void deleteContact()} className="h-10 cursor-pointer rounded-[11px] bg-[#e55264] px-4 text-[13px] font-semibold text-white">삭제</button></div>
          </div>
        </div>
      ) : null}

      {bulkDeleteOpen ? (
        <div className="fixed inset-0 z-[190] flex items-center justify-center bg-[rgba(12,18,31,0.42)] px-4 backdrop-blur-[3px]" onClick={() => !saving && setBulkDeleteOpen(false)}>
          <div className="w-full max-w-[420px] rounded-[20px] bg-white p-6 text-center shadow-[0_30px_80px_rgba(15,28,58,0.22)]" onClick={(event) => event.stopPropagation()}>
            <h2 className="text-[18px] font-semibold text-[#16233c]">선택한 연락처를 삭제할까요?</h2><p className="mt-2 text-[13px] leading-6 text-[#71819b]">선택한 {selectedContacts.length.toLocaleString("ko-KR")}개 주소가 목록에서 제거됩니다. 이후 직접 다시 등록할 수 있습니다.</p>
            <div className="mt-6 flex justify-center gap-2"><button type="button" disabled={saving} onClick={() => setBulkDeleteOpen(false)} className="om-btn-secondary h-10 cursor-pointer rounded-[11px] px-4 text-[13px]">취소</button><button type="button" disabled={saving} onClick={() => void bulkUpdateContacts("delete")} className="h-10 cursor-pointer rounded-[11px] bg-[#e55264] px-4 text-[13px] font-semibold text-white disabled:opacity-50">{saving ? "삭제 중..." : "삭제"}</button></div>
          </div>
        </div>
      ) : null}
    </div>
  );
}
