import { InjectRepository } from '@nestjs/typeorm';
import { Achievement } from './entities/achievement.entity';
import { Brackets, Not, Repository } from 'typeorm';
import { UserAchievement } from './entities/user-achievement.entity';
import { StreakRepository } from 'src/streaks/streaks.repository';
import { isEmpty } from 'class-validator';
import { getCompleteUrl } from 'src/common/helper';

export class AchievementRepository {
  constructor(
    @InjectRepository(Achievement)
    private readonly achievementRepository: Repository<Achievement>,
    @InjectRepository(UserAchievement)
    private readonly userAchievementRepository: Repository<UserAchievement>,
    private readonly userStreakRepository: StreakRepository,
  ) {}

  async getAllAchievements(
    take?: number,
    skip?: number,
    key?: string,
    order?: any,
    headers?: any,
  ) {
    const orderBy: { [key: string]: 'ASC' | 'DESC' } = {};

    if (key) {
      if (key === 'created_at' || key === 'name') {
        orderBy[key] = order || 'DESC';
      } else {
        orderBy['created_at'] = 'DESC';
      }
    }

    const [data, count] = await this.achievementRepository.findAndCount({
      where:
        headers && headers['device-type'] === 'mobile' ? { status: 1 } : {},
      skip,
      take,
      order: orderBy,
    });

    const response = {
      count,
      data,
    };

    return response;
  }

  async findById(id: string) {
    const data = await this.achievementRepository.findOne({
      where: {
        id: id,
      },
    });
    return data;
  }

  async findAllUserAchievement(
    take?: number,
    skip?: number,
    userId?: string,
    date?: string,
  ) {
    const [achievements, count] = await this.achievementRepository
      .createQueryBuilder('achievement')
      .leftJoinAndSelect(
        'achievement.user_achievements',
        'user_achievements',
        'user_achievements.user_id = :userId',
        { userId, date },
      )
      .withDeleted()
      .where(
        'achievement.deleted_at IS NULL OR (achievement.deleted_at IS NOT NULL AND user_achievements.user_id = :userId)',
        { userId },
      )
      .orderBy('CAST(achievement.name AS int)', 'ASC')
      .take(take)
      .skip(skip)
      .getManyAndCount();

    const result = achievements.map((achievement) => {
      const userAchievement = achievement?.user_achievements?.[0] || null;

      return {
        id: achievement?.id,
        name: achievement?.name,
        status: achievement?.status,
        created_at: userAchievement ? userAchievement.created_at : null,
        is_achieved: achievement?.user_achievements.length > 0,
      };
    });

    const data = await this.userStreakRepository.findAllUserStreak(
      take,
      skip,
      userId,
    );

    const achievementData = {
      achievement_count: count,
      achievement_data: result,
    };

    const response = {
      achievements: achievementData,
      streaks: data,
    };

    return response;
  }

  async findAllAchievement(
    take: number,
    skip: number,
    key?: string,
    order?: any,
    search?: string,
    recent?: boolean,
    province?: string,
  ) {
    const query = this.userAchievementRepository
      .createQueryBuilder('userAchievement')
      .leftJoinAndSelect('userAchievement.user', 'user')
      .leftJoinAndSelect('userAchievement.achievement', 'achievement')
      .leftJoinAndSelect('user.province', 'province');

    if (search) {
      query.andWhere(
        new Brackets((qb) => {
          qb.where('user.name ILIKE :search', { search: `%${search}%` })
            .orWhere('user.email ILIKE :search', { search: `%${search}%` })
            .orWhere('user.zipcode ILIKE :search', { search: `%${search}%` })
            .orWhere('province.name ILIKE :search', { search: `%${search}%` });
        }),
      );
    }

    if (province) {
      query.andWhere('user.province_id = :province', { province });
    }

    if (recent) {
      query.orderBy('userAchievement.created_at', 'DESC').limit(10);
    } else {
      query.take(take).skip(skip);
    }

    if (key) {
      const validSortFields = ['created_at', 'achievement_goal'];
      if (validSortFields.includes(key)) {
        query.orderBy(`userAchievement.${key}`, order);
      }
    } else {
      query.orderBy(`userAchievement.created_at`, 'DESC');
    }

    const [achievement, count] = await query.getManyAndCount();

    achievement.forEach((achievement) => {
      if (achievement.user.profile_picture) {
        achievement.user.profile_picture = getCompleteUrl(
          achievement.user.profile_picture,
        );
      }
    });
    return {
      count,
      achievement,
    };
  }

  async isUnique(name: string, excludeId?: string) {
    const whereCondition: any = { name };

    if (excludeId) {
      whereCondition.id = Not(excludeId);
    }

    const data = await this.achievementRepository.find({
      where: whereCondition,
    });

    return data.length > 0;
  }
}
