import { BaseRepository } from '../../../core/interfaces/IRepository';
import { IRewardRepository } from './interfaces/IRewardRepository';
import { 
  Reward, 
  RewardType, 
  RewardStatus, 
  RewardSource,
  LearningHoursReward,
  PerformanceBonusReward,
  CompletionReward,
  ConsistencyReward
} from '../types/reward.types';

export class MockRewardRepository extends BaseRepository<Reward> implements IRewardRepository {
  private rewards: Reward[] = [];

  constructor() {
    super();
    this.initializeMockData();
  }

  private initializeMockData(): void {
    const now = new Date();
    const lastWeek = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
    const lastMonth = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);

    this.rewards = [
      // Learning Hours Rewards
      {
        id: 'reward-1',
        userId: 'demo-user-123',
        type: RewardType.LEARNING_HOURS,
        source: RewardSource.LEARNING_TIME,
        status: RewardStatus.PAID,
        amount: 150000,
        currency: 'IDR',
        description: 'Reward for 3 hours of learning',
        hoursLogged: 3,
        ratePerHour: 50000,
        sessionIds: ['session-1', 'session-2', 'session-3'],
        metadata: {
          hoursCompleted: 3,
          courseId: 'course-1',
          courseName: 'HACCP Fundamentals'
        },
        approvedAt: new Date(lastWeek.getTime() + 24 * 60 * 60 * 1000),
        paidAt: new Date(lastWeek.getTime() + 48 * 60 * 60 * 1000),
        createdAt: lastWeek,
        updatedAt: new Date(lastWeek.getTime() + 48 * 60 * 60 * 1000)
      },
      {
        id: 'reward-2',
        userId: 'demo-user-123',
        type: RewardType.PERFORMANCE_BONUS,
        source: RewardSource.EXAM_PASS,
        status: RewardStatus.APPROVED,
        amount: 200000,
        currency: 'IDR',
        description: 'Performance bonus for excellent exam score',
        performanceScore: 95,
        bonusMultiplier: 1.5,
        achievementId: 'exam-1',
        metadata: {
          examScore: 95,
          examId: 'exam-1',
          examName: 'HACCP Final Assessment',
          bonusMultiplier: 1.5
        },
        approvedAt: new Date(now.getTime() - 2 * 24 * 60 * 60 * 1000),
        createdAt: new Date(now.getTime() - 3 * 24 * 60 * 60 * 1000),
        updatedAt: new Date(now.getTime() - 2 * 24 * 60 * 60 * 1000)
      },
      {
        id: 'reward-3',
        userId: 'demo-user-123',
        type: RewardType.COMPLETION_REWARD,
        source: RewardSource.COURSE_COMPLETION,
        status: RewardStatus.PENDING,
        amount: 300000,
        currency: 'IDR',
        description: 'Course completion reward',
        completedItemId: 'course-2',
        completedItemType: 'course' as const,
        completionScore: 88,
        metadata: {
          courseId: 'course-2',
          courseName: 'Food Safety Management',
          completionDate: new Date(),
          certificateIssued: true
        },
        createdAt: now,
        updatedAt: now
      },
      {
        id: 'reward-4',
        userId: 'demo-user-123',
        type: RewardType.CONSISTENCY_REWARD,
        source: RewardSource.STREAK_ACHIEVEMENT,
        status: RewardStatus.PAID,
        amount: 100000,
        currency: 'IDR',
        description: '7-day learning streak bonus',
        streakDays: 7,
        streakType: 'daily_learning' as const,
        bonusMultiplier: 1.2,
        metadata: {
          streakDays: 7,
          streakType: 'daily_learning',
          bonusMultiplier: 1.2
        },
        approvedAt: new Date(lastMonth.getTime() + 24 * 60 * 60 * 1000),
        paidAt: new Date(lastMonth.getTime() + 48 * 60 * 60 * 1000),
        createdAt: lastMonth,
        updatedAt: new Date(lastMonth.getTime() + 48 * 60 * 60 * 1000)
      }
    ];
  }

  async findAll(): Promise<Reward[]> {
    return [...this.rewards];
  }

  async findById(id: string): Promise<Reward | null> {
    return this.rewards.find(r => r.id === id) || null;
  }

  async create(data: Omit<Reward, 'id' | 'createdAt' | 'updatedAt'>): Promise<Reward> {
    const reward = {
      ...data,
      id: `reward-${Date.now()}`,
      createdAt: new Date(),
      updatedAt: new Date()
    } as Reward;
    this.rewards.push(reward);
    return reward;
  }

  async update(id: string, data: Partial<Reward>): Promise<Reward> {
    const index = this.rewards.findIndex(r => r.id === id);
    if (index === -1) {
      throw new Error(`Reward with id ${id} not found`);
    }

    this.rewards[index] = {
      ...this.rewards[index],
      ...data,
      updatedAt: new Date()
    } as Reward;
    return this.rewards[index];
  }

  async delete(id: string): Promise<void> {
    const index = this.rewards.findIndex(r => r.id === id);
    if (index === -1) {
      throw new Error(`Reward with id ${id} not found`);
    }

    this.rewards.splice(index, 1);
  }

  async findByUserId(userId: string): Promise<Reward[]> {
    return this.rewards.filter(r => r.userId === userId);
  }

  async findByUserIdAndType(userId: string, type: RewardType): Promise<Reward[]> {
    return this.rewards.filter(r => r.userId === userId && r.type === type);
  }

  async findByUserIdAndStatus(userId: string, status: RewardStatus): Promise<Reward[]> {
    return this.rewards.filter(r => r.userId === userId && r.status === status);
  }

  async findByUserIdAndDateRange(userId: string, startDate: Date, endDate: Date): Promise<Reward[]> {
    return this.rewards.filter(r => 
      r.userId === userId && 
      r.createdAt >= startDate && 
      r.createdAt <= endDate
    );
  }

  async getUserRewardStats(userId: string): Promise<{
    totalEarned: number;
    totalPending: number;
    totalPaid: number;
    rewardsByType: Record<RewardType, number>;
    rewardsByMonth: Record<string, number>;
  }> {
    const userRewards = await this.findByUserId(userId);
    
    const stats = {
      totalEarned: 0,
      totalPending: 0,
      totalPaid: 0,
      rewardsByType: {} as Record<RewardType, number>,
      rewardsByMonth: {} as Record<string, number>
    };

    userRewards.forEach(reward => {
      stats.totalEarned += reward.amount;
      
      if (reward.status === RewardStatus.PENDING) {
        stats.totalPending += reward.amount;
      } else if (reward.status === RewardStatus.PAID) {
        stats.totalPaid += reward.amount;
      }

      // Group by type
      if (!stats.rewardsByType[reward.type]) {
        stats.rewardsByType[reward.type] = 0;
      }
      stats.rewardsByType[reward.type] += reward.amount;

      // Group by month
      const monthKey = reward.createdAt.toISOString().substring(0, 7);
      if (!stats.rewardsByMonth[monthKey]) {
        stats.rewardsByMonth[monthKey] = 0;
      }
      stats.rewardsByMonth[monthKey] += reward.amount;
    });

    return stats;
  }

  async createBatch(rewards: Omit<Reward, 'id' | 'createdAt' | 'updatedAt'>[]): Promise<Reward[]> {
    const createdRewards: Reward[] = [];
    for (const rewardData of rewards) {
      const reward = await this.create(rewardData);
      createdRewards.push(reward);
    }
    return createdRewards;
  }

  async updateStatusBatch(rewardIds: string[], status: RewardStatus): Promise<void> {
    for (const id of rewardIds) {
      await this.update(id, { status });
    }
  }

  async findPendingRewardsForPayroll(startDate: Date, endDate: Date): Promise<Reward[]> {
    return this.rewards.filter(r => 
      r.status === RewardStatus.APPROVED &&
      r.createdAt >= startDate && 
      r.createdAt <= endDate
    );
  }

  async findRewardsByPayrollBatch(batchId: string): Promise<Reward[]> {
    return this.rewards.filter(r => r.payrollBatchId === batchId);
  }

  async findTopEarners(startDate: Date, endDate: Date, limit = 10): Promise<Array<{
    userId: string;
    totalEarned: number;
    rewardCount: number;
  }>> {
    const userStats = new Map<string, { totalEarned: number; rewardCount: number }>();

    this.rewards
      .filter(r => r.createdAt >= startDate && r.createdAt <= endDate)
      .forEach(reward => {
        const current = userStats.get(reward.userId) || { totalEarned: 0, rewardCount: 0 };
        current.totalEarned += reward.amount;
        current.rewardCount += 1;
        userStats.set(reward.userId, current);
      });

    return Array.from(userStats.entries())
      .map(([userId, stats]) => ({ userId, ...stats }))
      .sort((a, b) => b.totalEarned - a.totalEarned)
      .slice(0, limit);
  }

  async findUserConsistencyData(userId: string, days: number): Promise<Array<{
    date: Date;
    hasActivity: boolean;
    rewardEarned: number;
  }>> {
    const endDate = new Date();
    const startDate = new Date(endDate.getTime() - days * 24 * 60 * 60 * 1000);
    
    const userRewards = await this.findByUserIdAndDateRange(userId, startDate, endDate);
    const consistencyData: Array<{ date: Date; hasActivity: boolean; rewardEarned: number }> = [];

    for (let i = 0; i < days; i++) {
      const date = new Date(startDate.getTime() + i * 24 * 60 * 60 * 1000);
      const dayRewards = userRewards.filter(r => 
        r.createdAt.toDateString() === date.toDateString()
      );
      
      consistencyData.push({
        date,
        hasActivity: dayRewards.length > 0,
        rewardEarned: dayRewards.reduce((sum, r) => sum + r.amount, 0)
      });
    }

    return consistencyData;
  }

  async getRewardTrends(startDate: Date, endDate: Date, groupBy: 'day' | 'week' | 'month'): Promise<Array<{
    period: string;
    totalRewards: number;
    totalAmount: number;
    uniqueUsers: number;
  }>> {
    const filteredRewards = this.rewards.filter(r => 
      r.createdAt >= startDate && r.createdAt <= endDate
    );

    const trends = new Map<string, {
      totalRewards: number;
      totalAmount: number;
      users: Set<string>;
    }>();

    filteredRewards.forEach(reward => {
      let periodKey: string;
      
      switch (groupBy) {
        case 'day':
          periodKey = reward.createdAt.toISOString().substring(0, 10);
          break;
        case 'week':
          const weekStart = new Date(reward.createdAt);
          weekStart.setDate(weekStart.getDate() - weekStart.getDay());
          periodKey = weekStart.toISOString().substring(0, 10);
          break;
        case 'month':
          periodKey = reward.createdAt.toISOString().substring(0, 7);
          break;
      }

      const current = trends.get(periodKey) || {
        totalRewards: 0,
        totalAmount: 0,
        users: new Set<string>()
      };

      current.totalRewards += 1;
      current.totalAmount += reward.amount;
      current.users.add(reward.userId);
      trends.set(periodKey, current);
    });

    return Array.from(trends.entries()).map(([period, data]) => ({
      period,
      totalRewards: data.totalRewards,
      totalAmount: data.totalAmount,
      uniqueUsers: data.users.size
    })).sort((a, b) => a.period.localeCompare(b.period));
  }
}