"use client";

import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { useEffect, useState, type ReactNode } from "react";
import { api, ApiError, getCurrentUserId, forceLogin, type ProfileUser } from "../lib/api";
import {
  channelPath,
  normalizeChannelRows,
  textChannels as onlyTextChannels,
  type ConversationRow,
} from "../lib/conversations";
import { CommandPalette } from "./CommandPalette";
import { GlobalNav } from "./GlobalNav";
import { WorkspaceSidebar, type SidebarActive, type SidebarMember } from "./WorkspaceSidebar";
import { hydrateUnreadCounts, useUnreadTotal } from "../lib/unread-store";

export type { SidebarMember as ShellMember };

const ACTIVE_BY_PATH: Array<{ prefix: string; active: SidebarActive }> = [
  { prefix: "/activity", active: "tasks" },
  { prefix: "/threads", active: "rooms" },
  { prefix: "/unreads", active: "rooms" },
  { prefix: "/saved", active: "rooms" },
  { prefix: "/files", active: "rooms" },
  { prefix: "/tasks", active: "tasks" },
  { prefix: "/home", active: "tasks" },
  { prefix: "/calendar", active: "calendar" },
  { prefix: "/calls", active: "calls" },
  { prefix: "/people", active: "people" },
  { prefix: "/settings", active: "settings" },
  { prefix: "/search", active: "search" },
  { prefix: "/channel", active: "rooms" },
  { prefix: "/workspace", active: "rooms" },
];

function withWs(href: string, workspaceId: string) {
  if (href === "/workspace") return `/workspace?id=${workspaceId}`;
  return `${href}?workspaceId=${workspaceId}`;
}

export function useWorkspaceIdParam(altKeys: string[] = ["workspaceId", "id"]) {
  const params = useSearchParams();
  for (const key of altKeys) {
    const value = params.get(key);
    if (value) return value;
  }
  return null;
}

/**
 * Four-column Windshield shell: global nav | sidebar | main [| thread/about].
 */
export function AppShell({
  active,
  title,
  subtitle,
  list,
  children,
  actions,
  thread,
}: {
  active?: SidebarActive | "home" | "messages" | "docs" | "analytics";
  title?: string;
  subtitle?: string;
  /** Optional override for the whole sidebar (rarely needed). */
  list?: ReactNode;
  children: ReactNode;
  actions?: ReactNode;
  thread?: ReactNode;
}) {
  const router = useRouter();
  const pathname = usePathname();
  const params = useSearchParams();
  const workspaceId = useWorkspaceIdParam();
  const activeChannelId = params.get("channelId");
  const [members, setMembers] = useState<SidebarMember[]>([]);
  const [channels, setChannels] = useState<ConversationRow[]>([]);
  const [presence, setPresence] = useState<Record<string, { status: string }>>({});
  const [myUserId, setMyUserId] = useState<number | null>(null);
  const [error, setError] = useState<string | null>(null);
  const [unreadAssignedCount, setUnreadAssignedCount] = useState(0);
  const [activityUnreadCount, setActivityUnreadCount] = useState(0);
  const [missedCallCount, setMissedCallCount] = useState(0);
  const textChannelRows = onlyTextChannels(channels);
  const chatUnread = useUnreadTotal(workspaceId);

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

  useEffect(() => {
    function onProfileUpdated(event: Event) {
      const user = (event as CustomEvent<ProfileUser>).detail;
      if (!user) return;
      setMembers((current) =>
        current.map((row) =>
          String(row.user.id) === String(user.id) ? { ...row, user: { ...row.user, avatarUrl: user.avatarUrl } } : row,
        ),
      );
    }
    window.addEventListener("profile:updated", onProfileUpdated);
    return () => window.removeEventListener("profile:updated", onProfileUpdated);
  }, []);

  useEffect(() => {
    if (!workspaceId) {
      router.replace("/workspaces");
      return;
    }
    api
      .members(workspaceId)
      .then((res) => setMembers(res.members as SidebarMember[]))
      .catch((err) => {
        if (err instanceof ApiError && err.status === 401) void forceLogin(router);
        else setError(String(err));
      });
    api
      .listChannels(workspaceId)
      .then((res) => {
        const rows = normalizeChannelRows(res.channels);
        setChannels(rows);
        hydrateUnreadCounts(workspaceId, rows);
      })
      .catch(() => undefined);
    api
      .presenceSnapshot(workspaceId)
      .then((res) => setPresence(res.presence))
      .catch(() => undefined);
  }, [workspaceId, router]);

  useEffect(() => {
    if (!workspaceId) return;
    function onPresence(e: Event) {
      const detail = (e as CustomEvent).detail as
        | { workspaceId?: string | number; userId: string | number; status: string }
        | undefined;
      if (!detail) return;
      if (detail.workspaceId != null && String(detail.workspaceId) !== String(workspaceId)) return;
      setPresence((prev) => ({ ...prev, [String(detail.userId)]: { status: detail.status } }));
    }
    window.addEventListener("slackwsh:presence", onPresence);
    return () => window.removeEventListener("slackwsh:presence", onPresence);
  }, [workspaceId]);

  useEffect(() => {
    if (!workspaceId) return;
    let cancelled = false;
    const refreshMembers = (event: Event) => {
      const changedWorkspaceId = (event as CustomEvent<{ workspaceId?: string | number }>).detail?.workspaceId;
      if (changedWorkspaceId != null && String(changedWorkspaceId) !== String(workspaceId)) return;
      void api.members(workspaceId).then((res) => {
        if (!cancelled) setMembers(res.members as SidebarMember[]);
      }).catch(() => undefined);
    };
    window.addEventListener("connecthub:member-status-changed", refreshMembers);
    const statusRefresh = window.setInterval(() => refreshMembers(new CustomEvent("status-tick")), 60_000);
    return () => {
      cancelled = true;
      window.clearInterval(statusRefresh);
      window.removeEventListener("connecthub:member-status-changed", refreshMembers);
    };
  }, [workspaceId]);

  useEffect(() => {
    if (!workspaceId) return;
    let cancelled = false;
    function refreshActivityCount() {
      void api.mentionActivity(workspaceId!).then((res) => {
        if (!cancelled) setActivityUnreadCount(res.results.filter((hit) => !hit.readAt).length);
      }).catch(() => undefined);
    }
    const onActivityChanged = (event: Event) => {
      const changedWorkspaceId = (event as CustomEvent<{ workspaceId?: string | number }>).detail?.workspaceId;
      if (changedWorkspaceId != null && String(changedWorkspaceId) !== workspaceId) return;
      refreshActivityCount();
    };
    refreshActivityCount();
    window.addEventListener("slackwsh:message", onActivityChanged);
    window.addEventListener("slackwsh:activity-read-updated", onActivityChanged);
    return () => {
      cancelled = true;
      window.removeEventListener("slackwsh:message", onActivityChanged);
      window.removeEventListener("slackwsh:activity-read-updated", onActivityChanged);
    };
  }, [workspaceId]);

  // Sidebar Tasks badge: unread assignment notifications, not open-todo count.
  // Cleared when /tasks marks them seen (slackwsh:tasks-seen).
  useEffect(() => {
    if (!workspaceId) return;
    let cancelled = false;
    function refresh() {
      api
        .unreadAssignedTasks(workspaceId!)
        .then((res) => {
          if (!cancelled) setUnreadAssignedCount(res.unread);
        })
        .catch(() => undefined);
    }
    refresh();
    window.addEventListener("slackwsh:task", refresh);
    window.addEventListener("slackwsh:tasks-seen", refresh);
    return () => {
      cancelled = true;
      window.removeEventListener("slackwsh:task", refresh);
      window.removeEventListener("slackwsh:tasks-seen", refresh);
    };
  }, [workspaceId]);

  // Calls badge: missed calls I have never acknowledged. Refreshed on call
  // events and on the "slackwsh:calls-seen" the calls page fires after clearing
  // them, so the badge disappears on visiting the page rather than on the next
  // navigation.
  useEffect(() => {
    if (!workspaceId) return;
    let cancelled = false;
    function refresh() {
      api
        .unseenMissedCalls(workspaceId!)
        .then((res) => {
          if (!cancelled) setMissedCallCount(res.unseen);
        })
        .catch(() => undefined);
    }
    refresh();
    window.addEventListener("slackwsh:call", refresh);
    window.addEventListener("slackwsh:calls-seen", refresh);
    return () => {
      cancelled = true;
      window.removeEventListener("slackwsh:call", refresh);
      window.removeEventListener("slackwsh:calls-seen", refresh);
    };
  }, [workspaceId]);

  if (!workspaceId) {
    return <main className="page"><div className="empty-state">Redirecting to workspaces…</div></main>;
  }

  const me = members.find((row) => String(row.user.id) === String(myUserId))?.user;
  const mappedActive: SidebarActive =
    active === "home"
      ? "tasks"
      : active === "people"
        ? "people"
        : active === "messages" || active === "docs" || active === "analytics"
          ? "rooms"
          : active ?? ACTIVE_BY_PATH.find((entry) => pathname?.startsWith(entry.prefix))?.active ?? "rooms";

  async function openDm(userId: string | number) {
    if (!workspaceId || myUserId == null || String(userId) === String(myUserId)) return;
    try {
      const dm = await api.createDm(workspaceId, String(userId));
      if (!dm?.id) return;
      router.push(channelPath(workspaceId, String(dm.id)));
    } catch {
      // Sidebar DM open is best-effort; People page surfaces its own errors.
    }
  }

  return (
    <main className="app windshield">
      <GlobalNav
        workspaceId={workspaceId}
        myUserId={myUserId}
        meName={me?.name}
        meAvatarUrl={me?.avatarUrl}
        chatCount={chatUnread}
        activityCount={activityUnreadCount}
        taskCount={unreadAssignedCount}
        missedCallCount={missedCallCount}
      />
      {list ?? (
        <WorkspaceSidebar
          workspaceId={workspaceId}
          workspaceLabel={title ?? "Workspace"}
          active={mappedActive}
          variant={mappedActive === "tasks" || mappedActive === "calendar" || mappedActive === "calls" ? "workspace" : pathname?.startsWith("/channel") || pathname?.startsWith("/workspace") ? "chat" : "workspace"}
          channels={textChannelRows}
          conversations={channels}
          members={members}
          presence={presence}
          myUserId={myUserId}
          taskCount={unreadAssignedCount}
          missedCallCount={missedCallCount}
          meName={me?.name}
          activeChannelId={activeChannelId}
          onOpenDm={(userId) => void openDm(userId)}
        />
      )}

      <section className="main">
        {(title || actions) && (
          <header className="main-head">
            <div className="main-head-row">
              <div className="main-head-left">
                {title && (
                  <div className="main-head-title-row">
                    <h1 className="main-head-title">{title}</h1>
                  </div>
                )}
                {subtitle && <div className="main-head-sub">{subtitle}</div>}
              </div>
              {actions && <div className="main-head-right">{actions}</div>}
            </div>
          </header>
        )}
        {children}
        {error && <p className="error-text" style={{ padding: 16 }}>{error}</p>}
      </section>

      {thread}

      <CommandPalette workspaceId={workspaceId} channels={textChannelRows.map((row) => row.channel)} />
    </main>
  );
}

export function ScreenHeader({ title, subtitle, actions }: { title: string; subtitle?: string; actions?: ReactNode }) {
  return (
    <header className="main-head">
      <div className="main-head-row">
        <div className="main-head-left">
          <div className="main-head-title-row">
            <h1 className="main-head-title">{title}</h1>
          </div>
          {subtitle && <div className="main-head-sub">{subtitle}</div>}
        </div>
        {actions && <div className="main-head-right">{actions}</div>}
      </div>
    </header>
  );
}

export { withWs };
