import { v4 as uuidv4 } from 'uuid';
import db from '../config/database';
import { logger } from '../utils/logger';
import MerchantMetricsService, { LeaderboardEntry } from './MerchantMetricsService';

export interface LeaderboardSnapshot {
  id: string;
  leaderboard_type: 'REVENUE' | 'ROI' | 'GROWTH';
  period: 'DAILY' | 'WEEKLY' | 'MONTHLY' | 'ALL_TIME';
  rankings: LeaderboardEntry[];
  total_merchants: number;
  total_revenue: number;
  snapshot_date: Date;
  created_at: Date;
}

export interface LeaderboardConfig {
  refreshIntervalMinutes: number;
  topN: number;
  enableAntiCheat: boolean;
  minOrdersForRanking: number;
}

const DEFAULT_CONFIG: LeaderboardConfig = {
  refreshIntervalMinutes: 10,
  topN: 10,
  enableAntiCheat: true,
  minOrdersForRanking: 5
};

export class LeaderboardService {
  private static instance: LeaderboardService;
  private config: LeaderboardConfig;
  private refreshTimer: NodeJS.Timeout | null = null;

  private constructor(config: Partial<LeaderboardConfig> = {}) {
    this.config = { ...DEFAULT_CONFIG, ...config };
  }

  static getInstance(config?: Partial<LeaderboardConfig>): LeaderboardService {
    if (!LeaderboardService.instance) {
      LeaderboardService.instance = new LeaderboardService(config);
    }
    return LeaderboardService.instance;
  }

  async getLeaderboard(
    type: 'REVENUE' | 'ROI' | 'GROWTH',
    period: 'DAILY' | 'WEEKLY' | 'MONTHLY' | 'ALL_TIME' = 'MONTHLY'
  ): Promise<LeaderboardEntry[]> {
    const cachedSnapshot = await this.getLatestSnapshot(type, period);
    
    if (cachedSnapshot) {
      const snapshotAge = Date.now() - new Date(cachedSnapshot.snapshot_date).getTime();
      const maxAge = this.config.refreshIntervalMinutes * 60 * 1000;
      
      if (snapshotAge < maxAge) {
        logger.info(`[LeaderboardService] Returning cached ${type} leaderboard for ${period}`);
        return cachedSnapshot.rankings;
      }
    }

    return this.refreshLeaderboard(type, period);
  }

  async refreshLeaderboard(
    type: 'REVENUE' | 'ROI' | 'GROWTH',
    period: 'DAILY' | 'WEEKLY' | 'MONTHLY' | 'ALL_TIME'
  ): Promise<LeaderboardEntry[]> {
    logger.info(`[LeaderboardService] Refreshing ${type} leaderboard for ${period}`);

    let rankings = await MerchantMetricsService.getTopMerchants(
      type,
      this.config.topN,
      period
    );

    if (this.config.enableAntiCheat) {
      rankings = await this.applyAntiCheatFilters(rankings);
    }

    rankings = rankings.filter(entry => entry.value > 0);

    const totalRevenue = rankings.reduce((sum, r) => sum + r.value, 0);

    await this.saveSnapshot({
      id: uuidv4(),
      leaderboard_type: type,
      period,
      rankings,
      total_merchants: rankings.length,
      total_revenue: totalRevenue,
      snapshot_date: new Date(),
      created_at: new Date()
    });

    logger.info(`[LeaderboardService] Refreshed ${type} leaderboard with ${rankings.length} entries`);
    return rankings;
  }

  async getLatestSnapshot(
    type: 'REVENUE' | 'ROI' | 'GROWTH',
    period: 'DAILY' | 'WEEKLY' | 'MONTHLY' | 'ALL_TIME'
  ): Promise<LeaderboardSnapshot | null> {
    const snapshot = await db('cf_leaderboard_snapshot')
      .where('leaderboard_type', type)
      .where('period', period)
      .orderBy('snapshot_date', 'desc')
      .first();

    if (!snapshot) return null;

    return {
      ...snapshot,
      rankings: typeof snapshot.rankings === 'string' 
        ? JSON.parse(snapshot.rankings) 
        : snapshot.rankings
    };
  }

  async getSnapshotHistory(
    type: 'REVENUE' | 'ROI' | 'GROWTH',
    period: 'DAILY' | 'WEEKLY' | 'MONTHLY' | 'ALL_TIME',
    limit: number = 30
  ): Promise<LeaderboardSnapshot[]> {
    const snapshots = await db('cf_leaderboard_snapshot')
      .where('leaderboard_type', type)
      .where('period', period)
      .orderBy('snapshot_date', 'desc')
      .limit(limit);

    return snapshots.map(s => ({
      ...s,
      rankings: typeof s.rankings === 'string' ? JSON.parse(s.rankings) : s.rankings
    }));
  }

  async saveSnapshot(snapshot: LeaderboardSnapshot): Promise<void> {
    await db('cf_leaderboard_snapshot').insert({
      id: snapshot.id,
      leaderboard_type: snapshot.leaderboard_type,
      period: snapshot.period,
      rankings: JSON.stringify(snapshot.rankings),
      total_merchants: snapshot.total_merchants,
      total_revenue: snapshot.total_revenue,
      snapshot_date: snapshot.snapshot_date,
      created_at: snapshot.created_at
    });

    logger.info(`[LeaderboardService] Saved snapshot for ${snapshot.leaderboard_type} - ${snapshot.period}`);
  }

  startAutoRefresh(): void {
    if (this.refreshTimer) {
      logger.warn('[LeaderboardService] Auto refresh already running');
      return;
    }

    logger.info(`[LeaderboardService] Starting auto refresh every ${this.config.refreshIntervalMinutes} minutes`);

    this.refreshTimer = setInterval(async () => {
      try {
        await this.refreshAllLeaderboards();
      } catch (error) {
        logger.error('[LeaderboardService] Auto refresh failed:', error);
      }
    }, this.config.refreshIntervalMinutes * 60 * 1000);

    this.refreshAllLeaderboards().catch(err => {
      logger.error('[LeaderboardService] Initial refresh failed:', err);
    });
  }

  stopAutoRefresh(): void {
    if (this.refreshTimer) {
      clearInterval(this.refreshTimer);
      this.refreshTimer = null;
      logger.info('[LeaderboardService] Stopped auto refresh');
    }
  }

  async refreshAllLeaderboards(): Promise<void> {
    const types: Array<'REVENUE' | 'ROI' | 'GROWTH'> = ['REVENUE', 'ROI', 'GROWTH'];
    const periods: Array<'DAILY' | 'WEEKLY' | 'MONTHLY' | 'ALL_TIME'> = ['DAILY', 'WEEKLY', 'MONTHLY', 'ALL_TIME'];

    for (const type of types) {
      for (const period of periods) {
        try {
          await this.refreshLeaderboard(type, period);
        } catch (error) {
          logger.error(`[LeaderboardService] Failed to refresh ${type} - ${period}:`, error);
        }
      }
    }
  }

  async getMerchantRank(
    tenantId: string,
    type: 'REVENUE' | 'ROI' | 'GROWTH',
    period: 'DAILY' | 'WEEKLY' | 'MONTHLY' | 'ALL_TIME' = 'MONTHLY'
  ): Promise<{ rank: number; percentile: number } | null> {
    const leaderboard = await this.getLeaderboard(type, period);
    const entry = leaderboard.find(e => e.tenant_id === tenantId);

    if (!entry) return null;

    const percentile = ((leaderboard.length - entry.rank) / leaderboard.length) * 100;

    return {
      rank: entry.rank,
      percentile: Math.round(percentile * 100) / 100
    };
  }

  async getLeaderboardStats(): Promise<{
    totalSnapshots: number;
    lastRefreshTime: Date | null;
    activeMerchants: number;
  }> {
    const totalSnapshots = await db('cf_leaderboard_snapshot').count('* as count').first();

    const lastSnapshot = await db('cf_leaderboard_snapshot')
      .orderBy('snapshot_date', 'desc')
      .first();

    const activeMerchants = await db('cf_merchant_metrics')
      .where('metrics_date', '>=', new Date(Date.now() - 30 * 24 * 60 * 60 * 1000))
      .countDistinct('tenant_id as count')
      .first();

    return {
      totalSnapshots: Number(totalSnapshots?.count) || 0,
      lastRefreshTime: lastSnapshot ? new Date(lastSnapshot.snapshot_date) : null,
      activeMerchants: Number(activeMerchants?.count) || 0
    };
  }

  private async applyAntiCheatFilters(rankings: LeaderboardEntry[]): Promise<LeaderboardEntry[]> {
    const filtered: LeaderboardEntry[] = [];

    for (const entry of rankings) {
      const orderCount = await this.getMerchantOrderCount(entry.tenant_id);
      
      if (orderCount < this.config.minOrdersForRanking) {
        logger.info(`[LeaderboardService] Filtering out ${entry.tenant_name} - only ${orderCount} orders`);
        continue;
      }

      if (entry.growth_rate && Number(entry.growth_rate) > 10) {
        const isLegitimate = await this.verifyHighGrowth(entry.tenant_id);
        if (!isLegitimate) {
          logger.warn(`[LeaderboardService] Filtering suspicious high growth from ${entry.tenant_name}`);
          continue;
        }
      }

      filtered.push(entry);
    }

    return filtered.map((entry, index) => ({
      ...entry,
      rank: index + 1
    }));
  }

  private async getMerchantOrderCount(tenantId: string): Promise<number> {
    const result = await db('cf_order')
      .where('tenant_id', tenantId)
      .count('* as count')
      .first();

    return Number(result?.count) || 0;
  }

  private async verifyHighGrowth(tenantId: string): Promise<boolean> {
    const recentOrders = await db('cf_order')
      .where('tenant_id', tenantId)
      .where('created_at', '>=', new Date(Date.now() - 7 * 24 * 60 * 60 * 1000))
      .count('* as count')
      .first();

    const olderOrders = await db('cf_order')
      .where('tenant_id', tenantId)
      .whereBetween('created_at', [
        new Date(Date.now() - 14 * 24 * 60 * 60 * 1000),
        new Date(Date.now() - 7 * 24 * 60 * 60 * 1000)
      ])
      .count('* as count')
      .first();

    const recent = Number(recentOrders?.count) || 0;
    const older = Number(olderOrders?.count) || 0;

    if (recent > 0 && older === 0) {
      return false;
    }

    return true;
  }

  updateConfig(newConfig: Partial<LeaderboardConfig>): void {
    this.config = { ...this.config, ...newConfig };
    logger.info('[LeaderboardService] Config updated:', this.config);

    if (this.refreshTimer && newConfig.refreshIntervalMinutes) {
      this.stopAutoRefresh();
      this.startAutoRefresh();
    }
  }

  getConfig(): LeaderboardConfig {
    return { ...this.config };
  }
}

export default LeaderboardService.getInstance();
