/**
 * Discord guild permission helpers for dashboard access control.
 * Includes short-lived cache + 429 retry to avoid Discord rate limits.
 */

export interface DiscordGuild {
  id: string;
  name?: string;
  icon?: string | null;
  permissions: string;
  owner?: boolean;
}

const MANAGE_GUILD = BigInt(0x20);
const ADMINISTRATOR = BigInt(0x8);

const CACHE_TTL_MS = 60_000;
const MAX_RETRIES = 3;

type CacheEntry = { guilds: DiscordGuild[]; expiresAt: number };

const guildCache = new Map<string, CacheEntry>();
const inflight = new Map<string, Promise<DiscordGuild[]>>();

export function canManageGuild(guild: DiscordGuild): boolean {
  if (guild.owner) return true;
  try {
    const perms = BigInt(guild.permissions);
    return (
      (perms & ADMINISTRATOR) === ADMINISTRATOR ||
      (perms & MANAGE_GUILD) === MANAGE_GUILD
    );
  } catch {
    return false;
  }
}

function cacheKey(accessToken: string): string {
  // Avoid storing raw token as object key longer than needed — short fingerprint is enough for process cache
  return `tok:${accessToken.slice(0, 12)}:${accessToken.length}:${accessToken.slice(-8)}`;
}

export function invalidateUserGuildsCache(accessToken?: string) {
  if (!accessToken) {
    guildCache.clear();
    return;
  }
  guildCache.delete(cacheKey(accessToken));
}

async function fetchUserGuildsUncached(accessToken: string): Promise<DiscordGuild[]> {
  let attempt = 0;
  while (attempt < MAX_RETRIES) {
    attempt += 1;
    const res = await fetch("https://discord.com/api/users/@me/guilds", {
      headers: { Authorization: `Bearer ${accessToken}` },
      cache: "no-store",
    });

    if (res.ok) {
      return (await res.json()) as DiscordGuild[];
    }

    if (res.status === 429) {
      let retryAfter = 1;
      try {
        const body = await res.json();
        retryAfter = Number(body.retry_after ?? 1);
      } catch {
        const header = res.headers.get("retry-after");
        retryAfter = header ? Number(header) : 1;
      }
      const waitMs = Math.min(Math.max(retryAfter * 1000, 300), 5000);
      console.warn(`Discord guilds rate-limited — retry in ${waitMs}ms (attempt ${attempt}/${MAX_RETRIES})`);
      await new Promise((r) => setTimeout(r, waitMs));
      continue;
    }

    const body = await res.text().catch(() => "");
    console.error("Discord guilds fetch failed:", res.status, body);
    throw new Error(
      res.status === 401
        ? "Discord session expired — please sign in again."
        : `Failed to fetch your Discord servers. (${res.status})`
    );
  }

  throw new Error("Failed to fetch your Discord servers. (rate limited)");
}

export async function fetchUserGuilds(accessToken: string): Promise<DiscordGuild[]> {
  const key = cacheKey(accessToken);
  const cached = guildCache.get(key);
  if (cached && cached.expiresAt > Date.now()) {
    return cached.guilds;
  }

  const existing = inflight.get(key);
  if (existing) return existing;

  const promise = fetchUserGuildsUncached(accessToken)
    .then((guilds) => {
      guildCache.set(key, { guilds, expiresAt: Date.now() + CACHE_TTL_MS });
      return guilds;
    })
    .finally(() => {
      inflight.delete(key);
    });

  inflight.set(key, promise);
  return promise;
}

export async function userCanManageGuild(
  accessToken: string,
  guildId: string
): Promise<boolean> {
  const guilds = await fetchUserGuilds(accessToken);
  const guild = guilds.find((g) => String(g.id) === String(guildId));
  return guild ? canManageGuild(guild) : false;
}
