import { createSlackWebClient } from "./client.js";

import type { ChannelDirectoryEntry } from "../channels/plugins/types.js";
import type { DirectoryConfigParams } from "../channels/plugins/directory-config.js";
import { resolveSlackAccount } from "./accounts.js";

type SlackUser = {
  id?: string;
  name?: string;
  real_name?: string;
  is_bot?: boolean;
  is_app_user?: boolean;
  deleted?: boolean;
  profile?: {
    display_name?: string;
    real_name?: string;
    email?: string;
  };
};

type SlackChannel = {
  id?: string;
  name?: string;
  is_archived?: boolean;
  is_private?: boolean;
};

type SlackListUsersResponse = {
  members?: SlackUser[];
  response_metadata?: { next_cursor?: string };
};

type SlackListChannelsResponse = {
  channels?: SlackChannel[];
  response_metadata?: { next_cursor?: string };
};

function resolveReadToken(params: DirectoryConfigParams): string | undefined {
  const account = resolveSlackAccount({ cfg: params.cfg, accountId: params.accountId });
  const userToken = account.config.userToken?.trim() || undefined;
  return userToken ?? account.botToken?.trim();
}

function normalizeQuery(value?: string | null): string {
  return value?.trim().toLowerCase() ?? "";
}

function buildUserRank(user: SlackUser): number {
  let rank = 0;
  if (!user.deleted) rank += 2;
  if (!user.is_bot && !user.is_app_user) rank += 1;
  return rank;
}

function buildChannelRank(channel: SlackChannel): number {
  return channel.is_archived ? 0 : 1;
}

export async function listSlackDirectoryPeersLive(
  params: DirectoryConfigParams,
): Promise<ChannelDirectoryEntry[]> {
  const token = resolveReadToken(params);
  if (!token) return [];
  const client = createSlackWebClient(token);
  const query = normalizeQuery(params.query);
  const members: SlackUser[] = [];
  let cursor: string | undefined;

  do {
    const res = (await client.users.list({
      limit: 200,
      cursor,
    })) as SlackListUsersResponse;
    if (Array.isArray(res.members)) members.push(...res.members);
    const next = res.response_metadata?.next_cursor?.trim();
    cursor = next ? next : undefined;
  } while (cursor);

  const filtered = members.filter((member) => {
    const name = member.profile?.display_name || member.profile?.real_name || member.real_name;
    const handle = member.name;
    const email = member.profile?.email;
    const candidates = [name, handle, email]
      .map((item) => item?.trim().toLowerCase())
      .filter(Boolean);
    if (!query) return true;
    return candidates.some((candidate) => candidate?.includes(query));
  });

  const rows = filtered
    .map((member) => {
      const id = member.id?.trim();
      if (!id) return null;
      const handle = member.name?.trim();
      const display =
        member.profile?.display_name?.trim() ||
        member.profile?.real_name?.trim() ||
        member.real_name?.trim() ||
        handle;
      return {
        kind: "user",
        id: `user:${id}`,
        name: display || undefined,
        handle: handle ? `@${handle}` : undefined,
        rank: buildUserRank(member),
        raw: member,
      } satisfies ChannelDirectoryEntry;
    })
    .filter(Boolean) as ChannelDirectoryEntry[];

  if (typeof params.limit === "number" && params.limit > 0) {
    return rows.slice(0, params.limit);
  }
  return rows;
}

export async function listSlackDirectoryGroupsLive(
  params: DirectoryConfigParams,
): Promise<ChannelDirectoryEntry[]> {
  const token = resolveReadToken(params);
  if (!token) return [];
  const client = createSlackWebClient(token);
  const query = normalizeQuery(params.query);
  const channels: SlackChannel[] = [];
  let cursor: string | undefined;

  do {
    const res = (await client.conversations.list({
      types: "public_channel,private_channel",
      exclude_archived: false,
      limit: 1000,
      cursor,
    })) as SlackListChannelsResponse;
    if (Array.isArray(res.channels)) channels.push(...res.channels);
    const next = res.response_metadata?.next_cursor?.trim();
    cursor = next ? next : undefined;
  } while (cursor);

  const filtered = channels.filter((channel) => {
    const name = channel.name?.trim().toLowerCase();
    if (!query) return true;
    return Boolean(name && name.includes(query));
  });

  const rows = filtered
    .map((channel) => {
      const id = channel.id?.trim();
      const name = channel.name?.trim();
      if (!id || !name) return null;
      return {
        kind: "group",
        id: `channel:${id}`,
        name,
        handle: `#${name}`,
        rank: buildChannelRank(channel),
        raw: channel,
      } satisfies ChannelDirectoryEntry;
    })
    .filter(Boolean) as ChannelDirectoryEntry[];

  if (typeof params.limit === "number" && params.limit > 0) {
    return rows.slice(0, params.limit);
  }
  return rows;
}
