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

export interface Upgrade {
  id: string;
  name: string;
  description: string;
  baseCost: number;
  multiplier: number;
  clickBonus?: number;
  autoClickRate?: number;
  clickMultiplierBonus?: number;
  icon: string;
  mascotTiers?: MascotTier[];
  oneTime?: boolean;
  newsTitles?: string[];
}

export interface MascotTier {
  level: number;
  imageSrc: string;
  multiplier: number;
  rarity: number;
}

interface GameState {
  totalClicks: number;
  users: Record<string, { name: string; clicks: number; lastSeen: number; bonusMultiplier: number }>;
  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 AdminBroadcastMessage extends AuthenticatedMessage {
  type: 'admin-broadcast';
  message: string;
  targetUserId?: string; // Optional: if broadcasting to a specific user
}

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

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

const UPGRADES: Upgrade[] = [
  {
    id: 'clickMultiplier',
    name: '🖱️ Mega Click',
    description: '+1 click power per purchase',
    baseCost: 10,
    multiplier: 1.5,
    clickBonus: 1,
    icon: '🖱️'
  },
  {
    id: 'autoClicker',
    name: '🤖 Auto Clicker',
    description: '+1 click per second',
    baseCost: 50,
    multiplier: 2,
    autoClickRate: 1,
    icon: '🤖'
  },
  {
    id: 'megaBonus',
    name: '💎 Mega Bonus',
    description: '+5 click power per purchase',
    baseCost: 200,
    multiplier: 2.5,
    clickBonus: 5,
    icon: '💎'
  },
  {
    id: 'hyperClicker',
    name: '⚡ Hyper Clicker',
    description: '+10 auto clicks per second',
    baseCost: 1000,
    multiplier: 3,
    autoClickRate: 10,
    icon: '⚡'
  },
  {
    id: 'quantumClicker',
    name: '🌟 Quantum Clicker',
    description: '+50 click power per purchase',
    baseCost: 5000,
    multiplier: 4,
    clickBonus: 50,
    icon: '🌟'
  },
  {
    id: 'friendBoost',
    name: '🤝 Friend Boost',
    description: 'Spawns various clickable friends for a compounding click boost',
    baseCost: 2000,
    multiplier: 3,
    icon: '🤝',
    mascotTiers: [
      {
        level: 0,
        imageSrc: '/src/assets/bozo.png',
        multiplier: 1.02,
        rarity: 1.0,
      },
      {
        level: 1,
        imageSrc: '/src/assets/shoominion.png',
        multiplier: 1.03,
        rarity: 0.8,
      },
      {
        level: 5,
        imageSrc: '/src/assets/codebug.gif',
        multiplier: 1.05,
        rarity: 0.6,
      },
      {
        level: 10,
        imageSrc: '/src/assets/lalan.gif',
        multiplier: 1.07,
        rarity: 0.4,
      },
      {
        level: 15,
        imageSrc: '/src/assets/neuro-neurosama.gif',
        multiplier: 1.10,
        rarity: 0.2,
      },
      {
        level: 20,
        imageSrc: '/src/assets/evil-neurosama.gif',
        multiplier: 1.15,
        rarity: 0.1,
      },
    ],
  },
  {
    id: 'news',
    name: '📰 Bozo News Network',
    description: 'Unlock the latest (fake) news headlines!',
    baseCost: 50000, // A higher cost for a unique, one-time unlock
    multiplier: 1, // No direct click/auto-click bonus
    icon: '📰',
    oneTime: true,
    newsTitles: [
      `Its Bo's birthday!`,
      `Haru Urara looses another race, forced to eat pickle filled burger.`,
      `Cupid crashes out once again, completely expected.`,
      `Bo has been spotted in the wild, please do not approach.`,
      `Bozo Clicker is now the number game in the world!`,
      `Bo states that he did win the hidden gem vtuber award, it's just that he just didn't feel like it so Reya was really kind to step up.`,
      `Reya has been spotted in the wild, please do not approach.`,
      `What? Stop reading? I wrote this when you were playing roblox with cupid`,
      `It's 1pm in the night and my mom is calling me to sleep, I don't want to sleep I'm making this stupid game`,
      `FU BO`,
      `When are you watching paint dry again?`,
      `Insert Girls Kissing`,
      `We luub bo`,
      `MOOD DOWN`,
      `All this clicking in this game wont give you your money back from those horse races`,
      `UMAMUSUME PRETTY DERBY UPDATE : NEW SSSR IS ANNOUNCED - ITS [REDACTED]`,
      `LEts' get maried bo`,
      `My AI autocomplete on my code editor is shipping you with Reya for some reason. It literally completed Reya in this sentence`,
      `DUDE`,
      `There's a pipebomb in your DMs`,
      `om`,
      `You are the Glue to my life like how Tokai Teio is glue(stick) to a child's art project`,
    ]
  }
];

const MILESTONES = [
  { threshold: 1000, id: 'first-thousand', background: 'rainbow', image: 'https://media1.tenor.com/m/x8v1oNUOmg4AAAAd/spinning-rat-rat.gif' },
  { threshold: 5000, id: 'five-thousand', background: 'matrix', image: 'https://media1.tenor.com/m/pV74fmh_NLgAAAAd/louie-rat-spinning-rat.gif' },
  { threshold: 10000, id: 'ten-thousand', background: 'cyberpunk', image: 'https://media1.tenor.com/m/YsWlbVbRWFQAAAAd/rat-spinning.gif' },
  { threshold: 50000, id: 'epic-milestone', background: 'space', image: 'https://media1.tenor.com/m/x8v1oNUOmg4AAAAd/spinning-rat-rat.gif' },
  { threshold: 100000, id: 'legendary', background: 'glitch', image: 'https://media1.tenor.com/m/pV74fmh_NLgAAAAd/louie-rat-spinning-rat.gif' },
  { threshold: 500000, id: 'ultimate', background: 'ultimate', image: 'https://media1.tenor.com/m/YsWlbVbRWFQAAAAd/rat-spinning.gif' },
  { threshold: 1000000, id: 'god-tier', background: 'god-tier', image: 'https://media1.tenor.com/m/x8v1oNUOmg4AAAAd/spinning-rat-rat.gif' }
];

export default class GameServer implements Party.Server {
  clerkClient: ReturnType<typeof createClerkClient>;
  private userConnections: Map<string, Party.Connection> = new Map(); // Map userId to connection

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

  gameState: GameState = {
    totalClicks: 0,
    users: {},
    upgrades: UPGRADES.reduce((acc, upgrade) => {
      acc[upgrade.id] = { owned: 0, cost: upgrade.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 }));
  }

  onClose(conn: Party.Connection) {
    // Remove the connection from the map when it closes
    for (const [userId, connection] of this.userConnections.entries()) {
      if (connection === conn) {
        this.userConnections.delete(userId);
        break;
      }
    }
  }

  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;
      }
      // Store the connection for targeted broadcasts
      this.userConnections.set(currentUserId, sender);
    }

    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;
      case 'admin-broadcast':
        if (isAuthenticated && currentUserId === this.party.env.CLERK_ADMIN_USERID) {
          this.handleAdminBroadcast(data);
        } else {
          console.warn(`Unauthorized admin broadcast attempt from ${currentUserId}.`);
        }
        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();
  }

  handleAdminBroadcast(data: AdminBroadcastMessage) {
    const broadcastPayload = {
      type: 'admin-message',
      message: data.message,
      sender: 'Admin'
    };

    if (data.targetUserId) {
      // Send to a specific user using the stored connection
      const targetConn = this.userConnections.get(data.targetUserId);
      if (targetConn) {
        targetConn.send(JSON.stringify(broadcastPayload));
        console.log(`Admin broadcasted to user ${data.targetUserId}: ${data.message}`);
      } else {
        console.warn(`Target user ${data.targetUserId} not found or not connected.`);
      }
    } else {
      // Broadcast to all connections
      this.party.broadcast(JSON.stringify(broadcastPayload));
      console.log(`Admin broadcasted to all users: ${data.message}`);
    }
  }

  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 upgradeConfig = UPGRADES.find(u => u.id === data.upgradeId);
    const currentUpgradeState = this.gameState.upgrades[data.upgradeId];

    if (!upgradeConfig || !currentUpgradeState) return;

    // Prevent purchasing one-time upgrades if already owned
    if (upgradeConfig.oneTime && currentUpgradeState.owned > 0) {
      console.warn(`Attempted to re-purchase one-time upgrade: ${data.upgradeId}`);
      return;
    }

    // Check affordability against totalClicks
    if (this.gameState.totalClicks >= currentUpgradeState.cost) {
      this.gameState.totalClicks -= currentUpgradeState.cost; // Deduct from totalClicks
      currentUpgradeState.owned += 1;

      // For one-time upgrades, cost doesn't change after first purchase
      if (!upgradeConfig.oneTime) {
        currentUpgradeState.cost = Math.floor(upgradeConfig.baseCost * Math.pow(upgradeConfig.multiplier, currentUpgradeState.owned));
      }

      this.updateGameMultipliers();
    }
  }

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

    Object.entries(this.gameState.upgrades).forEach(([upgradeId, upgradeState]) => {
      const config = UPGRADES.find(u => u.id === upgradeId);
      if (!config) return; // Should not happen if UPGRADES is consistent

      if (config.clickBonus) {
        this.gameState.clickMultiplier += config.clickBonus * upgradeState.owned;
      }
      if (config.autoClickRate) {
        this.gameState.autoClickRate += config.autoClickRate * upgradeState.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;
