import { useEffect, useMemo, useState } from 'react';
import { useAdapter } from '../provider';
import { useConversations } from '../hooks/use-conversations';
import { ModalPortal } from './modal-portal';
import type { BulkAddResult, Conversation, Person } from '../types';

/** A source channel the caller belongs to, staged for a roster import once its members are read. */
interface PendingImport {
  source: Conversation;
  /** Members of the source who are NOT already here — the ones an import would actually add. */
  newcomers: Person[];
  /** Members of the source already in this conversation; reported so the count is never surprising. */
  alreadyHere: number;
}

/**
 * Settings for a group or channel (public + private): rename, member list, add/remove people, and
 * leave. Add/remove/rename appear only when the adapter implements them AND — for the server — OPA
 * allows it (admin-only); the panel is optimistic and surfaces the error if the door refuses.
 */
export function ConversationSettings({
  threadId,
  title,
  membership,
  onClose,
  onLeft,
}: {
  threadId: string;
  title: string;
  membership: 'group' | 'channel';
  onClose: () => void;
  onLeft?: () => void;
}) {
  const adapter = useAdapter();
  const [members, setMembers] = useState<Person[]>([]);
  const [directory, setDirectory] = useState<Person[]>([]);
  const [name, setName] = useState(title);
  const [q, setQ] = useState('');
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [nonce, setNonce] = useState(0);

  const [pending, setPending] = useState<PendingImport | null>(null);
  const [imported, setImported] = useState<BulkAddResult | null>(null);
  const { conversations } = useConversations();

  const canManage = typeof adapter.addMember === 'function' && typeof adapter.removeMember === 'function';
  const canRename = typeof adapter.renameConversation === 'function';
  const canLeave = membership === 'channel' && typeof adapter.leaveChannel === 'function';
  // Importing a roster needs both halves: read the source's members, and bulk-add them here.
  const canImport = typeof adapter.addMembers === 'function' && typeof adapter.listMembers === 'function';

  // The channels you belong to are the only valid import sources — this list is already
  // membership-scoped (it is your own conversation list), so private channels appear iff you're in
  // them, and the server re-checks the source-membership rule on the roster read regardless.
  const sourceChannels = useMemo(
    () => conversations.filter((c) => c.membership === 'channel' && c.threadId !== threadId),
    [conversations, threadId],
  );

  useEffect(() => {
    let alive = true;
    void Promise.all([
      adapter.listMembers ? adapter.listMembers(threadId) : Promise.resolve<Person[]>([]),
      adapter.directory ? adapter.directory() : Promise.resolve<Person[]>([]),
    ]).then(([m, d]) => {
      if (alive) {
        setMembers(m);
        setDirectory(d);
      }
    });
    return () => {
      alive = false;
    };
  }, [adapter, threadId, nonce]);

  const memberIds = useMemo(() => new Set(members.map((m) => m.id)), [members]);
  // A leading '#' switches the picker from people to channels — the roster-import affordance.
  const channelMode = canImport && q.trim().startsWith('#');
  const channelQuery = q.trim().slice(1).toLowerCase();
  const addable = directory.filter((p) => !memberIds.has(p.id) && p.name.toLowerCase().includes(q.trim().toLowerCase()));
  const matchingChannels = sourceChannels.filter((c) => c.title.toLowerCase().includes(channelQuery));

  /** Stage an import: read the source roster, then diff it against who is already here. */
  async function stageImport(source: Conversation): Promise<void> {
    setBusy(true);
    setError(null);
    try {
      const roster = await adapter.listMembers!(source.threadId);
      setPending({
        source,
        newcomers: roster.filter((p) => !memberIds.has(p.id)),
        alreadyHere: roster.filter((p) => memberIds.has(p.id)).length,
      });
    } catch (e) {
      setError(e instanceof Error ? e.message : String(e));
    } finally {
      setBusy(false);
    }
  }

  /** Commit the staged import. One bulk call; the result reports what actually landed. */
  async function confirmImport(): Promise<void> {
    if (!pending) return;
    const ids = pending.newcomers.map((p) => p.id);
    await run(async () => {
      const res = await adapter.addMembers!(threadId, ids);
      setImported(res);
      setPending(null);
      setQ('');
    });
  }

  async function run(fn: () => Promise<void>): Promise<void> {
    setBusy(true);
    setError(null);
    try {
      await fn();
      setNonce((n) => n + 1);
    } catch (e) {
      setError(e instanceof Error ? e.message : String(e));
    } finally {
      setBusy(false);
    }
  }

  return (
    <ModalPortal>
      <div className="miu-modal-overlay" onMouseDown={onClose}>
        <div className="miu-modal" role="dialog" aria-modal="true" onMouseDown={(e) => e.stopPropagation()}>
          <div className="miu-modal-head">
            <span>{membership === 'channel' ? 'Channel' : 'Group'} settings</span>
            <button type="button" className="miu-pane-close" onClick={onClose} aria-label="Close">✕</button>
          </div>
          <div className="miu-modal-body">
            {canRename ? (
              <div className="miu-field">
                <span className="miu-field-lbl">Name</span>
                <div className="miu-composer-row">
                  <input className="miu-input" value={name} onChange={(e) => setName(e.target.value)} aria-label="Conversation name" />
                  <button
                    type="button"
                    className="miu-send"
                    disabled={busy || !name.trim() || name.trim() === title}
                    onClick={() => void run(() => adapter.renameConversation!(threadId, name.trim()))}
                  >
                    Rename
                  </button>
                </div>
              </div>
            ) : null}

            <div className="miu-field">
              <span className="miu-field-lbl">Members · {members.length}</span>
              <div className="miu-settings-list">
                {members.map((m) => (
                  <div key={m.id} className="miu-settings-member">
                    <span className="miu-settings-name">{m.name}</span>
                    <span className="miu-pill">{m.kind}</span>
                    {canManage ? (
                      <button type="button" className="miu-attach-x" title="Remove" disabled={busy} onClick={() => void run(() => adapter.removeMember!(threadId, m.id))}>
                        ✕
                      </button>
                    ) : null}
                  </div>
                ))}
              </div>
            </div>

            {canManage && pending ? (
              // Confirm step — an import is an administrative action, so it never fires on a stray click.
              <div className="miu-field">
                <span className="miu-field-lbl">Add from #{pending.source.title}</span>
                {pending.newcomers.length === 0 ? (
                  <div className="miu-empty">Everyone from #{pending.source.title} is already here.</div>
                ) : (
                  <div className="miu-empty">
                    Add {pending.newcomers.length} {pending.newcomers.length === 1 ? 'person' : 'people'} from #{pending.source.title}?
                    {pending.alreadyHere > 0 ? ` (${pending.alreadyHere} already here)` : ''}
                  </div>
                )}
                <div className="miu-composer-row">
                  <button type="button" className="miu-tab" disabled={busy} onClick={() => setPending(null)}>Cancel</button>
                  <button type="button" className="miu-send" disabled={busy || pending.newcomers.length === 0} onClick={() => void confirmImport()}>
                    Add {pending.newcomers.length > 0 ? pending.newcomers.length : ''}
                  </button>
                </div>
              </div>
            ) : null}

            {canManage && !pending ? (
              <div className="miu-field">
                <span className="miu-field-lbl">Add people</span>
                <input
                  className="miu-input"
                  value={q}
                  onChange={(e) => { setQ(e.target.value); setImported(null); }}
                  placeholder={canImport ? 'Search people… or # for a channel' : 'Search people…'}
                  aria-label="Search people"
                />
                <div className="miu-settings-list">
                  {channelMode ? (
                    <>
                      {matchingChannels.length === 0 ? <div className="miu-empty">No channels to add from.</div> : null}
                      {matchingChannels.slice(0, 25).map((c) => (
                        <button key={c.threadId} type="button" className="miu-settings-member is-add" disabled={busy} onClick={() => void stageImport(c)}>
                          <span className="miu-settings-name">#{c.title}</span>
                          <span className="miu-pill">channel</span>
                          <span className="miu-settings-plus" aria-hidden="true">＋</span>
                        </button>
                      ))}
                    </>
                  ) : (
                    <>
                      {addable.length === 0 ? <div className="miu-empty">No one to add.</div> : null}
                      {addable.slice(0, 25).map((p) => (
                        <button key={p.id} type="button" className="miu-settings-member is-add" disabled={busy} onClick={() => void run(() => adapter.addMember!(threadId, p.id))}>
                          <span className="miu-settings-name">{p.name}</span>
                          <span className="miu-pill">{p.kind}</span>
                          <span className="miu-settings-plus" aria-hidden="true">＋</span>
                        </button>
                      ))}
                    </>
                  )}
                </div>
              </div>
            ) : null}

            {imported ? (
              <div className="miu-empty">
                Added {imported.added.length}
                {imported.skipped.length > 0 ? ` · ${imported.skipped.length} already a member` : ''}
                {imported.failed.length > 0 ? ` · ${imported.failed.length} failed` : ''}
              </div>
            ) : null}

            {error ? <div className="miu-empty miu-error">{error}</div> : null}
          </div>
          <div className="miu-modal-foot">
            {canLeave ? (
              <button type="button" className="miu-tab" disabled={busy} onClick={() => void run(async () => { await adapter.leaveChannel!(threadId); onLeft?.(); onClose(); })}>
                Leave {membership}
              </button>
            ) : (
              <span />
            )}
            <button type="button" className="miu-send" onClick={onClose}>Done</button>
          </div>
        </div>
      </div>
    </ModalPortal>
  );
}
