"use client";

import { useEffect, useRef, useState } from "react";
import { api, ApiError, type ChannelAbout, type ChannelNotifPref } from "../lib/api";
import { UserAvatar } from "./UserAvatar";
import {
  IconBellOff,
  IconCheck,
  IconChevron,
  IconLock,
  IconPin,
  IconPlus,
  IconSliders,
  IconTrash,
  IconUsers,
  IconX,
} from "./icons";

export interface RoomAboutMember {
  user: { id: string | number; name: string; email: string; avatarUrl?: string | null };
}

/** The apps a room can link, and how each renders in the strip. Mirrors
 * IntegrationProvider in @slackwsh/contracts — the server rejects anything
 * outside this list, so the two must stay in step. */
const PROVIDERS: Array<{ id: string; name: string; short: string; tone: string }> = [
  { id: "salesforce", name: "Salesforce", short: "SF", tone: "sf" },
  { id: "hubspot", name: "HubSpot", short: "HS", tone: "hubspot" },
  { id: "github", name: "GitHub", short: "GH", tone: "github" },
  { id: "jira", name: "Jira", short: "JR", tone: "jira" },
  { id: "linear", name: "Linear", short: "LN", tone: "linear" },
  { id: "notion", name: "Notion", short: "NO", tone: "notion" },
  { id: "zoom", name: "Zoom", short: "ZM", tone: "zoom" },
  { id: "google_drive", name: "Google Drive", short: "GD", tone: "gdrive" },
];

const NOTIF_OPTIONS: Array<{ value: ChannelNotifPref; label: string; hint: string }> = [
  { value: "all", label: "All new messages", hint: "Notify me every time someone posts here." },
  { value: "mentions", label: "Mentions only", hint: "Only when I'm @-mentioned or replied to." },
  { value: "none", label: "Nothing", hint: "Never notify me about this room." },
];

function providerMeta(id: string) {
  return PROVIDERS.find((p) => p.id === id) ?? { id, name: id, short: id.slice(0, 2).toUpperCase(), tone: "custom" };
}

function relativeDay(iso: string): string {
  const then = new Date(iso).getTime();
  const days = Math.floor((Date.now() - then) / 86_400_000);
  if (days <= 0) return "today";
  if (days === 1) return "yesterday";
  if (days < 30) return `${days}d ago`;
  return new Date(iso).toLocaleDateString([], { month: "short", day: "numeric" });
}

function errText(err: unknown): string {
  if (err instanceof ApiError) {
    const body = err.body as { message?: unknown } | null;
    if (body && typeof body.message === "string") return body.message;
    return `Request failed (${err.status})`;
  }
  return String(err);
}

/**
 * Right-rail context panel for the current room. Everything here is live
 * server state (`GET .../channels/:id/about`, hydrated by the parent): the
 * description, the pinned messages, the linked apps, and this user's own
 * notification preference — which is per-person, so two members of the same
 * room legitimately see different values in the same place.
 */
export function RoomAbout({
  workspaceId,
  channelId,
  channelName,
  channelType,
  members,
  about,
  loading,
  onClose,
  onAddMembers,
  onRefresh,
  onJumpToMessage,
  onOpenProfile,
  onLeft,
  onDeleted,
}: {
  workspaceId: string;
  channelId: string;
  channelName?: string | null;
  channelType?: string | null;
  members: RoomAboutMember[];
  about: ChannelAbout | null;
  loading?: boolean;
  onClose?: () => void;
  onAddMembers?: () => void;
  onRefresh: () => void | Promise<void>;
  onJumpToMessage?: (messageId: string | number) => void;
  onOpenProfile?: (userId: string | number) => void;
  onLeft?: () => void;
  onDeleted?: () => void;
}) {
  const [openSection, setOpenSection] = useState<"notifications" | "settings" | null>(null);
  const [editingDescription, setEditingDescription] = useState(false);
  const [descriptionDraft, setDescriptionDraft] = useState("");
  const [nameDraft, setNameDraft] = useState("");
  const [topicDraft, setTopicDraft] = useState("");
  const [addingIntegration, setAddingIntegration] = useState(false);
  const [confirmLeave, setConfirmLeave] = useState(false);
  const [confirmDelete, setConfirmDelete] = useState(false);
  const [busy, setBusy] = useState(false);
  const [panelError, setPanelError] = useState<string | null>(null);
  const descriptionRef = useRef<HTMLTextAreaElement | null>(null);

  const channel = about?.channel;
  const caps = about?.capabilities;
  const membership = about?.membership;
  const isPrivate = (channel?.type ?? channelType) === "private";
  const displayName = channel?.name ?? channelName ?? "this channel";
  const shown = members.slice(0, 5);
  const extra = Math.max(0, members.length - shown.length);
  const memberCount = members.length || channel?.memberCount || 0;
  const linked = about?.integrations ?? [];
  const linkedIds = new Set(linked.map((row) => row.provider));

  // Re-seed the settings drafts whenever the server row changes, so a
  // description someone else edited doesn't sit stale behind a closed form.
  useEffect(() => {
    setNameDraft(channel?.name ?? "");
    setTopicDraft(channel?.topic ?? "");
    if (!editingDescription) setDescriptionDraft(channel?.purpose ?? "");
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [channel?.id, channel?.name, channel?.topic, channel?.purpose]);

  useEffect(() => {
    if (editingDescription) descriptionRef.current?.focus();
  }, [editingDescription]);

  /** Every mutation in this panel is the same shape: disable, call, refetch,
   * surface the failure inline rather than throwing into the page. */
  async function run(action: () => Promise<unknown>) {
    setBusy(true);
    setPanelError(null);
    try {
      await action();
      await onRefresh();
      return true;
    } catch (err) {
      setPanelError(errText(err));
      return false;
    } finally {
      setBusy(false);
    }
  }

  async function saveDescription() {
    const ok = await run(() => api.updateChannel(workspaceId, channelId, { purpose: descriptionDraft.trim() }));
    if (ok) setEditingDescription(false);
  }

  async function saveRoomSettings() {
    const patch: { name?: string; topic?: string } = {};
    const nextName = nameDraft.trim();
    if (nextName && nextName !== (channel?.name ?? "")) patch.name = nextName;
    if (topicDraft.trim() !== (channel?.topic ?? "")) patch.topic = topicDraft.trim();
    if (Object.keys(patch).length === 0) return;
    await run(() => api.updateChannel(workspaceId, channelId, patch));
  }

  async function leaveRoom() {
    const ok = await run(() => api.leaveChannel(workspaceId, channelId));
    if (ok) {
      setConfirmLeave(false);
      onLeft?.();
    }
  }

  async function deleteRoom() {
    const ok = await run(() => api.permanentlyDeleteChannel(workspaceId, channelId));
    if (ok) {
      setConfirmDelete(false);
      onDeleted?.();
    }
  }

  return (
    <aside className="about" aria-label="About this room">
      <div className="about-head">
        <span>About this room</span>
        {onClose && (
          <button className="about-close" type="button" onClick={onClose} aria-label="Close about panel">
            <IconX />
          </button>
        )}
      </div>

      <div className="about-scroll">
        {panelError && (
          <p className="about-error" role="alert">
            {panelError}
          </p>
        )}

        {/* ── description ─────────────────────────────────────────────── */}
        <section className="about-section">
          <div className="about-section-head">
            <div className="about-section-label">Description</div>
            {caps?.canEditRoom && !editingDescription && (
              <button
                type="button"
                className="about-link"
                onClick={() => {
                  setDescriptionDraft(channel?.purpose ?? "");
                  setEditingDescription(true);
                }}
              >
                {channel?.purpose ? "Edit" : "Add"}
              </button>
            )}
          </div>

          {editingDescription ? (
            <div className="about-edit">
              <textarea
                ref={descriptionRef}
                className="about-textarea"
                value={descriptionDraft}
                maxLength={500}
                rows={4}
                aria-label="Room description"
                placeholder="What is this room for?"
                onChange={(e) => setDescriptionDraft(e.target.value)}
                onKeyDown={(e) => {
                  if (e.key === "Escape") setEditingDescription(false);
                  if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) void saveDescription();
                }}
              />
              <div className="about-edit-foot">
                <span className="about-hint">{500 - descriptionDraft.length} left</span>
                <button type="button" className="about-btn" onClick={() => setEditingDescription(false)} disabled={busy}>
                  Cancel
                </button>
                <button type="button" className="about-btn primary" onClick={() => void saveDescription()} disabled={busy}>
                  {busy ? "Saving…" : "Save"}
                </button>
              </div>
            </div>
          ) : (
            <p className={channel?.purpose ? "about-copy" : "about-copy muted"}>
              {channel?.purpose?.trim() ||
                (loading
                  ? "Loading…"
                  : isPrivate
                    ? `Private room for #${displayName}. No description yet.`
                    : `Room for #${displayName}. No description yet.`)}
            </p>
          )}

          {channel?.topic && <p className="about-topic">Topic — {channel.topic}</p>}
        </section>

        {/* ── members ─────────────────────────────────────────────────── */}
        <section className="about-section">
          <div className="about-section-head">
            <div className="about-section-label">Members</div>
            <span className="about-count">{memberCount}</span>
          </div>
          <div className="about-members">
            <div className="about-faces">
              {shown.map((row) => {
                return (
                  <button
                    key={String(row.user.id)}
                    type="button"
                    className="about-face-trigger"
                    aria-label={`Open ${row.user.name}'s profile`}
                    onClick={() => onOpenProfile?.(row.user.id)}
                    disabled={!onOpenProfile}
                  >
                    <UserAvatar
                      className="about-face"
                      userId={row.user.id}
                      name={row.user.name}
                      avatarUrl={row.user.avatarUrl}
                    />
                  </button>
                );
              })}
              {extra > 0 && <span className="about-face-extra">+{extra}</span>}
            </div>
            <button type="button" className="about-link" onClick={onAddMembers} disabled={!onAddMembers}>
              View all
            </button>
          </div>
        </section>

        {/* ── pinned ──────────────────────────────────────────────────── */}
        <section className="about-section">
          <div className="about-section-head">
            <div className="about-section-label">Pinned</div>
            {about && about.pins.length > 0 && <span className="about-count">{about.pins.length}</span>}
          </div>
          <div className="about-pins">
            {about?.pins.map((pin) => (
              <div className="about-pin" key={pin.messageId}>
                <span className="about-pin-icon pinned">
                  <IconPin size={13} />
                </span>
                <button
                  type="button"
                  className="about-pin-meta"
                  onClick={() => onJumpToMessage?.(pin.messageId)}
                  title={pin.text}
                >
                  <strong>{pin.text.length > 90 ? `${pin.text.slice(0, 90)}…` : pin.text}</strong>
                  <span>
                    {pin.authorName} · pinned by {pin.pinnedByName} {relativeDay(pin.pinnedAt)}
                  </span>
                </button>
                {caps?.canPin && (
                  <button
                    type="button"
                    className="about-pin-remove"
                    aria-label="Unpin this message"
                    title="Unpin"
                    disabled={busy}
                    onClick={() => void run(() => api.unpinMessage(workspaceId, channelId, String(pin.messageId)))}
                  >
                    <IconX size={13} />
                  </button>
                )}
              </div>
            ))}
            {about && about.pins.length === 0 && (
              <p className="about-empty">
                Nothing pinned yet. Use a message&apos;s ⋮ menu → <em>Pin to this room</em> to keep it here.
              </p>
            )}
            {!about && loading && <p className="about-empty">Loading…</p>}
          </div>
        </section>

        {/* ── integrations ────────────────────────────────────────────── */}
        <section className="about-section">
          <div className="about-section-head">
            <div className="about-section-label">Integrations</div>
            {caps?.canEditRoom && (
              <button type="button" className="about-link" onClick={() => setAddingIntegration((v) => !v)}>
                {addingIntegration ? "Done" : "Add"}
              </button>
            )}
          </div>

          <div className="about-integrations">
            {linked.map((row) => {
              const meta = providerMeta(row.provider);
              const badge = (
                <span className={`about-integration ${meta.tone}`} title={row.label ?? meta.name}>
                  {meta.short}
                </span>
              );
              return (
                <span className="about-integration-slot" key={row.id}>
                  {row.externalUrl ? (
                    <a href={row.externalUrl} target="_blank" rel="noreferrer noopener" className="about-integration-link">
                      {badge}
                    </a>
                  ) : (
                    badge
                  )}
                  {caps?.canEditRoom && (
                    <button
                      type="button"
                      className="about-integration-remove"
                      aria-label={`Remove ${meta.name}`}
                      title={`Remove ${meta.name}`}
                      disabled={busy}
                      onClick={() => void run(() => api.removeChannelIntegration(workspaceId, channelId, row.id))}
                    >
                      <IconX size={9} />
                    </button>
                  )}
                </span>
              );
            })}
            {linked.length === 0 && !addingIntegration && (
              <p className="about-empty">No apps linked to this room yet.</p>
            )}
            {caps?.canEditRoom && !addingIntegration && linked.length > 0 && (
              <button
                type="button"
                className="about-integration more"
                aria-label="Link an app"
                title="Link an app"
                onClick={() => setAddingIntegration(true)}
              >
                <IconPlus size={13} />
              </button>
            )}
          </div>

          {addingIntegration && (
            <div className="about-integration-picker">
              {PROVIDERS.map((provider) => (
                <button
                  key={provider.id}
                  type="button"
                  className={linkedIds.has(provider.id) ? "about-provider linked" : "about-provider"}
                  disabled={busy || linkedIds.has(provider.id)}
                  onClick={() =>
                    void run(() =>
                      api.addChannelIntegration(workspaceId, channelId, { provider: provider.id, label: provider.name }),
                    )
                  }
                >
                  <span className={`about-integration ${provider.tone}`}>{provider.short}</span>
                  <span>{provider.name}</span>
                  {linkedIds.has(provider.id) && <IconCheck size={12} />}
                </button>
              ))}
            </div>
          )}
        </section>

        {/* ── actions ─────────────────────────────────────────────────── */}
        <section className="about-section about-actions">
          <button
            type="button"
            className="about-action"
            aria-expanded={openSection === "notifications"}
            onClick={() => setOpenSection((v) => (v === "notifications" ? null : "notifications"))}
          >
            {membership?.isMuted ? <IconBellOff size={16} /> : <IconSliders size={16} />}
            <span>Notification preferences</span>
            <IconChevron size={14} />
          </button>

          {openSection === "notifications" && (
            <div className="about-drawer">
              <p className="about-hint">Applies to you only — everyone else keeps their own setting.</p>
              {NOTIF_OPTIONS.map((option) => (
                <label className="about-radio" key={option.value}>
                  <input
                    type="radio"
                    name="room-notif-pref"
                    value={option.value}
                    checked={(membership?.notifPref ?? "all") === option.value}
                    disabled={busy || !membership}
                    onChange={() => void run(() => api.updateChannelPrefs(workspaceId, channelId, { notifPref: option.value }))}
                  />
                  <span>
                    <strong>{option.label}</strong>
                    <em>{option.hint}</em>
                  </span>
                </label>
              ))}
              <label className="about-toggle">
                <input
                  type="checkbox"
                  checked={Boolean(membership?.isMuted)}
                  disabled={busy || !membership}
                  onChange={(e) => void run(() => api.updateChannelPrefs(workspaceId, channelId, { isMuted: e.target.checked }))}
                />
                <span>
                  <strong>Mute this room</strong>
                  <em>Keeps it out of your unread badge without changing the rule above.</em>
                </span>
              </label>
            </div>
          )}

          <button
            type="button"
            className="about-action"
            aria-expanded={openSection === "settings"}
            onClick={() => setOpenSection((v) => (v === "settings" ? null : "settings"))}
          >
            <IconUsers size={16} />
            <span>Room settings</span>
            <IconChevron size={14} />
          </button>

          {openSection === "settings" && (
            <div className="about-drawer">
              <label className="about-field">
                <span>Room name</span>
                <input
                  value={nameDraft}
                  maxLength={80}
                  disabled={busy || !caps?.canEditRoom}
                  onChange={(e) => setNameDraft(e.target.value)}
                />
              </label>
              <label className="about-field">
                <span>Topic</span>
                <input
                  value={topicDraft}
                  maxLength={250}
                  placeholder="What's happening right now?"
                  disabled={busy || !caps?.canEditRoom}
                  onChange={(e) => setTopicDraft(e.target.value)}
                />
              </label>
              {caps?.canEditRoom && (
                <div className="about-edit-foot">
                  <button type="button" className="about-btn primary" onClick={() => void saveRoomSettings()} disabled={busy}>
                    {busy ? "Saving…" : "Save changes"}
                  </button>
                </div>
              )}

              <label className="about-toggle">
                <input
                  type="checkbox"
                  checked={Boolean(membership?.isStarred)}
                  disabled={busy || !membership}
                  onChange={(e) => void run(() => api.updateChannelPrefs(workspaceId, channelId, { isStarred: e.target.checked }))}
                />
                <span>
                  <strong>Star this room</strong>
                  <em>Pins it to the top of your sidebar.</em>
                </span>
              </label>

              <dl className="about-facts">
                <div>
                  <dt>Visibility</dt>
                  <dd>
                    {isPrivate ? <IconLock size={12} /> : null} {isPrivate ? "Private" : "Public"}
                  </dd>
                </div>
                <div>
                  <dt>Your role</dt>
                  <dd>{membership?.role ?? "—"}</dd>
                </div>
                <div>
                  <dt>Members</dt>
                  <dd>{memberCount}</dd>
                </div>
                <div>
                  <dt>Joined</dt>
                  <dd>{membership ? relativeDay(membership.joinedAt) : "—"}</dd>
                </div>
              </dl>

              {caps?.canArchive && (
                <button
                  type="button"
                  className="about-btn danger wide"
                  disabled={busy}
                  onClick={() => void run(() => api.archiveChannel(workspaceId, channelId, !channel?.isArchived))}
                >
                  <IconTrash size={13} />
                  <span>{channel?.isArchived ? "Unarchive room" : "Archive room"}</span>
                </button>
              )}
              {caps?.canDeletePermanently && !["general", "random"].includes(displayName.toLowerCase()) && (
                <button type="button" className="about-btn danger wide" disabled={busy} onClick={() => setConfirmDelete(true)}>
                  <IconTrash size={13} />
                  <span>Permanently delete room</span>
                </button>
              )}
            </div>
          )}

          {caps?.canLeave !== false && (
            <button type="button" className="about-action leave" onClick={() => setConfirmLeave(true)}>
              <span>Leave room</span>
              <IconChevron size={14} />
            </button>
          )}
        </section>
      </div>

      {confirmLeave && (
        <div className="channel-invite-backdrop" role="presentation" onClick={() => setConfirmLeave(false)}>
          <div
            className="channel-invite-modal confirm"
            role="alertdialog"
            aria-modal="true"
            aria-labelledby="leave-room-title"
            onClick={(e) => e.stopPropagation()}
          >
            <div className="channel-invite-head">
              <div>
                <h2 id="leave-room-title">Leave #{displayName}?</h2>
                <p>
                  {isPrivate
                    ? "This room is private — you'll need someone inside to add you back."
                    : "You'll stop getting messages from this room. You can rejoin any time."}
                </p>
              </div>
              <button className="thread-close" type="button" onClick={() => setConfirmLeave(false)} aria-label="Close">
                <IconX />
              </button>
            </div>
            <div className="channel-invite-foot">
              <button type="button" className="screen-btn" onClick={() => setConfirmLeave(false)} disabled={busy}>
                Cancel
              </button>
              <button type="button" className="screen-btn danger" onClick={() => void leaveRoom()} disabled={busy}>
                {busy ? "Leaving…" : "Leave room"}
              </button>
            </div>
          </div>
        </div>
      )}
      {confirmDelete && (
        <div className="channel-invite-backdrop" role="presentation" onClick={() => setConfirmDelete(false)}>
          <div className="channel-invite-modal confirm" role="alertdialog" aria-modal="true" aria-labelledby="delete-room-title" onClick={(e) => e.stopPropagation()}>
            <div className="channel-invite-head">
              <div>
                <h2 id="delete-room-title">Permanently delete #{displayName}?</h2>
                <p>All messages and uploaded files in this room will be permanently removed. This cannot be undone.</p>
              </div>
              <button className="thread-close" type="button" onClick={() => setConfirmDelete(false)} aria-label="Close"><IconX /></button>
            </div>
            <div className="channel-invite-foot">
              <button type="button" className="screen-btn" onClick={() => setConfirmDelete(false)} disabled={busy}>Cancel</button>
              <button type="button" className="screen-btn danger" onClick={() => void deleteRoom()} disabled={busy}>{busy ? "Deleting…" : "Delete permanently"}</button>
            </div>
          </div>
        </div>
      )}
    </aside>
  );
}
