"use client";

import { Suspense, useEffect, useMemo, useRef, useState } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import { io, type Socket } from "socket.io-client";
import { api, ApiError, getCurrentUserId, getValidAccessToken, forceLogin } from "../../lib/api";
import { channelPath } from "../../lib/conversations";
import { AppShell, useWorkspaceIdParam } from "../../components/AppShell";
import { IconSearch } from "../../components/icons";
import { UserAvatar } from "../../components/UserAvatar";
import { useMemberStatusRefresh } from "../../lib/member-status-events";
import { gatewaySocketOptions, gatewayUrl } from "../../lib/gateway-socket";

type PresenceStatus = "active" | "away" | "offline" | string;
type PeopleFilter = "all" | "available" | "away" | "deactivated";

interface MemberRow {
  member: {
    role: string;
    displayName?: string | null;
    title?: string | null;
    statusText?: string | null;
    statusEmoji?: string | null;
    joinedAt?: string;
    deactivatedAt?: string | null;
  };
  user: {
    id: string | number;
    name: string;
    email: string;
    username?: string;
    avatarUrl?: string | null;
  };
}

function isAdminRole(role: string | null | undefined) {
  return role === "owner" || role === "admin";
}

function displayNameOf(row: MemberRow) {
  return row.member.displayName?.trim() || row.user.name;
}

function presenceLabel(status: PresenceStatus | undefined) {
  if (status === "active") return "Available";
  if (status === "away") return "Away";
  return "Offline";
}

function presenceColor(status: PresenceStatus | undefined) {
  if (status === "active") return "var(--online)";
  if (status === "away") return "#e0a800";
  return "var(--offline)";
}

function roleLabel(role: string) {
  if (role === "multi_channel_guest") return "Multi-channel guest";
  if (role === "single_channel_guest") return "Single-channel guest";
  return role.charAt(0).toUpperCase() + role.slice(1);
}

function formatJoined(iso?: string) {
  if (!iso) return "—";
  return new Date(iso).toLocaleDateString([], { month: "short", day: "numeric", year: "numeric" });
}

function PeopleView() {
  const router = useRouter();
  const params = useSearchParams();
  const workspaceId = useWorkspaceIdParam();
  const selectedUserId = params.get("userId");
  const showAdd = params.get("add") === "1";

  const [members, setMembers] = useState<MemberRow[]>([]);
  const [presence, setPresence] = useState<Record<string, { status: string }>>({});
  const [query, setQuery] = useState("");
  const [filter, setFilter] = useState<PeopleFilter>("all");
  const [myUserId, setMyUserId] = useState<number | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [busy, setBusy] = useState(false);
  const [inviteEmail, setInviteEmail] = useState("");
  const [inviteRole, setInviteRole] = useState("member");
  const [inviteMessage, setInviteMessage] = useState<string | null>(null);
  const [pendingInvites, setPendingInvites] = useState<
    Array<{ id: string; email: string | null; role: string; expiresAt: string }>
  >([]);

  const socketRef = useRef<Socket | null>(null);

  const meRow = useMemo(
    () => members.find((row) => String(row.user.id) === String(myUserId)) ?? null,
    [members, myUserId],
  );
  const canManage = isAdminRole(meRow?.member.role);

  async function loadMembers() {
    if (!workspaceId) return;
    setLoading(true);
    setError(null);
    try {
      const res = await api.members(workspaceId);
      setMembers(res.members as MemberRow[]);
    } catch (err) {
      if (err instanceof ApiError && err.status === 401) void forceLogin(router);
      else setError(err instanceof ApiError ? JSON.stringify(err.body) : String(err));
    } finally {
      setLoading(false);
    }
  }

  async function loadPendingInvites() {
    if (!workspaceId || !canManage) {
      setPendingInvites([]);
      return;
    }
    try {
      const overview = (await api.adminOverview(workspaceId)) as {
        invites?: Array<{ id: string; email: string | null; role: string; expiresAt: string }>;
      };
      setPendingInvites(overview.invites ?? []);
    } catch {
      setPendingInvites([]);
    }
  }

  useEffect(() => {
    getCurrentUserId().then(setMyUserId);
  }, []);

  useEffect(() => {
    void loadMembers();
  }, [workspaceId, router]);

  useMemberStatusRefresh(workspaceId, () => {
    if (!workspaceId) return;
    void api.members(workspaceId)
      .then((res) => setMembers(res.members as MemberRow[]))
      .catch(() => undefined);
  });

  useEffect(() => {
    if (!canManage) return;
    void loadPendingInvites();
  }, [workspaceId, canManage]);

  useEffect(() => {
    if (!workspaceId) return;
    let cancelled = false;

    api
      .presenceSnapshot(workspaceId)
      .then((res) => {
        if (!cancelled) setPresence(res.presence);
      })
      .catch(() => undefined);

    getValidAccessToken().then(async (token) => {
      if (!token || cancelled) return;
      const socket = io(gatewayUrl(), gatewaySocketOptions((cb) => getValidAccessToken().then((t) => cb({ accessToken: t }))));
      socketRef.current = socket;

      // Presence is workspace-scoped; channel join is only needed for live
      // presence:changed fanout into the ws:{workspaceId} room (also done by
      // workspace:join now).
      socket.on("connect", () => {
        socket.emit("workspace:join", { workspaceId });
      });
      socket.on("presence:changed", (payload: { userId: string | number; status: string }) => {
        if (cancelled) return;
        setPresence((prev) => ({ ...prev, [String(payload.userId)]: { status: payload.status } }));
      });
      const refresh = setInterval(() => {
        api
          .presenceSnapshot(workspaceId)
          .then((res) => {
            if (!cancelled) setPresence(res.presence);
          })
          .catch(() => undefined);
      }, 30_000);
      socket.on("disconnect", () => {
        clearInterval(refresh);
      });
      (socket as Socket & { __cleanup?: () => void }).__cleanup = () => {
        clearInterval(refresh);
      };
    });

    return () => {
      cancelled = true;
      const sock = socketRef.current as (Socket & { __cleanup?: () => void }) | null;
      sock?.__cleanup?.();
      sock?.disconnect();
      socketRef.current = null;
    };
  }, [workspaceId]);

  // Redirect non-admins away from invite mode.
  useEffect(() => {
    if (!showAdd || loading || !meRow) return;
    if (!canManage && workspaceId) {
      router.replace(`/people?workspaceId=${workspaceId}`);
    }
  }, [showAdd, loading, meRow, canManage, workspaceId, router]);

  const filtered = useMemo(() => {
    const q = query.trim().toLowerCase();
    let rows = members;

    if (filter === "deactivated") {
      rows = rows.filter((row) => Boolean(row.member.deactivatedAt));
    } else {
      rows = rows.filter((row) => !row.member.deactivatedAt);
      if (filter === "available") {
        rows = rows.filter((row) => presence[String(row.user.id)]?.status === "active");
      } else if (filter === "away") {
        rows = rows.filter((row) => presence[String(row.user.id)]?.status !== "active");
      }
    }

    if (q) {
      rows = rows.filter((row) => {
        const hay = [
          row.user.name,
          row.member.displayName,
          row.user.email,
          row.user.username,
          row.member.title,
          row.member.statusText,
          row.member.role,
        ]
          .filter(Boolean)
          .join(" ")
          .toLowerCase();
        return hay.includes(q);
      });
    }

    return [...rows].sort((a, b) => {
      const aActive = presence[String(a.user.id)]?.status === "active" ? 0 : 1;
      const bActive = presence[String(b.user.id)]?.status === "active" ? 0 : 1;
      if (aActive !== bActive) return aActive - bActive;
      return displayNameOf(a).localeCompare(displayNameOf(b));
    });
  }, [members, query, filter, presence]);

  const activeCount = useMemo(
    () => members.filter((row) => !row.member.deactivatedAt).length,
    [members],
  );

  const selected = useMemo(() => {
    if (showAdd) return null;
    if (selectedUserId) {
      const match = filtered.find((row) => String(row.user.id) === String(selectedUserId));
      if (match) return match;
      // Selected person may be hidden by filter — still show from full list.
      return members.find((row) => String(row.user.id) === String(selectedUserId)) ?? null;
    }
    return filtered[0] ?? null;
  }, [showAdd, selectedUserId, filtered, members]);

  function selectPerson(userId: string | number) {
    if (!workspaceId) return;
    router.replace(`/people?workspaceId=${workspaceId}&userId=${userId}`);
  }

  async function openDmFor(userId: string | number) {
    if (!workspaceId || String(userId) === String(myUserId)) return;
    const row = members.find((entry) => String(entry.user.id) === String(userId));
    // Deactivated people cannot be messaged — keep the directory profile instead.
    if (!row || row.member.deactivatedAt) {
      selectPerson(userId);
      return;
    }
    setBusy(true);
    setError(null);
    try {
      const dm = await api.createDm(workspaceId, String(userId));
      if (!dm?.id) throw new Error("Could not open direct message");
      router.push(channelPath(workspaceId, String(dm.id)));
    } catch (err) {
      setError(err instanceof ApiError ? JSON.stringify(err.body) : String(err));
    } finally {
      setBusy(false);
    }
  }

  async function openDm() {
    if (!selected) return;
    await openDmFor(selected.user.id);
  }

  async function inviteUser(e: React.FormEvent) {
    e.preventDefault();
    if (!workspaceId || !inviteEmail.trim() || !canManage) return;
    setBusy(true);
    setError(null);
    setInviteMessage(null);
    try {
      const result = await api.createInvite(workspaceId, {
        email: inviteEmail.trim(),
        role: inviteRole,
      });
      setInviteEmail("");
      if (result.userExists && result.notified) {
        setInviteMessage("Invite sent. They already have an account and will see a notification to join.");
      } else if (result.userExists) {
        setInviteMessage("Invite ready. They can accept it from their workspace list.");
      } else {
        setInviteMessage("Invite created. They can join after signing up with that email.");
      }
      await loadPendingInvites();
    } catch (err) {
      if (err instanceof ApiError && err.status === 403) {
        setError("You need admin access to invite people.");
      } else {
        setError(err instanceof ApiError ? JSON.stringify(err.body) : String(err));
      }
    } finally {
      setBusy(false);
    }
  }

  async function changeRole(role: string) {
    if (!workspaceId || !selected || !canManage) return;
    if (String(selected.user.id) === String(myUserId)) return;
    if (selected.member.role === "owner") return;
    setBusy(true);
    setError(null);
    try {
      await api.changeMemberRole(workspaceId, String(selected.user.id), role);
      await loadMembers();
    } catch (err) {
      setError(err instanceof ApiError ? JSON.stringify(err.body) : String(err));
    } finally {
      setBusy(false);
    }
  }

  async function toggleActive() {
    if (!workspaceId || !selected || !canManage) return;
    if (String(selected.user.id) === String(myUserId)) return;
    if (selected.member.role === "owner") return;
    setBusy(true);
    setError(null);
    try {
      if (selected.member.deactivatedAt) {
        await api.reactivateMember(workspaceId, String(selected.user.id));
      } else {
        await api.deactivateMember(workspaceId, String(selected.user.id));
      }
      await loadMembers();
    } catch (err) {
      setError(err instanceof ApiError ? JSON.stringify(err.body) : String(err));
    } finally {
      setBusy(false);
    }
  }

  const filters: Array<{ id: PeopleFilter; label: string; adminOnly?: boolean }> = [
    { id: "all", label: "All" },
    { id: "available", label: "Available" },
    { id: "away", label: "Away" },
    { id: "deactivated", label: "Deactivated", adminOnly: true },
  ];

  return (
    <AppShell
      active="people"
      title="People"
      subtitle={loading ? "Loading directory…" : `${activeCount} in this workspace`}
      actions={
        showAdd ? (
          <button
            className="screen-btn"
            type="button"
            onClick={() => router.replace(`/people?workspaceId=${workspaceId}`)}
          >
            Back to people
          </button>
        ) : canManage ? (
          <button
            className="screen-btn primary"
            type="button"
            onClick={() => router.replace(`/people?workspaceId=${workspaceId}&add=1`)}
          >
            Invite people
          </button>
        ) : undefined
      }
    >
      <div className="screen-body people-layout">
        {showAdd && canManage ? (
          <section className="people-invite">
            <div className="people-invite-card">
              <h2>Invite someone to this workspace</h2>
              <p>
                Send an invite by work email. Existing accounts get a notification and must accept before
                joining. New emails can join after they create an account.
              </p>
              <form className="form-stack" onSubmit={inviteUser}>
                <label>
                  Email address
                  <input
                    type="email"
                    value={inviteEmail}
                    onChange={(e) => setInviteEmail(e.target.value)}
                    placeholder="alex@company.com"
                    required
                  />
                </label>
                <label>
                  Role
                  <select value={inviteRole} onChange={(e) => setInviteRole(e.target.value)}>
                    <option value="member">Member</option>
                    <option value="admin">Admin</option>
                    <option value="multi_channel_guest">Multi-channel guest</option>
                    <option value="single_channel_guest">Single-channel guest</option>
                  </select>
                </label>
                <button className="screen-btn primary" type="submit" disabled={busy}>
                  {busy ? "Sending…" : "Send invite"}
                </button>
              </form>
              {inviteMessage && <p className="success-text">{inviteMessage}</p>}
              {error && <p className="error-text">{error}</p>}
            </div>

            <div className="people-invite-pending">
              <h3>Pending invites</h3>
              {pendingInvites.length === 0 && <div className="empty-state">No pending invites.</div>}
              <ul className="people-invite-list">
                {pendingInvites.map((invite) => (
                  <li key={invite.id}>
                    <strong>{invite.email ?? "Open invite"}</strong>
                    <span>
                      {roleLabel(invite.role)} · expires {new Date(invite.expiresAt).toLocaleDateString()}
                    </span>
                  </li>
                ))}
              </ul>
            </div>
          </section>
        ) : (
          <div className="people-directory">
            <div className="people-directory-list">
              <div className="people-search">
                <IconSearch />
                <input
                  value={query}
                  onChange={(e) => setQuery(e.target.value)}
                  placeholder="Search people"
                  aria-label="Search people"
                />
              </div>

              <div className="people-filters" role="tablist" aria-label="People filters">
                {filters
                  .filter((tab) => !tab.adminOnly || canManage)
                  .map((tab) => (
                    <button
                      key={tab.id}
                      type="button"
                      role="tab"
                      aria-selected={filter === tab.id}
                      className={filter === tab.id ? "people-filter active" : "people-filter"}
                      onClick={() => setFilter(tab.id)}
                    >
                      {tab.label}
                    </button>
                  ))}
              </div>

              {loading && <div className="empty-state">Loading people…</div>}
              {!loading && filtered.length === 0 && (
                <div className="empty-state">
                  {members.length === 0
                    ? "No one is in this workspace yet."
                    : query.trim()
                      ? "No people match your search."
                      : "No people in this filter."}
                </div>
              )}

              <div className="people-rows">
                {filtered.map((row) => {
                  const status = presence[String(row.user.id)]?.status;
                  const active = selected != null && String(selected.user.id) === String(row.user.id);
                  const name = displayNameOf(row);
                  const sub =
                    row.member.title?.trim() ||
                    (row.user.username ? `@${row.user.username}` : roleLabel(row.member.role));
                  return (
                    <button
                      type="button"
                      className={active ? "people-row active" : "people-row"}
                      key={String(row.user.id)}
                      onClick={() => void openDmFor(row.user.id)}
                      aria-label={`Message ${name}`}
                    >
                      <span className="list-row-avatar-wrap">
                        <UserAvatar
                          className="list-row-avatar"
                          userId={row.user.id}
                          name={name}
                          avatarUrl={row.user.avatarUrl}
                        />
                        {!row.member.deactivatedAt && (
                          <span
                            className="list-row-presence"
                            style={{ background: presenceColor(status) }}
                            title={presenceLabel(status)}
                          />
                        )}
                      </span>
                      <span className="people-row-main">
                        <span className="people-row-name">
                          {name}
                          {row.member.deactivatedAt && <span className="people-deactivated-pill">Deactivated</span>}
                        </span>
                        <span className="people-row-sub">
                          {sub}
                          {!row.member.deactivatedAt && <> · {presenceLabel(status)}</>}
                        </span>
                      </span>
                    </button>
                  );
                })}
              </div>
            </div>

            {selected ? (
              <section className="people-profile">
                <UserAvatar
                  className="people-profile-avatar"
                  userId={selected.user.id}
                  name={displayNameOf(selected)}
                  avatarUrl={selected.user.avatarUrl}
                />
                <h2>{displayNameOf(selected)}</h2>
                {selected.member.title && <p className="people-profile-title">{selected.member.title}</p>}
                <p className="people-profile-email">{selected.user.email}</p>

                {(selected.member.statusText || selected.member.statusEmoji) && (
                  <p className="people-profile-status">
                    {selected.member.statusEmoji ? `${selected.member.statusEmoji} ` : ""}
                    {selected.member.statusText}
                  </p>
                )}

                {selected.member.deactivatedAt && (
                  <p className="people-profile-banner">This person is deactivated and cannot be messaged.</p>
                )}

                <dl className="people-profile-meta">
                  <div>
                    <dt>Role</dt>
                    <dd>{roleLabel(selected.member.role)}</dd>
                  </div>
                  <div>
                    <dt>Status</dt>
                    <dd>
                      {selected.member.deactivatedAt
                        ? "Deactivated"
                        : presenceLabel(presence[String(selected.user.id)]?.status)}
                    </dd>
                  </div>
                  {selected.user.username && (
                    <div>
                      <dt>Username</dt>
                      <dd>@{selected.user.username}</dd>
                    </div>
                  )}
                  <div>
                    <dt>Joined</dt>
                    <dd>{formatJoined(selected.member.joinedAt)}</dd>
                  </div>
                </dl>

                <div className="people-profile-actions">
                  {String(selected.user.id) !== String(myUserId) && !selected.member.deactivatedAt && (
                    <button className="screen-btn primary" type="button" onClick={openDm} disabled={busy}>
                      {busy ? "Opening…" : "Message"}
                    </button>
                  )}
                </div>

                {canManage &&
                  String(selected.user.id) !== String(myUserId) &&
                  selected.member.role !== "owner" && (
                    <div className="people-admin">
                      <h3>Admin</h3>
                      <label>
                        Role
                        <select
                          value={selected.member.role}
                          disabled={busy}
                          onChange={(e) => void changeRole(e.target.value)}
                          aria-label={`Role for ${displayNameOf(selected)}`}
                        >
                          <option value="admin">Admin</option>
                          <option value="member">Member</option>
                          <option value="multi_channel_guest">Multi-channel guest</option>
                          <option value="single_channel_guest">Single-channel guest</option>
                        </select>
                      </label>
                      <button className="screen-btn" type="button" disabled={busy} onClick={() => void toggleActive()}>
                        {selected.member.deactivatedAt ? "Reactivate" : "Deactivate"}
                      </button>
                    </div>
                  )}

                {error && <p className="error-text">{error}</p>}
              </section>
            ) : (
              !loading && (
                <section className="people-profile empty">
                  <div className="empty-state">
                    {members.length === 0
                      ? canManage
                        ? "Invite teammates to build your directory."
                        : "No people to show yet."
                      : "Select someone to view their profile."}
                  </div>
                  {members.length === 0 && canManage && (
                    <button
                      className="screen-btn primary"
                      type="button"
                      onClick={() => router.replace(`/people?workspaceId=${workspaceId}&add=1`)}
                    >
                      Invite people
                    </button>
                  )}
                </section>
              )
            )}
          </div>
        )}
      </div>
    </AppShell>
  );
}

export default function PeoplePage() {
  return (
    <Suspense fallback={<main className="page"><div className="empty-state">Loading people…</div></main>}>
      <PeopleView />
    </Suspense>
  );
}
