import type express from 'express';
import type { Plugin, PluginContext } from '../../core/plugin.js';
import { sseBroadcast } from '../../core/sse.js';
import fs from 'node:fs';
import path from 'node:path';
import crypto from 'node:crypto';
import { lookupGame, enrichGames, enrichGamesByName, initIgdbCache, type IgdbGameInfo } from './igdb.js';
import { getGogAuthUrl, exchangeGogCode, refreshGogToken, fetchGogUserInfo, fetchGogGames, type GogGame } from './gog.js';

// ── Types ──

interface SteamGame {
  appid: number;
  name: string;
  playtime_forever: number; // minutes
  img_icon_url: string;
  igdb?: IgdbGameInfo;
}

interface SteamUser {
  steamId: string;
  personaName: string;
  avatarUrl: string;
  profileUrl: string;
  games: SteamGame[];
  lastUpdated: string; // ISO date
}

interface GogUserData {
  gogUserId: string;
  username: string;
  avatarUrl: string;
  games: Array<{ gogId: number; title: string; image: string; slug: string; igdb?: IgdbGameInfo }>;
  accessToken: string;
  refreshToken: string;
  tokenExpiresAt: number;
  lastUpdated: string;
}

interface UserProfile {
  id: string;
  displayName: string;
  avatarUrl: string;
  steamId?: string;
  gogUserId?: string;
  lastUpdated: string;
}

interface GameLibraryData {
  users: Record<string, SteamUser>;         // keyed by steamId
  gogUsers: Record<string, GogUserData>;    // keyed by gogUserId
  profiles: Record<string, UserProfile>;    // keyed by profileId
}

// ── Constants ──

const STEAM_API_KEY = process.env.STEAM_API_KEY || '2481D43449821AA4FF66502A9166DF32';

// ── Admin auth helpers (same system as soundboard) ──

function readCookie(req: express.Request, name: string): string | undefined {
  const raw = req.headers.cookie || '';
  const match = raw.match(new RegExp(`(?:^|;\\s*)${name}=([^;]*)`));
  return match ? decodeURIComponent(match[1]) : undefined;
}

function b64url(str: string): string {
  return Buffer.from(str).toString('base64url');
}

function verifyAdminToken(adminPwd: string, token: string | undefined): boolean {
  if (!token || !adminPwd) return false;
  const [body, sig] = token.split('.');
  if (!body || !sig) return false;
  const expected = crypto.createHmac('sha256', adminPwd).update(body).digest('base64url');
  if (expected !== sig) return false;
  try {
    const payload = JSON.parse(Buffer.from(body, 'base64').toString('utf8')) as { iat: number; exp: number };
    return typeof payload.exp === 'number' && Date.now() < payload.exp;
  } catch { return false; }
}

function signAdminToken(adminPwd: string): string {
  const payload = { iat: Date.now(), exp: Date.now() + 7 * 24 * 3600_000 };
  const body = b64url(JSON.stringify(payload));
  const sig = crypto.createHmac('sha256', adminPwd).update(body).digest('base64url');
  return `${body}.${sig}`;
}

// ── Data Persistence ──

function getDataPath(ctx: PluginContext): string {
  return `${ctx.dataDir}/game-library.json`;
}

function loadData(ctx: PluginContext): GameLibraryData {
  try {
    const raw = fs.readFileSync(getDataPath(ctx), 'utf-8');
    return JSON.parse(raw) as GameLibraryData;
  } catch {
    const empty: GameLibraryData = { users: {}, gogUsers: {}, profiles: {} };
    saveData(ctx, empty);
    return empty;
  }
}

function saveData(ctx: PluginContext, data: GameLibraryData): void {
  try {
    const dir = path.dirname(getDataPath(ctx));
    if (!fs.existsSync(dir)) {
      fs.mkdirSync(dir, { recursive: true });
    }
    fs.writeFileSync(getDataPath(ctx), JSON.stringify(data, null, 2), 'utf-8');
  } catch (err) {
    console.error('[GameLibrary] Failed to save data:', err);
  }
}

// ── SSE Broadcast ──

function buildProfileSummaries(data: GameLibraryData) {
  return Object.values(data.profiles || {}).map(p => {
    const steam = p.steamId ? data.users[p.steamId] : null;
    const gog = p.gogUserId ? data.gogUsers?.[p.gogUserId] : null;
    return {
      id: p.id,
      displayName: p.displayName,
      avatarUrl: p.avatarUrl,
      platforms: {
        steam: steam ? { steamId: steam.steamId, personaName: steam.personaName, gameCount: steam.games.length } : null,
        gog: gog ? { gogUserId: gog.gogUserId, username: gog.username, gameCount: gog.games.length } : null,
      },
      totalGames: (steam?.games.length || 0) + (gog?.games.length || 0),
      lastUpdated: p.lastUpdated,
    };
  });
}

function broadcastUpdate(data: GameLibraryData): void {
  const profiles = buildProfileSummaries(data);
  sseBroadcast({ type: 'game_library_update', plugin: 'game-library', profiles });
}

// ── Steam API Helpers ──

async function fetchSteamProfile(steamId: string): Promise<{ personaName: string; avatarUrl: string; profileUrl: string }> {
  const url = `https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v2/?key=${STEAM_API_KEY}&steamids=${steamId}`;
  const resp = await fetch(url);
  if (!resp.ok) {
    throw new Error(`Steam API error: ${resp.status} ${resp.statusText}`);
  }
  const json = await resp.json() as any;
  const player = json?.response?.players?.[0];
  if (!player) {
    throw new Error(`Steam user not found: ${steamId}`);
  }
  return {
    personaName: player.personaname || steamId,
    avatarUrl: player.avatarfull || '',
    profileUrl: player.profileurl || '',
  };
}

async function fetchSteamGames(steamId: string): Promise<SteamGame[]> {
  const url = `https://api.steampowered.com/IPlayerService/GetOwnedGames/v1/?key=${STEAM_API_KEY}&steamid=${steamId}&include_appinfo=true&include_played_free_games=true&format=json`;
  const resp = await fetch(url);
  if (!resp.ok) {
    throw new Error(`Steam API error: ${resp.status} ${resp.statusText}`);
  }
  const json = await resp.json() as any;
  const games: SteamGame[] = (json?.response?.games || []).map((g: any) => ({
    appid: g.appid,
    name: g.name || '',
    playtime_forever: g.playtime_forever || 0,
    img_icon_url: g.img_icon_url || '',
  }));
  return games;
}

// ── Plugin ──

const gameLibraryPlugin: Plugin = {
  name: 'game-library',
  version: '1.0.0',
  description: 'Steam Spielebibliothek',

  async init(ctx) {
    // Initialize persistent IGDB cache
    initIgdbCache(ctx.dataDir);

    const data = loadData(ctx); // ensure file exists

    // Migrate: ensure gogUsers and profiles exist
    if (!data.profiles) data.profiles = {};
    if (!data.gogUsers) data.gogUsers = {};

    // Migrate existing Steam users to profiles
    const existingProfileSteamIds = new Set(Object.values(data.profiles).map(p => p.steamId).filter(Boolean));
    for (const user of Object.values(data.users)) {
      if (!existingProfileSteamIds.has(user.steamId)) {
        const profileId = crypto.randomUUID();
        data.profiles[profileId] = {
          id: profileId,
          displayName: user.personaName,
          avatarUrl: user.avatarUrl,
          steamId: user.steamId,
          lastUpdated: user.lastUpdated,
        };
      }
    }
    saveData(ctx, data);

    console.log('[GameLibrary] Initialized');

    // Fire-and-forget: auto-enrich all existing users with IGDB data
    const allUsers = Object.values(data.users);
    const unenrichedUsers = allUsers.filter(u =>
      u.games.some(g => !g.igdb),
    );
    if (unenrichedUsers.length > 0) {
      console.log(`[GameLibrary] Auto-enriching ${unenrichedUsers.length} user(s) with IGDB data...`);
      (async () => {
        for (const user of unenrichedUsers) {
          try {
            const unenrichedGames = user.games.filter(g => !g.igdb).map(g => ({ appid: g.appid, name: g.name }));
            console.log(`[GameLibrary] IGDB auto-enriching ${user.personaName}: ${unenrichedGames.length} games...`);
            const igdbMap = await enrichGames(unenrichedGames);
            // Reload fresh to avoid stale writes
            const freshData = loadData(ctx);
            const freshUser = freshData.users[user.steamId];
            if (freshUser) {
              let count = 0;
              for (const game of freshUser.games) {
                const info = igdbMap.get(game.appid);
                if (info) { game.igdb = info; count++; }
              }
              saveData(ctx, freshData);
              console.log(`[GameLibrary] IGDB startup enrichment for ${user.personaName}: ${count}/${unenrichedGames.length} games matched`);
            }
          } catch (err) {
            console.error(`[GameLibrary] IGDB startup enrichment error for ${user.personaName}:`, err);
          }
        }
        console.log('[GameLibrary] IGDB startup enrichment complete.');
      })();
    }
  },

  registerRoutes(app: express.Application, ctx: PluginContext) {
    // ── Steam OpenID Login ──
    app.get('/api/game-library/steam/login', (_req, res) => {
      const proto = _req.headers['x-forwarded-proto'] || _req.protocol || 'http';
      const host = _req.headers['x-forwarded-host'] || _req.headers.host || 'localhost';
      const realm = `${proto}://${host}`;
      const returnTo = `${realm}/api/game-library/steam/callback`;

      const params = new URLSearchParams({
        'openid.ns': 'http://specs.openid.net/auth/2.0',
        'openid.mode': 'checkid_setup',
        'openid.return_to': returnTo,
        'openid.realm': realm,
        'openid.identity': 'http://specs.openid.net/auth/2.0/identifier_select',
        'openid.claimed_id': 'http://specs.openid.net/auth/2.0/identifier_select',
      });

      res.redirect(`https://steamcommunity.com/openid/login?${params.toString()}`);
    });

    // ── Steam OpenID Callback ──
    app.get('/api/game-library/steam/callback', async (req, res) => {
      try {
        const claimedId = String(req.query['openid.claimed_id'] || '');
        const steamIdMatch = claimedId.match(/\/id\/(\d+)$/);
        if (!steamIdMatch) {
          res.status(400).send(errorPage('Steam-Authentifizierung fehlgeschlagen', 'Keine gueltige Steam-ID erhalten.'));
          return;
        }
        const steamId = steamIdMatch[1];

        // Verify authentication with Steam
        const verifyParams = new URLSearchParams();
        for (const [key, value] of Object.entries(req.query)) {
          verifyParams.set(key, String(value));
        }
        verifyParams.set('openid.mode', 'check_authentication');

        const verifyResp = await fetch(`https://steamcommunity.com/openid/login?${verifyParams.toString()}`);
        const verifyText = await verifyResp.text();

        if (!verifyText.includes('is_valid:true')) {
          res.status(403).send(errorPage('Verifizierung fehlgeschlagen', 'Steam konnte die Anmeldung nicht verifizieren.'));
          return;
        }

        // Fetch profile and games in parallel
        const [profile, games] = await Promise.all([
          fetchSteamProfile(steamId),
          fetchSteamGames(steamId),
        ]);

        // Store user data
        const data = loadData(ctx);
        data.users[steamId] = {
          steamId,
          personaName: profile.personaName,
          avatarUrl: profile.avatarUrl,
          profileUrl: profile.profileUrl,
          games,
          lastUpdated: new Date().toISOString(),
        };

        // Create or update profile for this Steam user
        if (!data.profiles) data.profiles = {};
        const existingProfile = Object.values(data.profiles).find(p => p.steamId === steamId);
        if (!existingProfile) {
          const profileId = crypto.randomUUID();
          data.profiles[profileId] = {
            id: profileId,
            displayName: profile.personaName,
            avatarUrl: profile.avatarUrl,
            steamId,
            lastUpdated: new Date().toISOString(),
          };
        }

        saveData(ctx, data);
        broadcastUpdate(data);

        // Fire-and-forget IGDB enrichment
        enrichGames(games.map(g => ({ appid: g.appid, name: g.name }))).then(igdbMap => {
          const data2 = loadData(ctx);
          const user2 = data2.users[steamId];
          if (user2) {
            let count = 0;
            for (const game of user2.games) {
              const info = igdbMap.get(game.appid);
              if (info) { game.igdb = info; count++; }
            }
            saveData(ctx, data2);
            console.log(`[GameLibrary] IGDB auto-enrichment: ${count}/${games.length} games`);
          }
        }).catch(err => console.error('[GameLibrary] IGDB auto-enrichment error:', err));

        const personaName = profile.personaName;
        console.log(`[GameLibrary] Steam-Benutzer verknuepft: ${personaName} (${steamId}) - ${games.length} Spiele`);

        res.send(`<!DOCTYPE html><html><head><title>Steam verbunden</title><style>body{background:#1a1a2e;color:#fff;font-family:sans-serif;display:flex;align-items:center;justify-content:center;height:100vh;margin:0}div{text-align:center}h2{color:#2ecc71}</style></head><body><div><h2>Steam verbunden!</h2><p>${personaName} wurde erfolgreich verknuepft.</p><p>${games.length} Spiele geladen.</p><p>Du kannst dieses Fenster schliessen.</p><script>setTimeout(()=>window.close(),3000)</script></div></body></html>`);
      } catch (err) {
        console.error('[GameLibrary] Callback error:', err);
        res.status(500).send(errorPage('Fehler', 'Ein unerwarteter Fehler ist aufgetreten.'));
      }
    });

    // ── GET /api/game-library/users ──
    app.get('/api/game-library/users', (_req, res) => {
      const data = loadData(ctx);
      const users = Object.values(data.users).map(u => ({
        steamId: u.steamId,
        personaName: u.personaName,
        avatarUrl: u.avatarUrl,
        gameCount: u.games.length,
        lastUpdated: u.lastUpdated,
      }));
      res.json({ users });
    });

    // ── GET /api/game-library/user/:steamId ──
    app.get('/api/game-library/user/:steamId', (req, res) => {
      const data = loadData(ctx);
      const user = data.users[req.params.steamId];
      if (!user) {
        res.status(404).json({ error: 'Benutzer nicht gefunden.' });
        return;
      }
      // Sort games by playtime descending
      const sortedGames = [...user.games].sort((a, b) => b.playtime_forever - a.playtime_forever);
      res.json({
        steamId: user.steamId,
        personaName: user.personaName,
        avatarUrl: user.avatarUrl,
        profileUrl: user.profileUrl,
        gameCount: sortedGames.length,
        lastUpdated: user.lastUpdated,
        games: sortedGames,
      });
    });

    // ── GET /api/game-library/common-games?users=id1,id2,... ──
    app.get('/api/game-library/common-games', (req, res) => {
      const userIds = String(req.query.users || '').split(',').filter(Boolean);
      if (userIds.length < 2) {
        res.status(400).json({ error: 'Mindestens zwei Steam-IDs erforderlich (users=id1,id2).' });
        return;
      }

      const data = loadData(ctx);

      // Validate all users exist
      for (const id of userIds) {
        if (!data.users[id]) {
          res.status(404).json({ error: `Benutzer ${id} nicht gefunden.` });
          return;
        }
      }

      // Build game sets per user: Map<appid, SteamGame>
      const userGameMaps = userIds.map(id => {
        const map = new Map<number, SteamGame>();
        for (const game of data.users[id].games) {
          map.set(game.appid, game);
        }
        return { userId: id, gameMap: map };
      });

      // Find intersection: games present in ALL users
      const firstUserGames = userGameMaps[0].gameMap;
      const commonAppIds: number[] = [];

      for (const appid of firstUserGames.keys()) {
        const allOwn = userGameMaps.every(u => u.gameMap.has(appid));
        if (allOwn) {
          commonAppIds.push(appid);
        }
      }

      // Build result with owner info
      const games = commonAppIds.map(appid => {
        const refGame = firstUserGames.get(appid)!;
        const owners = userIds.map(id => {
          const user = data.users[id];
          const userGame = user.games.find(g => g.appid === appid);
          return {
            steamId: user.steamId,
            personaName: user.personaName,
            playtime_forever: userGame?.playtime_forever || 0,
          };
        });
        return {
          appid: refGame.appid,
          name: refGame.name,
          img_icon_url: refGame.img_icon_url,
          ...(refGame.igdb ? { igdb: refGame.igdb } : {}),
          owners,
        };
      });

      // Sort by name
      games.sort((a, b) => a.name.localeCompare(b.name));

      res.json({ games, count: games.length });
    });

    // ── GET /api/game-library/search?q=term ──
    app.get('/api/game-library/search', (req, res) => {
      const query = String(req.query.q || '').trim().toLowerCase();
      if (!query) {
        res.status(400).json({ error: 'Suchbegriff erforderlich (q=...).' });
        return;
      }

      const data = loadData(ctx);
      const allUsers = Object.values(data.users);

      // Collect all unique games matching the query
      const gameMap = new Map<number, { appid: number; name: string; img_icon_url: string; owners: { steamId: string; personaName: string }[] }>();

      for (const user of allUsers) {
        for (const game of user.games) {
          if (game.name.toLowerCase().includes(query)) {
            if (!gameMap.has(game.appid)) {
              gameMap.set(game.appid, {
                appid: game.appid,
                name: game.name,
                img_icon_url: game.img_icon_url,
                owners: [],
              });
            }
            gameMap.get(game.appid)!.owners.push({
              steamId: user.steamId,
              personaName: user.personaName,
            });
          }
        }
      }

      const games = [...gameMap.values()].sort((a, b) => a.name.localeCompare(b.name));
      res.json({ games, count: games.length });
    });

    // ── GET /api/game-library/igdb/enrich/:steamId ──
    app.get('/api/game-library/igdb/enrich/:steamId', async (req, res) => {
      try {
        const { steamId } = req.params;
        const force = req.query.force === 'true';
        const data = loadData(ctx);
        const user = data.users[steamId];
        if (!user) {
          res.status(404).json({ error: 'Benutzer nicht gefunden.' });
          return;
        }

        const gamesToEnrich = force
          ? user.games.map(g => ({ appid: g.appid, name: g.name }))
          : user.games.filter(g => !g.igdb).map(g => ({ appid: g.appid, name: g.name }));

        const igdbMap = await enrichGames(gamesToEnrich);

        // Reload data to avoid stale writes
        const freshData = loadData(ctx);
        const freshUser = freshData.users[steamId];
        if (!freshUser) {
          res.status(404).json({ error: 'Benutzer nicht gefunden.' });
          return;
        }

        let enriched = 0;
        for (const game of freshUser.games) {
          const info = igdbMap.get(game.appid);
          if (info) {
            game.igdb = info;
            enriched++;
          }
        }

        saveData(ctx, freshData);
        console.log(`[GameLibrary] IGDB enrichment for ${freshUser.personaName}: ${enriched}/${freshUser.games.length} games`);

        res.json({ enriched, total: freshUser.games.length });
      } catch (err) {
        console.error('[GameLibrary] IGDB enrich error:', err);
        res.status(500).json({ error: 'Fehler bei der IGDB-Anreicherung.' });
      }
    });

    // ── GET /api/game-library/igdb/game/:appid ──
    app.get('/api/game-library/igdb/game/:appid', async (req, res) => {
      try {
        const appid = parseInt(req.params.appid, 10);
        if (isNaN(appid)) {
          res.status(400).json({ error: 'Ungueltige App-ID.' });
          return;
        }

        // Find game name from any user's library
        const data = loadData(ctx);
        let gameName = '';
        for (const user of Object.values(data.users)) {
          const game = user.games.find(g => g.appid === appid);
          if (game) {
            gameName = game.name;
            break;
          }
        }

        if (!gameName) {
          res.status(404).json({ error: 'Spiel nicht in der Bibliothek gefunden.' });
          return;
        }

        const info = await lookupGame(appid, gameName);
        if (!info) {
          res.status(404).json({ error: 'Spiel nicht in IGDB gefunden.' });
          return;
        }

        res.json(info);
      } catch (err) {
        console.error('[GameLibrary] IGDB game lookup error:', err);
        res.status(500).json({ error: 'Fehler bei der IGDB-Abfrage.' });
      }
    });

    // ── POST /api/game-library/refresh/:steamId ──
    app.post('/api/game-library/refresh/:steamId', async (req, res) => {
      try {
        const { steamId } = req.params;
        const data = loadData(ctx);
        const user = data.users[steamId];
        if (!user) {
          res.status(404).json({ error: 'Benutzer nicht gefunden.' });
          return;
        }

        // Re-fetch profile and games in parallel
        const [profile, games] = await Promise.all([
          fetchSteamProfile(steamId),
          fetchSteamGames(steamId),
        ]);

        data.users[steamId] = {
          steamId,
          personaName: profile.personaName,
          avatarUrl: profile.avatarUrl,
          profileUrl: profile.profileUrl,
          games,
          lastUpdated: new Date().toISOString(),
        };
        saveData(ctx, data);
        broadcastUpdate(data);

        // Fire-and-forget IGDB enrichment
        enrichGames(games.map(g => ({ appid: g.appid, name: g.name }))).then(igdbMap => {
          const data2 = loadData(ctx);
          const user2 = data2.users[steamId];
          if (user2) {
            let count = 0;
            for (const game of user2.games) {
              const info = igdbMap.get(game.appid);
              if (info) { game.igdb = info; count++; }
            }
            saveData(ctx, data2);
            console.log(`[GameLibrary] IGDB auto-enrichment: ${count}/${games.length} games`);
          }
        }).catch(err => console.error('[GameLibrary] IGDB auto-enrichment error:', err));

        console.log(`[GameLibrary] Aktualisiert: ${profile.personaName} (${steamId}) - ${games.length} Spiele`);

        res.json({
          steamId,
          personaName: profile.personaName,
          avatarUrl: profile.avatarUrl,
          profileUrl: profile.profileUrl,
          gameCount: games.length,
          lastUpdated: data.users[steamId].lastUpdated,
        });
      } catch (err) {
        console.error('[GameLibrary] Refresh error:', err);
        res.status(500).json({ error: 'Fehler beim Aktualisieren der Spielebibliothek.' });
      }
    });

    // ── GET /api/game-library/profiles ──
    app.get('/api/game-library/profiles', (_req, res) => {
      const data = loadData(ctx);
      res.json({ profiles: buildProfileSummaries(data) });
    });

    // ── GOG Login (redirect to GOG auth page) ──
    app.get('/api/game-library/gog/login', (req, res) => {
      const linkTo = req.query.linkTo ? String(req.query.linkTo) : '';
      // Pass linkTo as OAuth state param — GOG passes it back to redirect_uri
      let authUrl = getGogAuthUrl();
      if (linkTo) authUrl += `&state=${encodeURIComponent(linkTo)}`;
      res.redirect(authUrl);
    });

    // ── GOG Code Exchange ──
    // User pastes the code from the GOG redirect URL.  We exchange it for
    // tokens and fetch the game library.
    app.post('/api/game-library/gog/exchange', async (req, res) => {
      try {
        const code = String(req.body?.code || '');
        const linkToProfileId = String(req.body?.linkTo || '');
        if (!code) {
          res.status(400).json({ error: 'Kein Authorization-Code angegeben.' });
          return;
        }

        // Exchange code for tokens
        const tokens = await exchangeGogCode(code);

        // Fetch user info + games
        const [userInfo, games] = await Promise.all([
          fetchGogUserInfo(tokens.accessToken),
          fetchGogGames(tokens.accessToken),
        ]);

        const data = loadData(ctx);
        if (!data.gogUsers) data.gogUsers = {};
        if (!data.profiles) data.profiles = {};

        // Store GOG user
        data.gogUsers[userInfo.userId] = {
          gogUserId: userInfo.userId,
          username: userInfo.username,
          avatarUrl: userInfo.avatarUrl,
          games,
          accessToken: tokens.accessToken,
          refreshToken: tokens.refreshToken,
          tokenExpiresAt: tokens.expiresAt,
          lastUpdated: new Date().toISOString(),
        };

        // Link to existing profile or create new one
        let profileName = userInfo.username;
        if (linkToProfileId && data.profiles[linkToProfileId]) {
          // Link GOG to existing profile
          data.profiles[linkToProfileId].gogUserId = userInfo.userId;
          data.profiles[linkToProfileId].lastUpdated = new Date().toISOString();
          profileName = data.profiles[linkToProfileId].displayName;
        } else {
          // Check if GOG user already has a profile
          const existingProfile = Object.values(data.profiles).find(p => p.gogUserId === userInfo.userId);
          if (!existingProfile) {
            const profileId = crypto.randomUUID();
            data.profiles[profileId] = {
              id: profileId,
              displayName: userInfo.username,
              avatarUrl: userInfo.avatarUrl,
              gogUserId: userInfo.userId,
              lastUpdated: new Date().toISOString(),
            };
          }
        }

        saveData(ctx, data);
        broadcastUpdate(data);

        console.log(`[GameLibrary] GOG verknuepft: ${profileName} (${userInfo.userId}) - ${games.length} Spiele`);

        res.json({ ok: true, username: userInfo.username, gameCount: games.length, profileName });
      } catch (err) {
        console.error('[GameLibrary] GOG exchange error:', err);
        res.status(500).json({ error: 'GOG-Authentifizierung fehlgeschlagen.' });
      }
    });

    // ── GET /api/game-library/profile/:profileId/games ──
    app.get('/api/game-library/profile/:profileId/games', (req, res) => {
      const data = loadData(ctx);
      const profile = data.profiles[req.params.profileId];
      if (!profile) { res.status(404).json({ error: 'Profil nicht gefunden.' }); return; }

      const mergedGames: Array<{
        name: string;
        platform: 'steam' | 'gog';
        appid?: number;
        gogId?: number;
        playtime_forever?: number;
        img_icon_url?: string;
        image?: string;
        igdb?: IgdbGameInfo;
      }> = [];

      // Add Steam games
      if (profile.steamId && data.users[profile.steamId]) {
        for (const g of data.users[profile.steamId].games) {
          mergedGames.push({
            name: g.name,
            platform: 'steam',
            appid: g.appid,
            playtime_forever: g.playtime_forever,
            img_icon_url: g.img_icon_url,
            igdb: g.igdb,
          });
        }
      }

      // Add GOG games (avoid duplicates by name)
      if (profile.gogUserId && data.gogUsers[profile.gogUserId]) {
        const steamNames = new Set(mergedGames.map(g => g.name.toLowerCase()));
        for (const g of data.gogUsers[profile.gogUserId].games) {
          if (!steamNames.has(g.title.toLowerCase())) {
            mergedGames.push({
              name: g.title,
              platform: 'gog',
              gogId: g.gogId,
              image: g.image,
              igdb: g.igdb,
            });
          }
        }
      }

      // Sort by playtime (Steam first since they have playtime), then by name
      mergedGames.sort((a, b) => (b.playtime_forever || 0) - (a.playtime_forever || 0));

      res.json({
        profile: {
          id: profile.id,
          displayName: profile.displayName,
          avatarUrl: profile.avatarUrl,
          steamId: profile.steamId,
          gogUserId: profile.gogUserId,
        },
        games: mergedGames,
        totalGames: mergedGames.length,
      });
    });

    // ── DELETE /api/game-library/user/:steamId ──
    app.delete('/api/game-library/user/:steamId', (req, res) => {
      const { steamId } = req.params;
      const data = loadData(ctx);
      const user = data.users[steamId];
      if (!user) {
        res.status(404).json({ error: 'Benutzer nicht gefunden.' });
        return;
      }

      const personaName = user.personaName;
      delete data.users[steamId];
      saveData(ctx, data);
      broadcastUpdate(data);

      console.log(`[GameLibrary] Benutzer entfernt: ${personaName} (${steamId})`);
      res.json({ success: true, message: `${personaName} wurde entfernt.` });
    });

    // ── DELETE /api/game-library/profile/:profileId/steam ── Unlink Steam
    app.delete('/api/game-library/profile/:profileId/steam', (req, res) => {
      const data = loadData(ctx);
      const profile = data.profiles[req.params.profileId];
      if (!profile) { res.status(404).json({ error: 'Profil nicht gefunden.' }); return; }
      if (!profile.steamId) { res.status(400).json({ error: 'Kein Steam-Konto verknuepft.' }); return; }

      const steamId = profile.steamId;
      const name = data.users[steamId]?.personaName || steamId;
      delete profile.steamId;
      profile.lastUpdated = new Date().toISOString();

      // Wenn kein anderes Profil diesen Steam-User nutzt, Daten entfernen
      const stillUsed = Object.values(data.profiles).some(p => p.steamId === steamId);
      if (!stillUsed) delete data.users[steamId];

      // Profil löschen wenn keine Plattform mehr verknüpft
      if (!profile.steamId && !profile.gogUserId) {
        delete data.profiles[req.params.profileId];
        console.log(`[GameLibrary] Profil geloescht (keine Plattformen): ${profile.displayName}`);
      }

      saveData(ctx, data);
      broadcastUpdate(data);
      console.log(`[GameLibrary] Steam getrennt: ${name} von ${profile.displayName}`);
      res.json({ ok: true });
    });

    // ── DELETE /api/game-library/profile/:profileId/gog ── Unlink GOG
    app.delete('/api/game-library/profile/:profileId/gog', (req, res) => {
      const data = loadData(ctx);
      const profile = data.profiles[req.params.profileId];
      if (!profile) { res.status(404).json({ error: 'Profil nicht gefunden.' }); return; }
      if (!profile.gogUserId) { res.status(400).json({ error: 'Kein GOG-Konto verknuepft.' }); return; }

      const gogUserId = profile.gogUserId;
      const name = data.gogUsers?.[gogUserId]?.username || gogUserId;
      delete profile.gogUserId;
      profile.lastUpdated = new Date().toISOString();

      // Wenn kein anderes Profil diesen GOG-User nutzt, Daten entfernen
      const stillUsed = Object.values(data.profiles).some(p => p.gogUserId === gogUserId);
      if (!stillUsed && data.gogUsers) delete data.gogUsers[gogUserId];

      // Profil löschen wenn keine Plattform mehr verknüpft
      if (!profile.steamId && !profile.gogUserId) {
        delete data.profiles[req.params.profileId];
        console.log(`[GameLibrary] Profil geloescht (keine Plattformen): ${profile.displayName}`);
      }

      saveData(ctx, data);
      broadcastUpdate(data);
      console.log(`[GameLibrary] GOG getrennt: ${name} von ${profile.displayName}`);
      res.json({ ok: true });
    });

    // ── DELETE /api/game-library/profile/:profileId ── Ganzes Profil löschen
    app.delete('/api/game-library/profile/:profileId', (req, res) => {
      const data = loadData(ctx);
      const profile = data.profiles[req.params.profileId];
      if (!profile) { res.status(404).json({ error: 'Profil nicht gefunden.' }); return; }

      const displayName = profile.displayName;

      // Zugehörige Daten aufräumen wenn nicht anderweitig genutzt
      if (profile.steamId) {
        const stillUsed = Object.values(data.profiles).some(p => p.id !== profile.id && p.steamId === profile.steamId);
        if (!stillUsed) delete data.users[profile.steamId];
      }
      if (profile.gogUserId) {
        const stillUsed = Object.values(data.profiles).some(p => p.id !== profile.id && p.gogUserId === profile.gogUserId);
        if (!stillUsed && data.gogUsers) delete data.gogUsers[profile.gogUserId];
      }

      delete data.profiles[req.params.profileId];
      saveData(ctx, data);
      broadcastUpdate(data);

      console.log(`[GameLibrary] Profil geloescht: ${displayName}`);
      res.json({ ok: true });
    });

    // ═══════════════════════════════════════════════════════════════════
    //  Admin endpoints (same auth as soundboard)
    // ═══════════════════════════════════════════════════════════════════

    const requireAdmin = (req: express.Request, res: express.Response, next: () => void) => {
      if (!ctx.adminPwd) { res.status(503).json({ error: 'Admin nicht konfiguriert' }); return; }
      if (!verifyAdminToken(ctx.adminPwd, readCookie(req, 'admin'))) {
        res.status(401).json({ error: 'Nicht eingeloggt' });
        return;
      }
      next();
    };

    // ── GET /api/game-library/admin/status ──
    app.get('/api/game-library/admin/status', (req, res) => {
      if (!ctx.adminPwd) { res.json({ admin: false, configured: false }); return; }
      const valid = verifyAdminToken(ctx.adminPwd, readCookie(req, 'admin'));
      res.json({ admin: valid, configured: true });
    });

    // ── POST /api/game-library/admin/login ──
    app.post('/api/game-library/admin/login', (req, res) => {
      const password = String(req.body?.password || '');
      if (!ctx.adminPwd) { res.status(503).json({ error: 'Admin nicht konfiguriert' }); return; }
      if (password !== ctx.adminPwd) { res.status(401).json({ error: 'Falsches Passwort' }); return; }
      const token = signAdminToken(ctx.adminPwd);
      res.setHeader('Set-Cookie', `admin=${token}; HttpOnly; Path=/; Max-Age=${7 * 24 * 3600}; SameSite=Lax`);
      res.json({ ok: true });
    });

    // ── POST /api/game-library/admin/logout ──
    app.post('/api/game-library/admin/logout', (_req, res) => {
      res.setHeader('Set-Cookie', 'admin=; HttpOnly; Path=/; Max-Age=0; SameSite=Lax');
      res.json({ ok: true });
    });

    // ── GET /api/game-library/admin/profiles ── Alle Profile mit Details
    app.get('/api/game-library/admin/profiles', requireAdmin, (_req, res) => {
      const data = loadData(ctx);
      const profiles = Object.values(data.profiles).map(p => {
        const steam = p.steamId ? data.users[p.steamId] : null;
        const gog = p.gogUserId ? data.gogUsers?.[p.gogUserId] : null;
        return {
          id: p.id,
          displayName: p.displayName,
          avatarUrl: p.avatarUrl,
          steamId: p.steamId || null,
          steamName: steam?.personaName || null,
          steamGames: steam?.games.length || 0,
          gogUserId: p.gogUserId || null,
          gogName: gog?.username || null,
          gogGames: gog?.games.length || 0,
          totalGames: (steam?.games.length || 0) + (gog?.games.length || 0),
          lastUpdated: p.lastUpdated,
        };
      });
      res.json({ profiles });
    });

    // ── DELETE /api/game-library/admin/profile/:profileId ── Admin löscht Profil
    app.delete('/api/game-library/admin/profile/:profileId', requireAdmin, (req, res) => {
      const data = loadData(ctx);
      const profileId = String(req.params.profileId);
      const profile = data.profiles[profileId];
      if (!profile) { res.status(404).json({ error: 'Profil nicht gefunden.' }); return; }

      const displayName = profile.displayName;

      if (profile.steamId) {
        const stillUsed = Object.values(data.profiles).some(p => p.id !== profile.id && p.steamId === profile.steamId);
        if (!stillUsed) delete data.users[profile.steamId];
      }
      if (profile.gogUserId) {
        const stillUsed = Object.values(data.profiles).some(p => p.id !== profile.id && p.gogUserId === profile.gogUserId);
        if (!stillUsed && data.gogUsers) delete data.gogUsers[profile.gogUserId];
      }

      delete data.profiles[profileId];
      saveData(ctx, data);
      broadcastUpdate(data);

      console.log(`[GameLibrary] Admin: Profil geloescht: ${displayName}`);
      res.json({ ok: true, displayName });
    });
  },

  getSnapshot(ctx) {
    const data = loadData(ctx);
    return {
      'game-library': {
        profiles: buildProfileSummaries(data),
      },
    };
  },
};

// ── Helper: Error HTML Page ──

function errorPage(title: string, message: string): string {
  return `<!DOCTYPE html><html><head><title>${title}</title><style>body{background:#1a1a2e;color:#fff;font-family:sans-serif;display:flex;align-items:center;justify-content:center;height:100vh;margin:0}div{text-align:center}h2{color:#e74c3c}</style></head><body><div><h2>${title}</h2><p>${message}</p><p>Du kannst dieses Fenster schliessen.</p><script>setTimeout(()=>window.close(),5000)</script></div></body></html>`;
}

export default gameLibraryPlugin;
