import { EventEmitter } from 'events';
import crypto from 'crypto';

export interface ReferralCode {
  code: string;
  referrerId: string;
  referrerName: string;
  createdAt: Date;
  expiresAt?: Date;
  active: boolean;
  uses: number;
  maxUses?: number;
  bonusAmount: number;
}

export interface ReferralRecord {
  id: string;
  referrerId: string;
  referredId: string;
  referredName: string;
  code: string;
  bonusAwarded: number;
  status: 'pending' | 'completed' | 'rejected';
  createdAt: Date;
  completedAt?: Date;
}

export interface PlayerReferralStats {
  playerId: string;
  totalReferrals: number;
  completedReferrals: number;
  totalBonusEarned: number;
  referralTree: Map<string, string[]>; // playerId -> referred playerIds
}

/**
 * Referral Bonus System
 * Manages referral codes, tracking, and bonus distribution
 * 1 SC per successful referral
 */
export class ReferralBonusSystem extends EventEmitter {
  private referralCodes: Map<string, ReferralCode> = new Map();
  private referralRecords: Map<string, ReferralRecord> = new Map();
  private playerStats: Map<string, PlayerReferralStats> = new Map();
  private codeToReferrer: Map<string, string> = new Map(); // code -> referrerId
  private referralHistory: ReferralRecord[] = [];

  private readonly BONUS_AMOUNT = 1; // 1 SC per referral
  private readonly CODE_LENGTH = 8;

  constructor() {
    super();
    console.log('[ReferralBonus] System initialized (1 SC per referral)');
  }

  /**
   * Generate referral code
   */
  generateReferralCode(referrerId: string, referrerName: string, maxUses?: number): ReferralCode {
    let code: string;
    let attempts = 0;

    // Generate unique code
    do {
      code = this.generateCode();
      attempts++;
    } while (this.referralCodes.has(code) && attempts < 10);

    if (attempts >= 10) {
      throw new Error('Failed to generate unique referral code');
    }

    const referralCode: ReferralCode = {
      code,
      referrerId,
      referrerName,
      createdAt: new Date(),
      expiresAt: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000), // 1 year
      active: true,
      uses: 0,
      maxUses,
      bonusAmount: this.BONUS_AMOUNT,
    };

    this.referralCodes.set(code, referralCode);
    this.codeToReferrer.set(code, referrerId);

    // Initialize stats if needed
    if (!this.playerStats.has(referrerId)) {
      this.playerStats.set(referrerId, {
        playerId: referrerId,
        totalReferrals: 0,
        completedReferrals: 0,
        totalBonusEarned: 0,
        referralTree: new Map(),
      });
    }

    this.emit('codeGenerated', { code, referrerId, referrerName });
    console.log(`[ReferralBonus] Code generated: ${code} for ${referrerName}`);

    return referralCode;
  }

  /**
   * Generate unique code
   */
  private generateCode(): string {
    return crypto.randomBytes(this.CODE_LENGTH / 2).toString('hex').toUpperCase();
  }

  /**
   * Validate referral code
   */
  validateCode(code: string): { valid: boolean; referrerId?: string; reason?: string } {
    const referralCode = this.referralCodes.get(code);

    if (!referralCode) {
      return { valid: false, reason: 'Code not found' };
    }

    if (!referralCode.active) {
      return { valid: false, reason: 'Code is inactive' };
    }

    if (referralCode.expiresAt && new Date() > referralCode.expiresAt) {
      return { valid: false, reason: 'Code has expired' };
    }

    if (referralCode.maxUses && referralCode.uses >= referralCode.maxUses) {
      return { valid: false, reason: 'Code usage limit reached' };
    }

    return { valid: true, referrerId: referralCode.referrerId };
  }

  /**
   * Apply referral code
   */
  applyReferralCode(code: string, referredId: string, referredName: string): ReferralRecord | null {
    const validation = this.validateCode(code);
    if (!validation.valid) {
      console.log(`[ReferralBonus] Invalid code: ${code} - ${validation.reason}`);
      return null;
    }

    const referrerId = validation.referrerId!;

    // Prevent self-referral
    if (referrerId === referredId) {
      console.log(`[ReferralBonus] Self-referral attempted`);
      return null;
    }

    // Check if already referred
    const existingReferral = Array.from(this.referralRecords.values()).find(
      (r) => r.referredId === referredId && r.status === 'completed'
    );

    if (existingReferral) {
      console.log(`[ReferralBonus] Player already referred`);
      return null;
    }

    // Create referral record
    const referralRecord: ReferralRecord = {
      id: `ref-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
      referrerId,
      referredId,
      referredName,
      code,
      bonusAwarded: this.BONUS_AMOUNT,
      status: 'pending',
      createdAt: new Date(),
    };

    this.referralRecords.set(referralRecord.id, referralRecord);

    // Update code usage
    const referralCode = this.referralCodes.get(code)!;
    referralCode.uses++;

    // Update stats
    const stats = this.playerStats.get(referrerId)!;
    stats.totalReferrals++;

    // Add to referral tree
    if (!stats.referralTree.has(referrerId)) {
      stats.referralTree.set(referrerId, []);
    }
    stats.referralTree.get(referrerId)!.push(referredId);

    this.emit('referralApplied', { referralRecord, code });
    console.log(`[ReferralBonus] Referral applied: ${referredId} referred by ${referrerId}`);

    return referralRecord;
  }

  /**
   * Complete referral (award bonus)
   */
  completeReferral(referralId: string): { success: boolean; bonusAmount?: number; reason?: string } {
    const referral = this.referralRecords.get(referralId);
    if (!referral) {
      return { success: false, reason: 'Referral not found' };
    }

    if (referral.status !== 'pending') {
      return { success: false, reason: 'Referral already processed' };
    }

    referral.status = 'completed';
    referral.completedAt = new Date();

    // Update stats
    const stats = this.playerStats.get(referral.referrerId);
    if (stats) {
      stats.completedReferrals++;
      stats.totalBonusEarned += referral.bonusAwarded;
    }

    // Add to history
    this.referralHistory.push({ ...referral });

    // Keep only last 10000 records
    if (this.referralHistory.length > 10000) {
      this.referralHistory.shift();
    }

    this.emit('referralCompleted', { referralId, bonusAmount: referral.bonusAwarded });
    console.log(`[ReferralBonus] Referral completed: ${referralId} - ${referral.bonusAwarded} SC awarded`);

    return { success: true, bonusAmount: referral.bonusAwarded };
  }

  /**
   * Get player referral stats
   */
  getPlayerStats(playerId: string): PlayerReferralStats | null {
    return this.playerStats.get(playerId) || null;
  }

  /**
   * Get referral code
   */
  getReferralCode(code: string): ReferralCode | null {
    return this.referralCodes.get(code) || null;
  }

  /**
   * Get player referral codes
   */
  getPlayerReferralCodes(playerId: string): ReferralCode[] {
    return Array.from(this.referralCodes.values()).filter((c) => c.referrerId === playerId);
  }

  /**
   * Get referral tree
   */
  getReferralTree(playerId: string): Map<string, string[]> {
    const stats = this.playerStats.get(playerId);
    return stats?.referralTree || new Map();
  }

  /**
   * Get referral record
   */
  getReferralRecord(referralId: string): ReferralRecord | null {
    return this.referralRecords.get(referralId) || null;
  }

  /**
   * Get player referrals
   */
  getPlayerReferrals(playerId: string): ReferralRecord[] {
    return Array.from(this.referralRecords.values()).filter((r) => r.referrerId === playerId);
  }

  /**
   * Get completed referrals for player
   */
  getCompletedReferrals(playerId: string): ReferralRecord[] {
    return this.getPlayerReferrals(playerId).filter((r) => r.status === 'completed');
  }

  /**
   * Deactivate code
   */
  deactivateCode(code: string): boolean {
    const referralCode = this.referralCodes.get(code);
    if (!referralCode) return false;

    referralCode.active = false;

    this.emit('codeDeactivated', { code });
    console.log(`[ReferralBonus] Code deactivated: ${code}`);

    return true;
  }

  /**
   * Get referral statistics
   */
  getReferralStats() {
    const totalCodes = this.referralCodes.size;
    const activeCodes = Array.from(this.referralCodes.values()).filter((c) => c.active).length;
    const totalReferrals = this.referralRecords.size;
    const completedReferrals = Array.from(this.referralRecords.values()).filter((r) => r.status === 'completed').length;
    const totalBonusDistributed = Array.from(this.referralRecords.values())
      .filter((r) => r.status === 'completed')
      .reduce((sum, r) => sum + r.bonusAwarded, 0);

    return {
      totalCodes,
      activeCodes,
      totalReferrals,
      completedReferrals,
      totalBonusDistributed,
      bonusPerReferral: this.BONUS_AMOUNT,
    };
  }

  /**
   * Get top referrers
   */
  getTopReferrers(limit: number = 10): Array<{ playerId: string; completedReferrals: number; totalBonusEarned: number }> {
    return Array.from(this.playerStats.values())
      .filter((s) => s.completedReferrals > 0)
      .map((s) => ({
        playerId: s.playerId,
        completedReferrals: s.completedReferrals,
        totalBonusEarned: s.totalBonusEarned,
      }))
      .sort((a, b) => b.totalBonusEarned - a.totalBonusEarned)
      .slice(0, limit);
  }

  /**
   * Get recent referrals
   */
  getRecentReferrals(limit: number = 50): ReferralRecord[] {
    return this.referralHistory.slice(-limit).reverse();
  }

  /**
   * Clear all data
   */
  clear(): void {
    this.referralCodes.clear();
    this.referralRecords.clear();
    this.playerStats.clear();
    this.codeToReferrer.clear();
    this.referralHistory = [];
    console.log('[ReferralBonus] System cleared');
  }
}

export const referralBonusSystem = new ReferralBonusSystem();
