import { NextResponse } from "next/server";
import { BEXOGAMES_DISCORD_GUILD_ID, BEXOGAMES_STREAMER_ROLE_ID } from "../../../lib/discord";
import {
  TWITCH_CHANNELS_BY_DISCORD_ID,
  type CommunityStreamer,
  type CommunityStreamerResponse,
} from "../../../lib/streamers";

export const dynamic = "force-dynamic";

type DiscordMember = {
  roles?: string[];
  nick?: string | null;
  user?: {
    id?: string;
    username?: string;
    global_name?: string | null;
  };
};

type TwitchUser = {
  id: string;
  login: string;
  display_name: string;
  profile_image_url: string;
  offline_image_url: string;
};

type TwitchStream = {
  user_id: string;
  user_login: string;
  title: string;
  game_name: string;
  viewer_count: number;
  thumbnail_url: string;
  started_at: string;
};

async function getDiscordStreamers(token: string, guildId: string, roleId: string) {
  const members: DiscordMember[] = [];
  let after: string | undefined;

  for (let page = 0; page < 20; page++) {
    const params = new URLSearchParams({ limit: "1000" });
    if (after) params.set("after", after);

    const response = await fetch(`https://discord.com/api/v10/guilds/${guildId}/members?${params}`, {
      cache: "no-store",
      headers: {
        Authorization: `Bot ${token}`,
        "User-Agent": "BexoGames.de Website",
      },
    });

    if (!response.ok) throw new Error(`Discord Member API antwortete mit ${response.status}`);

    const pageMembers: DiscordMember[] = await response.json();
    members.push(...pageMembers);
    if (pageMembers.length < 1000) break;
    after = pageMembers[pageMembers.length - 1]?.user?.id;
    if (!after) break;
  }

  return members
    .filter((member) => member.user?.id && member.roles?.includes(roleId))
    .map((member) => ({
      id: member.user!.id!,
      username: member.user!.username || "Discord User",
      displayName: member.nick || member.user!.global_name || member.user!.username || "Discord User",
    }));
}

async function getTwitchAccessToken(clientId: string, clientSecret: string) {
  const params = new URLSearchParams({
    client_id: clientId,
    client_secret: clientSecret,
    grant_type: "client_credentials",
  });

  const response = await fetch(`https://id.twitch.tv/oauth2/token?${params}`, {
    method: "POST",
    cache: "no-store",
  });

  if (!response.ok) throw new Error(`Twitch OAuth antwortete mit ${response.status}`);
  const data = await response.json();
  if (!data.access_token) throw new Error("Twitch hat keinen Access Token geliefert");
  return data.access_token as string;
}

export async function GET() {
  const discordToken = process.env.DISCORD_BOT_TOKEN;
  const twitchClientId = process.env.TWITCH_CLIENT_ID;
  const twitchClientSecret = process.env.TWITCH_CLIENT_SECRET;
  const guildId = process.env.DISCORD_GUILD_ID || BEXOGAMES_DISCORD_GUILD_ID;
  const roleId = process.env.DISCORD_STREAMER_ROLE_ID || BEXOGAMES_STREAMER_ROLE_ID;

  if (!discordToken || !twitchClientId || !twitchClientSecret) {
    const payload: CommunityStreamerResponse = {
      ok: true,
      configured: false,
      count: 0,
      liveCount: 0,
      streamers: [],
    };
    return NextResponse.json(payload);
  }

  try {
    const discordStreamers = await getDiscordStreamers(discordToken, guildId, roleId);
    const linked = discordStreamers
      .map((member) => ({ ...member, twitchLogin: TWITCH_CHANNELS_BY_DISCORD_ID[member.id] }))
      .filter((member): member is typeof member & { twitchLogin: string } => Boolean(member.twitchLogin));

    if (linked.length === 0) {
      const payload: CommunityStreamerResponse = {
        ok: true,
        configured: true,
        count: 0,
        liveCount: 0,
        streamers: [],
      };
      return NextResponse.json(payload);
    }

    const accessToken = await getTwitchAccessToken(twitchClientId, twitchClientSecret);
    const headers = {
      "Client-ID": twitchClientId,
      Authorization: `Bearer ${accessToken}`,
    };

    const userParams = new URLSearchParams();
    const streamParams = new URLSearchParams();
    linked.forEach(({ twitchLogin }) => {
      userParams.append("login", twitchLogin);
      streamParams.append("user_login", twitchLogin);
    });

    const [usersResponse, streamsResponse] = await Promise.all([
      fetch(`https://api.twitch.tv/helix/users?${userParams}`, { headers, cache: "no-store" }),
      fetch(`https://api.twitch.tv/helix/streams?${streamParams}`, { headers, cache: "no-store" }),
    ]);

    if (!usersResponse.ok) throw new Error(`Twitch User API antwortete mit ${usersResponse.status}`);
    if (!streamsResponse.ok) throw new Error(`Twitch Stream API antwortete mit ${streamsResponse.status}`);

    const usersData: { data: TwitchUser[] } = await usersResponse.json();
    const streamsData: { data: TwitchStream[] } = await streamsResponse.json();
    const usersByLogin = new Map(usersData.data.map((user) => [user.login.toLowerCase(), user]));
    const streamsByLogin = new Map(streamsData.data.map((stream) => [stream.user_login.toLowerCase(), stream]));

    const streamers: CommunityStreamer[] = linked
      .map((member) => {
        const twitchLogin = member.twitchLogin.toLowerCase();
        const user = usersByLogin.get(twitchLogin);
        if (!user) return null;
        const stream = streamsByLogin.get(twitchLogin);

        return {
          discordId: member.id,
          discordUsername: member.username,
          discordDisplayName: member.displayName,
          twitchLogin: user.login,
          twitchDisplayName: user.display_name,
          twitchUrl: `https://www.twitch.tv/${user.login}`,
          profileImageUrl: user.profile_image_url || null,
          offlineImageUrl: user.offline_image_url || null,
          isLive: Boolean(stream),
          title: stream?.title || null,
          gameName: stream?.game_name || null,
          viewerCount: stream?.viewer_count ?? null,
          thumbnailUrl: stream?.thumbnail_url
            ? stream.thumbnail_url.replace("{width}", "1280").replace("{height}", "720")
            : null,
          startedAt: stream?.started_at || null,
        } satisfies CommunityStreamer;
      })
      .filter((streamer): streamer is CommunityStreamer => streamer !== null)
      .sort((a, b) => Number(b.isLive) - Number(a.isLive) || a.twitchDisplayName.localeCompare(b.twitchDisplayName, "de"));

    const payload: CommunityStreamerResponse = {
      ok: true,
      configured: true,
      count: streamers.length,
      liveCount: streamers.filter((streamer) => streamer.isLive).length,
      streamers,
    };

    return NextResponse.json(payload, {
      headers: { "Cache-Control": "private, max-age=30" },
    });
  } catch (error) {
    const payload: CommunityStreamerResponse = {
      ok: false,
      configured: true,
      count: 0,
      liveCount: 0,
      streamers: [],
      error: error instanceof Error ? error.message : "Streamer konnten nicht geladen werden",
    };
    return NextResponse.json(payload, { status: 200 });
  }
}
