import { InjectRepository } from '@nestjs/typeorm';
import { Streak } from './entities/streak.entity';
import { Brackets, Not, Repository } from 'typeorm';
import { UserStreak } from './entities/user-streak.entity';
import { getCompleteUrl } from 'src/common/helper';

export class StreakRepository {
  constructor(
    @InjectRepository(Streak)
    private readonly streakRepository: Repository<Streak>,
    @InjectRepository(UserStreak)
    private readonly userStreakRepository: Repository<UserStreak>,
  ) {}

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

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

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

    return response;
  }

  async findAllStreak(
    take: number,
    skip: number,
    key?: string,
    order?: any,
    search?: string,
    recent?: boolean,
    province?: string,
  ) {
    const query = this.userStreakRepository
      .createQueryBuilder('userStreak')
      .leftJoinAndSelect('userStreak.app_user', 'user')
      .leftJoinAndSelect('userStreak.streak', 'streak')
      .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('userStreak.created_at', 'DESC').limit(10);
    } else {
      query.take(take).skip(skip);
    }

    if (key) {
      const validSortFields = ['created_at', 'streak_count'];
      if (validSortFields.includes(key)) {
        const sortOrder = order === 'ASC' ? 'ASC' : 'DESC';

        query.orderBy(`userStreak.${key}`, sortOrder);
      }
    } else {
      query.orderBy(`userStreak.created_at`, 'DESC');
    }

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

    streaks.forEach((streak) => {
      if (streak.app_user.profile_picture) {
        streak.app_user.profile_picture = getCompleteUrl(
          streak.app_user.profile_picture,
        );
      }
    });
    return {
      count,
      streaks,
    };
  }

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

  async findAllUserStreak(
    take?: number,
    skip?: number,
    userId?: string,
    date?: string,
  ) {
    const [streaks, count] = await this.streakRepository
      .createQueryBuilder('streak')
      .leftJoinAndSelect(
        'streak.user_streaks',
        'user_streak',
        'user_streak.app_user_id = :userId',
        { userId },
      )
      .withDeleted()
      .where(
        'streak.deleted_at IS NULL OR (streak.deleted_at IS NOT NULL AND user_streak.app_user_id = :userId)',
        { userId },
      )
      .orderBy('CAST(streak.streak_count AS int)', 'ASC')
      .take(take)
      .skip(skip)
      .getManyAndCount();

    const result = streaks.map((streak) => {
      const userStreak = streak?.user_streaks?.[0] || null;

      return {
        id: streak?.id,
        streak_count: streak?.streak_count,
        status: streak?.status,
        created_at: userStreak ? userStreak.created_at : null,
        is_achieved: streak?.user_streaks.length > 0,
      };
    });

    const response = {
      streak_count: count,
      streak_data: result,
    };

    return response;
  }

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

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

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

    return data.length > 0;
  }
}
