/* eslint-disable @typescript-eslint/no-unused-vars */
import type * as Party from 'partykit/server';
import { createClerkClient, verifyToken } from '@clerk/backend';

interface GameState {
  totalClicks: number;
  users: Record<string, { name: string; clicks: number; lastSeen: number; bonusMultiplier: number }>; // Added bonusMultiplier
  upgrades: Record<string, { owned: number; cost: number }>;
  milestones: Record<string, boolean>;
  clickMultiplier: number;
  autoClickRate: number;
  currentBackground: string;
  currentClickImage: string;
}

interface AuthenticatedMessage {
  token: string;
  userId: string; // Expected userId from client
  userName: string; // Expected userName from client
}

interface ClickMessage extends AuthenticatedMessage {
  type: 'click';
}

interface PurchaseUpgradeMessage extends AuthenticatedMessage {
  type: 'purchase-upgrade';
  upgradeId: string;
}

interface ApplyMultiplierBonusMessage extends AuthenticatedMessage { // New message type
  type: 'apply-multiplier-bonus';
  multiplierBonus: number;
}

interface UserJoinMessage extends AuthenticatedMessage {
  type: 'user-join';
}

type Message = ClickMessage | PurchaseUpgradeMessage | ApplyMultiplierBonusMessage | UserJoinMessage; // Updated Message type

const UPGRADES = {
  clickMultiplier: { baseCost: 10, multiplier: 1.5, clickBonus: 1 },
  autoClicker: { baseCost: 50, multiplier: 2, autoClickRate: 1 },
  megaBonus: { baseCost: 200, multiplier: 2.5, clickBonus: 5 },
  hyperClicker: { baseCost: 1000, multiplier: 3, autoClickRate: 10 },
  quantumClicker: { baseCost: 5000, multiplier: 4, clickBonus: 50 },
  friendBoost: { baseCost: 2000, multiplier: 3, clickMultiplierBonus: 1.02 } // Renamed from shoominions upgrade
};

const MILESTONES = [
  { threshold: 100, id: 'first-hundred', background: 'rainbow', image: 'https://media1.tenor.com/m/x8v1oNUOmg4AAAAd/spinning-rat-rat.gif' },
  { threshold: 500, id: 'five-hundred', background: 'matrix', image: 'https://media1.tenor.com/m/pV74fmh_NLgAAAAd/louie-rat-spinning-rat.gif' },
  { threshold: 1000, id: 'one-thousand', background: 'cyberpunk', image: 'https://media1.tenor.com/m/YsWlbVbRWFQAAAAd/rat-spinning.gif' },
  { threshold: 2500, id: 'epic-milestone', background: 'space', image: 'https://media1.tenor.com/m/x8v1oNUOmg4AAAAd/spinning-rat-rat.gif' },
  { threshold: 5000, id: 'legendary', background: 'glitch', image: 'https://media1.tenor.com/m/pV74fmh_NLgAAAAd/louie-rat-spinning-rat.gif' },
  { threshold: 10000, id: 'ultimate', background: 'ultimate', image: 'https://media1.tenor.com/m/YsWlbVbRWFQAAAAd/rat-spinning.gif' }
];

export default class GameServer implements Party.Server {
  clerkClient: ReturnType<typeof createClerkClient>;

  constructor(readonly party: Party.Party) {
    this.clerkClient = createClerkClient({
      secretKey: party.env.CLERK_SECRET_KEY as string,
    });
  }

  gameState: GameState = {
    totalClicks: 0,
    users: {},
    upgrades: Object.keys(UPGRADES).reduce((acc, key) => {
      acc[key] = { owned: 0, cost: UPGRADES[key as keyof typeof UPGRADES].baseCost };
      return acc;
    }, {} as Record<string, { owned: number; cost: number }>),
    milestones: {},
    clickMultiplier: 1,
    autoClickRate: 0,
    currentBackground: 'default',
    currentClickImage: 'https://media1.tenor.com/m/pV74fmh_NLgAAAAd/louie-rat-spinning-rat.gif'
  };

  autoClickInterval?: NodeJS.Timeout;

  async onConnect(conn: Party.Connection, _ctx: Party.ConnectionContext) {
    conn.send(JSON.stringify({ type: 'game-state', state: this.gameState }));
  }

  async onMessage(message: string, sender: Party.Connection) {
    const data = JSON.parse(message) as Message;

    let currentUserId: string;
    let currentUserName: string;
    let isAuthenticated = false;

    // Verify the Clerk token for authenticated messages
    if ('token' in data && data.token) {
      try {
        const sessionClaims = await verifyToken(data.token, {
          jwtKey: this.party.env.CLERK_JWT_KEY as string,
        });
        currentUserId = sessionClaims.sub;
        isAuthenticated = true;

        // Ensure the userId from the client matches the authenticated userId
        if (currentUserId !== data.userId) {
          console.warn(`User ID mismatch: Client sent ${data.userId}, but token is for ${currentUserId}`);
          sender.close(); // Close connection for potential tampering
          return;
        }
        currentUserName = data.userName; // Use name from client, which comes from Clerk
      } catch (error) {
        console.error('Clerk token verification failed:', error);
        sender.close(); // Close connection if token is invalid
        return;
      }
    } else {
      // Handle unauthenticated messages (guest users)
      currentUserId = data.userId;
      currentUserName = data.userName;
      isAuthenticated = false;

      // Only allow 'user-join' for unauthenticated users.
      // Other actions like 'click', 'purchase-upgrade', 'apply-multiplier-bonus' are ignored.
      if (data.type !== 'user-join') {
        console.warn(`Received unauthenticated message of type '${data.type}' from ${currentUserId}. Action ignored.`);
        this.broadcast(); // Still broadcast game state to keep guests updated
        return;
      }
    }

    // Only update user info in gameState for authenticated users
    if (isAuthenticated) {
      if (!this.gameState.users[currentUserId]) {
        this.gameState.users[currentUserId] = {
          name: currentUserName,
          clicks: 0,
          lastSeen: Date.now(),
          bonusMultiplier: 1 // Initialize bonus multiplier
        };
      }
      this.gameState.users[currentUserId].lastSeen = Date.now();
      this.gameState.users[currentUserId].name = currentUserName; // Update name in case it changed
      // Ensure bonusMultiplier is initialized if it somehow wasn't
      if (this.gameState.users[currentUserId].bonusMultiplier === undefined) {
        this.gameState.users[currentUserId].bonusMultiplier = 1;
      }
    }

    switch (data.type) {
      case 'user-join':
        // For authenticated users, user join is handled by the user state update above.
        // For guest users, no action is needed here as they are not added to gameState.users.
        break;
      case 'click':
        if (isAuthenticated) {
          this.handleClick(data, currentUserId);
        } else {
          console.warn(`Unauthenticated click from ${currentUserId} ignored.`);
        }
        break;
      case 'purchase-upgrade':
        if (isAuthenticated) {
          this.handlePurchaseUpgrade(data, currentUserId);
        } else {
          console.warn(`Unauthenticated purchase-upgrade from ${currentUserId} ignored.`);
        }
        break;
      case 'apply-multiplier-bonus':
        if (isAuthenticated) {
          this.handleApplyMultiplierBonus(data, currentUserId);
        } else {
          console.warn(`Unauthenticated apply-multiplier-bonus from ${currentUserId} ignored.`);
        }
        break;
    }

    this.broadcast();
  }

  // handleUserJoin is now fully integrated into onMessage and can be removed or simplified.
  // Removing it as its logic is now directly in onMessage.
  // handleUserJoin(data: UserJoinMessage) {
  //   // This function is now mostly handled by the onMessage token verification logic
  //   // It ensures the user exists in gameState.users and updates lastSeen/name.
  //   // No explicit action needed here beyond what onMessage does.
  // }

  handleClick(data: ClickMessage, authenticatedUserId: string) {
    // Apply global click multiplier and user-specific bonus multiplier
    const userBonusMultiplier = this.gameState.users[authenticatedUserId]?.bonusMultiplier || 1;
    const clickValue = this.gameState.clickMultiplier * userBonusMultiplier;

    this.gameState.totalClicks += clickValue;

    // Ensure user exists (should be handled by onMessage's token verification)
    if (!this.gameState.users[authenticatedUserId]) {
      this.gameState.users[authenticatedUserId] = {
        name: data.userName, // Use the name from the client, which comes from Clerk
        clicks: 0,
        lastSeen: Date.now(),
        bonusMultiplier: 1 // Initialize if user was just created
      };
    }

    this.gameState.users[authenticatedUserId].clicks += clickValue;
    this.gameState.users[authenticatedUserId].lastSeen = Date.now();

    this.checkMilestones();
  }

  handleApplyMultiplierBonus(data: ApplyMultiplierBonusMessage, authenticatedUserId: string) {
    if (!this.gameState.users[authenticatedUserId]) {
      console.warn(`User ${authenticatedUserId} not found for multiplier bonus application.`);
      return;
    }
    // Apply the compounding multiplier bonus
    this.gameState.users[authenticatedUserId].bonusMultiplier *= data.multiplierBonus;
    console.log(`User ${authenticatedUserId} bonus multiplier updated to: ${this.gameState.users[authenticatedUserId].bonusMultiplier}`);
  }

  handlePurchaseUpgrade(data: PurchaseUpgradeMessage, authenticatedUserId: string) {
    const upgrade = UPGRADES[data.upgradeId as keyof typeof UPGRADES];
    const currentUpgrade = this.gameState.upgrades[data.upgradeId];

    if (!upgrade || !currentUpgrade) return;

    // Check affordability against totalClicks
    if (this.gameState.totalClicks >= currentUpgrade.cost) {
      this.gameState.totalClicks -= currentUpgrade.cost; // Deduct from totalClicks
      currentUpgrade.owned += 1;
      currentUpgrade.cost = Math.floor(upgrade.baseCost * Math.pow(upgrade.multiplier, currentUpgrade.owned));

      this.updateGameMultipliers();
    }
  }

  updateGameMultipliers() {
    this.gameState.clickMultiplier = 1;
    this.gameState.autoClickRate = 0;

    Object.entries(this.gameState.upgrades).forEach(([upgradeId, upgrade]) => {
      const config = UPGRADES[upgradeId as keyof typeof UPGRADES];
      if ('clickBonus' in config && config.clickBonus) {
        this.gameState.clickMultiplier += config.clickBonus * upgrade.owned;
      }
      if ('autoClickRate' in config && config.autoClickRate) {
        this.gameState.autoClickRate += config.autoClickRate * upgrade.owned;
      }
      // Note: clickMultiplierBonus from upgrades.ts is handled client-side for spawning frequency
      // and applied per-click on the server via handleApplyMultiplierBonus
    });

    this.setupAutoClicker();
  }

  setupAutoClicker() {
    if (this.autoClickInterval) {
      clearInterval(this.autoClickInterval);
    }

    if (this.gameState.autoClickRate > 0) {
      this.autoClickInterval = setInterval(() => {
        this.gameState.totalClicks += this.gameState.autoClickRate;
        this.checkMilestones();
        this.broadcast();
      }, 1000);
    }
  }

  checkMilestones() {
    MILESTONES.forEach(milestone => {
      if (this.gameState.totalClicks >= milestone.threshold && !this.gameState.milestones[milestone.id]) {
        this.gameState.milestones[milestone.id] = true;
        this.gameState.currentBackground = milestone.background;
        this.gameState.currentClickImage = milestone.image;
      }
    });
  }

  broadcast() {
    this.party.broadcast(JSON.stringify({ type: 'game-state', state: this.gameState }));
  }
}

GameServer satisfies Party.Worker;
