"use client";

import { useEffect, useState } from "react";
import { CloseIconButton } from "@/components/close-icon-button";
import type { MailboxContact, MailboxContactSettings } from "@/lib/official-mail";

export type ComposeAddressBookRecipient = {
  email: string;
  label: string;
};

export type ComposeAddressBookSelection = {
  bcc: ComposeAddressBookRecipient[];
  cc: ComposeAddressBookRecipient[];
  to: ComposeAddressBookRecipient[];
};

type RecipientBucket = keyof ComposeAddressBookSelection;

type MailComposeAddressBookDialogProps = {
  initialSelection: ComposeAddressBookSelection;
  onClose: () => void;
  onConfirm: (selection: ComposeAddressBookSelection) => void;
};

const bucketLabels: Record<RecipientBucket, string> = {
  bcc: "숨은 참조",
  cc: "참조",
  to: "받는 사람",
};

function contactLabel(contact: MailboxContact) {
  const name = contact.displayName.trim();
  return name ? `${name} <${contact.email}>` : contact.email;
}

function contactToRecipient(contact: MailboxContact): ComposeAddressBookRecipient {
  return {
    email: contact.email.toLowerCase(),
    label: contactLabel(contact),
  };
}

function mergeRecipients(
  current: ComposeAddressBookRecipient[],
  incoming: ComposeAddressBookRecipient[],
) {
  const seen = new Set(current.map((recipient) => recipient.email.toLowerCase()));
  const next = [...current];

  incoming.forEach((recipient) => {
    const email = recipient.email.toLowerCase();

    if (!seen.has(email)) {
      seen.add(email);
      next.push({ ...recipient, email });
    }
  });

  return next;
}

function cloneSelection(selection: ComposeAddressBookSelection): ComposeAddressBookSelection {
  return {
    bcc: [...selection.bcc],
    cc: [...selection.cc],
    to: [...selection.to],
  };
}

function RecipientPanel({
  active,
  bucket,
  dragActive,
  recipients,
  onActivate,
  onDragEnd,
  onDragHover,
  onDragStart,
  onDrop,
  onRemove,
}: {
  active: boolean;
  bucket: RecipientBucket;
  dragActive: boolean;
  recipients: ComposeAddressBookRecipient[];
  onActivate: () => void;
  onDragEnd: () => void;
  onDragHover: () => void;
  onDragStart: (recipient: ComposeAddressBookRecipient) => void;
  onDrop: () => void;
  onRemove: (email: string) => void;
}) {
  return (
    <section
      aria-label={`${bucketLabels[bucket]} ${recipients.length}명`}
      className={`min-h-[124px] cursor-pointer border transition-colors lg:min-h-0 lg:flex-1 ${
        dragActive
          ? "border-[#2f6df6] bg-[#edf4ff] shadow-[inset_0_0_0_2px_rgba(47,109,246,0.2)]"
          : active
          ? "border-transparent bg-[#f7faff] shadow-[inset_3px_0_0_#2f6df6]"
          : "border-transparent bg-white hover:bg-[#fbfcff]"
      }`}
      onClick={onActivate}
      onDragEnter={(event) => {
        event.preventDefault();
        onActivate();
        onDragHover();
      }}
      onDragOver={(event) => {
        event.preventDefault();
        event.dataTransfer.dropEffect = "move";
      }}
      onDrop={(event) => {
        event.preventDefault();
        onDrop();
      }}
    >
      <div className="flex items-center justify-between border-b border-[#edf1f7] px-4 py-3">
        <p className="text-[13px] font-semibold text-[#1d2b43]">
          {bucketLabels[bucket]} <span className="text-[#2f6df6]">{recipients.length}</span>
        </p>
        {active ? (
          <span className="rounded-full bg-[#e8f1ff] px-2 py-1 text-[10px] font-semibold text-[#2f6df6]">
            추가 위치
          </span>
        ) : null}
      </div>
      <div className="max-h-[132px] overflow-y-auto p-2 lg:max-h-[156px]">
        {recipients.length > 0 ? (
          recipients.map((recipient) => (
            <div
              key={recipient.email}
              draggable
              onDragStart={(event) => {
                event.dataTransfer.effectAllowed = "move";
                event.dataTransfer.setData("text/plain", recipient.email);
                onDragStart(recipient);
              }}
              onDragEnd={onDragEnd}
              className="group flex cursor-grab items-center gap-2 rounded-[9px] px-2 py-1.5 hover:bg-white active:cursor-grabbing"
            >
              <span className="min-w-0 flex-1 truncate text-[12px] text-[#455975]">
                {recipient.label}
              </span>
              <button
                type="button"
                aria-label={`${recipient.label} 삭제`}
                onClick={(event) => {
                  event.stopPropagation();
                  onRemove(recipient.email);
                }}
                className="inline-flex h-6 w-6 shrink-0 cursor-pointer items-center justify-center rounded-full text-[#99a8bc] transition-colors hover:bg-[#edf3fd] hover:text-[#2f6df6]"
              >
                ×
              </button>
            </div>
          ))
        ) : (
          <button
            type="button"
            onClick={onActivate}
            className="flex min-h-[72px] w-full cursor-pointer items-center justify-center rounded-[10px] border border-dashed border-[#dce5f2] px-3 text-[11px] leading-5 text-[#94a1b3]"
          >
            이 영역을 선택한 뒤 연락처를 추가하세요.
          </button>
        )}
      </div>
    </section>
  );
}

export function MailComposeAddressBookDialog({
  initialSelection,
  onClose,
  onConfirm,
}: MailComposeAddressBookDialogProps) {
  const [settings, setSettings] = useState<MailboxContactSettings | null>(null);
  const [loading, setLoading] = useState(true);
  const [loadError, setLoadError] = useState<string | null>(null);
  const [query, setQuery] = useState("");
  const [category, setCategory] = useState("recent");
  const [activeBucket, setActiveBucket] = useState<RecipientBucket>("to");
  const [checkedEmails, setCheckedEmails] = useState<Set<string>>(new Set());
  const [draggedRecipients, setDraggedRecipients] = useState<ComposeAddressBookRecipient[]>([]);
  const [dragOverBucket, setDragOverBucket] = useState<RecipientBucket | null>(null);
  const [dragSourceBucket, setDragSourceBucket] = useState<RecipientBucket | null>(null);
  const [dragOverContactList, setDragOverContactList] = useState(false);
  const [selection, setSelection] = useState(() => cloneSelection(initialSelection));

  useEffect(() => {
    const previousOverflow = document.body.style.overflow;
    document.body.style.overflow = "hidden";

    const controller = new AbortController();

    void (async () => {
      try {
        const response = await fetch("/api/mailbox/contacts", {
          cache: "no-store",
          credentials: "same-origin",
          signal: controller.signal,
        });
        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) {
        if (error instanceof DOMException && error.name === "AbortError") {
          return;
        }

        setLoadError(error instanceof Error ? error.message : "주소록을 불러오지 못했습니다.");
      } finally {
        if (!controller.signal.aborted) {
          setLoading(false);
        }
      }
    })();

    const handleKeyDown = (event: KeyboardEvent) => {
      if (event.key === "Escape") {
        onClose();
      }
    };

    window.addEventListener("keydown", handleKeyDown);

    return () => {
      controller.abort();
      document.body.style.overflow = previousOverflow;
      window.removeEventListener("keydown", handleKeyDown);
    };
  }, [onClose]);

  const contacts = settings?.contacts ?? [];
  const savedContacts = contacts.filter((contact) => contact.isSaved);
  const vipContacts = contacts.filter((contact) => contact.isVip);
  const recentContacts = [...contacts].sort(
    (left, right) => Date.parse(right.updatedAt) - Date.parse(left.updatedAt),
  );
  const categories = [
    { count: recentContacts.length, id: "recent", label: "최근 사용한 주소" },
    { count: vipContacts.length, id: "vip", label: "VIP" },
    { count: savedContacts.length, id: "saved", label: "내 주소록" },
    ...(settings?.groups ?? []).map((groupName) => ({
      count: contacts.filter((contact) => contact.groupName === groupName).length,
      id: `group:${groupName}`,
      label: groupName,
    })),
  ];

  const visibleContacts = (() => {
    const categoryContacts = category === "recent"
      ? recentContacts
      : category === "vip"
        ? vipContacts
        : category === "saved"
          ? savedContacts
          : category.startsWith("group:")
            ? contacts.filter((contact) => contact.groupName === category.slice(6))
            : contacts;
    const normalizedQuery = query.trim().toLowerCase();

    if (!normalizedQuery) {
      return categoryContacts;
    }

    return categoryContacts.filter((contact) =>
      [contact.displayName, contact.email, contact.companyName, contact.groupName]
        .join(" ")
        .toLowerCase()
        .includes(normalizedQuery),
    );
  })();

  const allVisibleChecked =
    visibleContacts.length > 0 &&
    visibleContacts.every((contact) => checkedEmails.has(contact.email.toLowerCase()));

  const toggleChecked = (email: string) => {
    const normalizedEmail = email.toLowerCase();
    setCheckedEmails((current) => {
      const next = new Set(current);

      if (next.has(normalizedEmail)) {
        next.delete(normalizedEmail);
      } else {
        next.add(normalizedEmail);
      }

      return next;
    });
  };

  const addRecipientsToBucket = (
    bucket: RecipientBucket,
    incoming: ComposeAddressBookRecipient[],
  ) => {
    if (incoming.length === 0) {
      return;
    }

    const incomingEmails = new Set(incoming.map((recipient) => recipient.email));

    setSelection((current) => {
      const next = cloneSelection(current);

      (Object.keys(next) as RecipientBucket[]).forEach((bucket) => {
        next[bucket] = next[bucket].filter(
          (recipient) => !incomingEmails.has(recipient.email.toLowerCase()),
        );
      });
      next[bucket] = mergeRecipients(next[bucket], incoming);

      return next;
    });
  };

  const addCheckedToActiveBucket = () => {
    const incoming = contacts
      .filter((contact) => checkedEmails.has(contact.email.toLowerCase()))
      .map(contactToRecipient);

    addRecipientsToBucket(activeBucket, incoming);
    if (incoming.length > 0) {
      setCheckedEmails(new Set());
    }
  };

  const finishDragging = () => {
    setDraggedRecipients([]);
    setDragOverBucket(null);
    setDragSourceBucket(null);
    setDragOverContactList(false);
  };

  const dropRecipientsIntoBucket = (bucket: RecipientBucket) => {
    addRecipientsToBucket(bucket, draggedRecipients);
    if (draggedRecipients.length > 0) {
      const movedEmails = new Set(draggedRecipients.map((recipient) => recipient.email.toLowerCase()));
      setCheckedEmails((current) => {
        const next = new Set(current);
        movedEmails.forEach((email) => next.delete(email));
        return next;
      });
    }
    finishDragging();
  };

  const removeDraggedRecipients = () => {
    if (!dragSourceBucket || draggedRecipients.length === 0) {
      finishDragging();
      return;
    }

    const removedEmails = new Set(
      draggedRecipients.map((recipient) => recipient.email.toLowerCase()),
    );
    setSelection((current) => ({
      ...current,
      [dragSourceBucket]: current[dragSourceBucket].filter(
        (recipient) => !removedEmails.has(recipient.email.toLowerCase()),
      ),
    }));
    finishDragging();
  };

  const removeRecipient = (bucket: RecipientBucket, email: string) => {
    setSelection((current) => ({
      ...current,
      [bucket]: current[bucket].filter((recipient) => recipient.email !== email),
    }));
  };

  const selectedCount = checkedEmails.size;

  return (
    <div
      className="fixed inset-0 z-[2147483000] flex items-center justify-center bg-[rgba(15,23,42,0.28)] p-3 backdrop-blur-[3px] sm:p-6"
      role="presentation"
      onMouseDown={(event) => {
        if (event.target === event.currentTarget) {
          onClose();
        }
      }}
    >
      <div
        role="dialog"
        aria-modal="true"
        aria-labelledby="compose-address-book-title"
        className="flex h-[calc(100dvh-24px)] w-full max-w-[1120px] flex-col overflow-hidden rounded-[22px] border border-[#dce5f2] bg-white shadow-[0_32px_90px_rgba(15,23,42,0.24)] sm:h-[min(760px,calc(100dvh-48px))]"
      >
        <header className="flex shrink-0 flex-wrap items-center gap-4 border-b border-[#e9eef6] px-4 py-4 sm:flex-nowrap sm:px-6">
          <div className="min-w-0 flex-1">
            <h2 id="compose-address-book-title" className="text-[18px] font-semibold tracking-[-0.04em] text-[#17243b]">
              메일 주소록
            </h2>
            <p className="mt-1 hidden text-[12px] text-[#7b8ba3] sm:block">
              연락처를 고른 뒤 받는 사람, 참조 또는 숨은 참조 영역에 추가하세요.
            </p>
          </div>
          <label className="relative order-3 block w-full sm:order-none sm:max-w-[320px]">
            <span className="sr-only">메일 주소록 검색</span>
            <input
              autoFocus
              type="search"
              value={query}
              onChange={(event) => setQuery(event.currentTarget.value)}
              placeholder="이름, 회사 또는 이메일 검색"
              className="h-10 w-full rounded-full border border-[#e2e8f2] bg-[#f6f8fb] pl-4 pr-10 text-[13px] text-[#25354f] outline-none placeholder:text-[#a0acbd] focus:border-[#aac4fb] focus:bg-white"
            />
            <svg viewBox="0 0 24 24" fill="none" className="pointer-events-none absolute right-3.5 top-1/2 h-4 w-4 -translate-y-1/2 text-[#8d9bb0]" aria-hidden="true">
              <circle cx="11" cy="11" r="6" stroke="currentColor" strokeWidth="1.7" />
              <path d="m16 16 4 4" stroke="currentColor" strokeLinecap="round" strokeWidth="1.7" />
            </svg>
          </label>
          <a
            href="/mail?panel=setup&section=contacts"
            target="_blank"
            rel="noreferrer"
            className="hidden shrink-0 text-[12px] font-semibold text-[#637694] hover:text-[#2f6df6] md:block"
          >
            주소록 관리 ↗
          </a>
          <CloseIconButton ariaLabel="메일 주소록 닫기" onClick={onClose} />
        </header>

        <div className="grid min-h-0 flex-1 overflow-x-hidden overflow-y-auto lg:grid-cols-[210px_minmax(0,1fr)_330px] lg:overflow-hidden">
          <nav aria-label="주소록 분류" className="border-b border-[#e9eef5] bg-[#f8fafd] lg:overflow-y-auto lg:border-b-0 lg:border-r">
            <div className="flex gap-1 overflow-x-auto p-3 lg:block lg:space-y-1 lg:p-4">
              {categories.map((item) => (
                <button
                  key={item.id}
                  type="button"
                  onClick={() => {
                    setCategory(item.id);
                    setCheckedEmails(new Set());
                  }}
                  className={`flex h-10 shrink-0 cursor-pointer items-center justify-between gap-3 rounded-[10px] px-3 text-left text-[12px] font-medium leading-none transition-colors lg:w-full ${
                    category === item.id
                      ? "bg-white text-[#1f3f75] shadow-[0_6px_18px_rgba(50,75,112,0.08)]"
                      : "text-[#667993] hover:bg-white/80 hover:text-[#2f6df6]"
                  }`}
                >
                  <span className="truncate">{item.label}</span>
                  <span className="text-[11px] text-[#91a0b5]">{item.count}</span>
                </button>
              ))}
            </div>
          </nav>

          <section
            className={`relative flex min-h-[330px] flex-col border-b border-[#e9eef5] transition-colors lg:min-h-0 lg:border-b-0 lg:border-r ${
              dragOverContactList ? "bg-[#fff8f8]" : "bg-white"
            }`}
            onDragEnter={(event) => {
              if (!dragSourceBucket) return;
              event.preventDefault();
              setDragOverContactList(true);
              setDragOverBucket(null);
            }}
            onDragOver={(event) => {
              if (!dragSourceBucket) return;
              event.preventDefault();
              event.dataTransfer.dropEffect = "move";
              setDragOverContactList(true);
            }}
            onDragLeave={(event) => {
              if (!event.currentTarget.contains(event.relatedTarget as Node | null)) {
                setDragOverContactList(false);
              }
            }}
            onDrop={(event) => {
              if (!dragSourceBucket) return;
              event.preventDefault();
              removeDraggedRecipients();
            }}
          >
            {dragOverContactList ? (
              <div className="pointer-events-none absolute inset-3 z-30 flex items-center justify-center rounded-[14px] border-2 border-dashed border-[#e19a9a] bg-[rgba(255,248,248,0.94)] px-6 text-center text-[13px] font-semibold text-[#b84c4c]">
                여기에 놓으면 선택한 수신자에서 제거됩니다.
              </div>
            ) : null}
            <div className="flex h-12 shrink-0 items-center justify-between border-b border-[#edf1f6] px-4">
              <label className="flex cursor-pointer items-center gap-2 text-[12px] font-medium text-[#536680]">
                <input
                  type="checkbox"
                  checked={allVisibleChecked}
                  onChange={(event) => {
                    setCheckedEmails((current) => {
                      const next = new Set(current);

                      visibleContacts.forEach((contact) => {
                        const email = contact.email.toLowerCase();
                        if (event.target.checked) next.add(email);
                        else next.delete(email);
                      });

                      return next;
                    });
                  }}
                  className="h-4 w-4 rounded border-[#cfd9e7] text-[#2f6df6] focus:ring-[#cfdeff]"
                />
                전체
              </label>
              <button
                type="button"
                disabled={selectedCount === 0}
                onClick={addCheckedToActiveBucket}
                className="inline-flex h-8 cursor-pointer items-center rounded-[9px] bg-[#2f6df6] px-3 text-[11px] font-semibold leading-none text-white transition-colors hover:bg-[#245de3] disabled:cursor-not-allowed disabled:bg-[#dbe3ef]"
              >
                {selectedCount > 0 ? `${selectedCount}개 ` : ""}{bucketLabels[activeBucket]}에 추가
              </button>
            </div>

            <div className="min-h-0 flex-1 overflow-y-auto p-2">
              {loading ? (
                <div className="flex min-h-[260px] items-center justify-center text-[13px] text-[#8290a6]">
                  주소록을 불러오고 있습니다.
                </div>
              ) : loadError ? (
                <div className="m-3 rounded-[14px] border border-[#f0d0d0] bg-[#fff8f8] px-4 py-5 text-center">
                  <p className="text-[13px] font-semibold text-[#ba4545]">주소록을 불러오지 못했습니다.</p>
                  <p className="mt-1 text-[12px] text-[#916060]">{loadError}</p>
                </div>
              ) : visibleContacts.length > 0 ? (
                visibleContacts.map((contact) => {
                  const email = contact.email.toLowerCase();
                  const checked = checkedEmails.has(email);

                  return (
                    <label
                      key={email}
                      draggable
                      onDragStart={(event) => {
                        const draggedContacts = checkedEmails.has(email)
                          ? contacts.filter((item) => checkedEmails.has(item.email.toLowerCase()))
                          : [contact];
                        const recipients = draggedContacts.map(contactToRecipient);

                        event.dataTransfer.effectAllowed = "move";
                        event.dataTransfer.setData(
                          "text/plain",
                          recipients.map((recipient) => recipient.email).join(","),
                        );
                        setDragSourceBucket(null);
                        setDraggedRecipients(recipients);
                      }}
                      onDragEnd={finishDragging}
                      className={`flex cursor-pointer items-center gap-3 rounded-[11px] px-3 py-2.5 transition-colors ${
                        checked
                          ? "cursor-grab bg-[#eef4ff] active:cursor-grabbing"
                          : "cursor-grab hover:bg-[#f7f9fc] active:cursor-grabbing"
                      }`}
                      title={checked ? "선택한 연락처를 오른쪽 영역으로 드래그" : "오른쪽 영역으로 드래그"}
                    >
                      <input
                        type="checkbox"
                        checked={checked}
                        onChange={() => toggleChecked(email)}
                        className="h-4 w-4 shrink-0 rounded border-[#cfd9e7] text-[#2f6df6] focus:ring-[#cfdeff]"
                      />
                      <span className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[#eaf1ff] text-[11px] font-semibold text-[#2f6df6]">
                        {(contact.displayName || contact.email).trim().slice(0, 1).toUpperCase()}
                      </span>
                      <span className="min-w-0 flex-1">
                        <span className="block truncate text-[13px] font-medium text-[#273854]">
                          {contact.displayName || contact.email}
                        </span>
                        <span className="mt-0.5 block truncate text-[11px] text-[#7d8ca2]">
                          {contact.displayName ? contact.email : contact.companyName || "이름 미등록"}
                        </span>
                      </span>
                      {contact.isVip ? (
                        <span className="shrink-0 text-[14px] text-[#f2a93b]" title="VIP">★</span>
                      ) : null}
                    </label>
                  );
                })
              ) : (
                <div className="flex min-h-[260px] flex-col items-center justify-center px-4 text-center">
                  <p className="text-[13px] font-semibold text-[#445773]">표시할 연락처가 없습니다.</p>
                  <p className="mt-1 text-[12px] text-[#8b99ad]">검색어나 주소록 분류를 바꿔보세요.</p>
                </div>
              )}
            </div>
          </section>

          <aside className="grid min-h-[390px] grid-cols-1 divide-y divide-[#e9eef5] bg-[#fbfcfe] sm:grid-cols-3 sm:divide-x sm:divide-y-0 lg:flex lg:min-h-0 lg:flex-col lg:divide-x-0 lg:divide-y">
            {(["to", "cc", "bcc"] as RecipientBucket[]).map((bucket) => (
              <RecipientPanel
                key={bucket}
                active={activeBucket === bucket}
                bucket={bucket}
                dragActive={dragOverBucket === bucket && draggedRecipients.length > 0}
                recipients={selection[bucket]}
                onActivate={() => setActiveBucket(bucket)}
                onDragEnd={finishDragging}
                onDragHover={() => setDragOverBucket(bucket)}
                onDragStart={(recipient) => {
                  setDragSourceBucket(bucket);
                  setDraggedRecipients([recipient]);
                }}
                onDrop={() => dropRecipientsIntoBucket(bucket)}
                onRemove={(email) => removeRecipient(bucket, email)}
              />
            ))}
          </aside>
        </div>

        <footer className="flex shrink-0 flex-col gap-3 border-t border-[#e9eef5] bg-white px-4 py-4 sm:flex-row sm:items-center sm:justify-between sm:px-6">
          <p className="text-[11px] leading-5 text-[#8492a6]">
            같은 주소를 다른 영역에 추가하면 마지막으로 선택한 영역으로 이동합니다.
          </p>
          <div className="flex justify-end gap-2">
            <button
              type="button"
              onClick={onClose}
              className="inline-flex h-10 min-w-[72px] cursor-pointer items-center justify-center rounded-[10px] border border-[#dce4ef] bg-white px-4 text-[13px] font-semibold leading-none text-[#60718b] hover:bg-[#f7f9fc]"
            >
              취소
            </button>
            <button
              type="button"
              onClick={() => onConfirm(selection)}
              className="inline-flex h-10 min-w-[80px] cursor-pointer items-center justify-center rounded-[10px] bg-[#2f6df6] px-5 text-[13px] font-semibold leading-none text-white hover:bg-[#245de3]"
            >
              적용
            </button>
          </div>
        </footer>
      </div>
    </div>
  );
}
