import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { ILike, Repository } from 'typeorm';
import { getCompleteUrl, isEmpty } from 'src/common/helper';
import { AppUser } from './entities/app_user.entity';
import { GroupsRepository } from 'src/groups/groups.repository';
import { AppUsersStep } from 'src/app_users_steps/entities/app_users_step.entity';
import * as moment from 'moment';
import { IsNull } from 'typeorm';

interface UserGroupsQueryOptions {
  where: { id: string };
  relations: string[];
}

@Injectable()
export class AppUserRepository {
  constructor(
    @InjectRepository(AppUser)
    private readonly appUserRepository: Repository<AppUser>,
    private readonly groupsRepository: GroupsRepository,
  ) {}

  async findAll(take: number, skip: number, search: string) {
    const whereCondition = search
      ? [
          { name: ILike(`%${search}%`) },
          { email: ILike(`%${search}%`) },
          { province: { name: ILike(`%${search}%`) } },
          { zipcode: ILike(`%${search}%`) },
        ]
      : [];

    const [result, count] = await this.appUserRepository.findAndCount({
      relations: ['user_steps', 'user_groups', 'province'],
      skip,
      take,
      where: whereCondition.length ? whereCondition : undefined,
      order: { created_at: 'DESC' },
      select: [
        'id',
        'name',
        'email',
        'profile_picture',
        'step_goal',
        'social_type',
        'status',
        'user_steps',
        'user_groups',
        'created_at',
        'is_email_verified',
        'whatsapp_no',
        'zipcode',
        'is_step_tracking_enabled',
      ],
    });

    const data = result.map((result) => ({
      ...result,
      profile_picture: getCompleteUrl(result.profile_picture),

      total_steps_count:
        result.user_steps &&
        result.user_steps.reduce(
          (total, step) => total + parseInt(step.steps),
          0,
        ),

      user_groups: result.user_groups.map((group) => ({
        ...group,
        icon: isEmpty(group.icon) ? null : `${process.env.DOMAIN}${group.icon}`,
      })),
    }));

    data.forEach((user) => delete user.user_steps);

    const response = {
      count,
      data,
    };

    return response;
  }

  async findUserByDeviceIdForSkipLogin(deviceId: string) {
    const data = await this.appUserRepository.findOne({
      where: {
        device_id: deviceId,
        social_id: IsNull(),
        email: IsNull(),
      },
      relations: ['user_groups'],
    });

    return data;
  }

  async findAllUsers() {
    const result = await this.appUserRepository.find({
      order: { created_at: 'DESC' },
    });

    return result;
  }

  async findUserGroups(id: string, search?: string) {
    // Define the query options
    const queryOptions: UserGroupsQueryOptions = {
      where: { id },
      relations: ['user_groups', 'user_groups.user_groups'],
    };

    // Use the defined query options in your repository call
    const data = await this.appUserRepository.findOne(queryOptions);

    if (data) {
      let groups = data.user_groups;

      if (search) {
        groups = groups.filter(
          (group) =>
            group.name.toLowerCase().includes(search.toLowerCase()) ||
            group.description.toLowerCase().includes(search.toLowerCase()),
        );
      }

      const currentDate = moment().format('YYYY-MM-DD');
      groups = groups.filter(
        (group) =>
          moment(group.goal_end_date).isAfter(currentDate) &&
          group.status === 1,
      );

      const groupsWithIcons = await Promise.all(
        groups.map(async (group) => {
          const userIdWithJoinDate =
            await this.groupsRepository.getAppUsersWithCreatedAt(group.id);
          let totalSteps: number = 0;

          if (!isEmpty(userIdWithJoinDate)) {
            const stepsPromises = userIdWithJoinDate.map(async (value) => {
              const data = await this.groupsRepository.findUserSteps(
                value.userId,
              );

              if (data) {
                const userJoinDate = moment(value.joinDate).utc(true).valueOf();
                data.forEach((step: AppUsersStep) => {
                  const stepDate = moment(step.created_at).utc(true).valueOf();
                  if (stepDate >= userJoinDate) {
                    totalSteps += parseInt(step.steps);
                  }
                });
              }
            });

            await Promise.all(stepsPromises);
          }

          return {
            ...group,
            total_steps: totalSteps,
            users:
              group.user_groups &&
              group.user_groups.map((user) => ({
                id: user.id,
                name: user.name,
                profile_picture: getCompleteUrl(user.profile_picture),
              })),
            icon: isEmpty(group.icon)
              ? null
              : `${process.env.DOMAIN}${group.icon}`,
          };
        }),
      );

      return groupsWithIcons.map(({ user_groups, ...rest }) => rest);
    }
  }

  async findExploreGroups(id: string, search: string) {
    const user = await this.appUserRepository.findOne({
      where: { id },
      relations: ['user_groups'],
    });

    if (user) {
      const allGroups = await this.groupsRepository.findAllExploreGroup(search);

      // Filter out groups where the user is already linked
      const groupsNotLinked = allGroups.filter(
        (group) =>
          !user.user_groups.some((userGroup) => userGroup.id === group.id),
      );

      const groupsWithIcons = groupsNotLinked.map((group) => ({
        ...group,
        users: (group.user_groups || []).map((user) => ({
          id: user.id,
          name: user.name,
          profile_picture: getCompleteUrl(user.profile_picture),
        })),
        icon: isEmpty(group.icon) ? null : `${process.env.DOMAIN}${group.icon}`,
      }));

      return groupsWithIcons.map(({ user_groups, ...rest }) => rest);
    }
  }

  async findOne(id: string) {
    const data = await this.appUserRepository.findOne({
      where: { id },
      select: [
        'id',
        'name',
        'email',
        'profile_picture',
        'step_goal',
        'social_type',
        'social_id',
        'access_token',
        'access_token_expiry',
        'status',
        'device_id',
        'best_streak',
        'current_streak',
        'streak_updated_at',
        'is_email_verified',
        'zipcode',
        'whatsapp_no',
        'notify',
      ],
      relations: ['province'],
    });

    if (data) {
      const completeUrl = getCompleteUrl(data.profile_picture);

      const response = {
        ...data,
        profile_picture: completeUrl,
      };

      return response;
    }
  }

  async findOneBySocialId(social_id: string) {
    const data = await this.appUserRepository.findOne({
      where: { social_id: social_id },
      select: [
        'id',
        'name',
        'email',
        'password',
        'profile_picture',
        'step_goal',
        'access_token',
        'access_token_expiry',
        'social_id',
        'social_type',
        'device_id',
        'status',
        'is_email_verified',
        'streak_updated_at',
        'whatsapp_no',
        'zipcode',
        'notify',
      ],
      relations: ['province'],
    });

    if (data) {
      const completeUrl = getCompleteUrl(data.profile_picture);

      const response = {
        ...data,
        profile_picture: completeUrl,
      };

      return response;
    }
  }

  async getUserWithGroupCount(id: string) {
    const data = await this.appUserRepository.findOne({
      where: { id },
      relations: ['user_steps', 'user_groups'],
    });

    if (data) {
      const completeUrl = getCompleteUrl(data.profile_picture);

      const currentDate = moment().format('YYYY-MM-DD');

      const groupsWithIcons = await Promise.all(
        data.user_groups.map(async (group) => {
          // Check if group's goal end date is after current date and status is 1
          if (
            moment(group.goal_end_date).isAfter(currentDate) &&
            group.status === 1
          ) {
            const userIdWithJoinDate =
              await this.groupsRepository.getAppUsersWithCreatedAt(group.id);
            let totalSteps: number = 0;

            if (!isEmpty(userIdWithJoinDate)) {
              const stepsPromises = userIdWithJoinDate.map(async (value) => {
                const userData = await this.groupsRepository.findUserSteps(
                  value.userId,
                );

                if (userData) {
                  const userJoinDate = moment(value.joinDate)
                    .utc(true)
                    .valueOf();
                  userData.forEach((step: AppUsersStep) => {
                    const stepDate = moment(step.created_at)
                      .utc(true)
                      .valueOf();
                    if (stepDate >= userJoinDate) {
                      totalSteps += parseInt(step.steps);
                    }
                  });
                }
              });
              await Promise.all(stepsPromises);
            }

            return {
              ...group,
              icon: isEmpty(group.icon)
                ? null
                : `${process.env.DOMAIN}${group.icon}`,
              total_steps: totalSteps,
            };
          } else {
            // If the group does not meet the conditions, return null
            return null;
          }
        }),
      );

      // Filter out null values (groups that did not meet conditions)
      const validGroupsWithIcons = groupsWithIcons.filter(
        (group) => group !== null,
      );

      const response = {
        ...data,
        profile_picture: completeUrl,
        user_groups: validGroupsWithIcons,
      };

      return response;
    }
  }

  async findUserId(id: string) {
    const data = await this.appUserRepository.findOne({
      where: { id },
      relations: ['user_steps', 'user_groups'],
    });

    if (data) {
      const response = {
        ...data,
      };

      return response;
    }
  }

  async findUserDetails(id: string) {
    const data = await this.appUserRepository.findOne({
      where: { id },
      select: [
        'id',
        'name',
        'email',
        'profile_picture',
        'step_goal',
        'social_id',
        'social_type',
        'best_streak',
        'current_streak',
        'access_token',
        'access_token_expiry',
        'streak_updated_at',
        'zipcode',
        'notify',
        'whatsapp_no',
      ],
      relations: ['province'],
    });

    const completeUrl = getCompleteUrl(data?.profile_picture);

    if (data) {
      const response = {
        ...data,
        profile_picture: completeUrl,
      };

      return response;
    }
  }

  async findUserDetailsUpdate(id: string) {
    const data = await this.appUserRepository.findOne({
      where: { id },
      select: [
        'id',
        'name',
        'email',
        'profile_picture',
        'step_goal',
        'social_id',
        'social_type',
        'best_streak',
        'current_streak',
        'access_token',
        'access_token_expiry',
        'streak_updated_at',
      ],
    });

    if (data) {
      const response = {
        ...data,
      };

      return response;
    }
  }

  async findByEmail(email: string) {
    const data = await this.appUserRepository.findOne({
      where: {
        email: ILike(email.trim()),
        social_id: IsNull(),
      },
      relations: ['province'],
    });

    if (data) {
      const completeUrl = getCompleteUrl(data.profile_picture);

      const response = {
        ...data,
        profile_picture: completeUrl,
      };

      return response;
    }
  }

  async findByVerificationToken(verification_token: string) {
    const data = await this.appUserRepository.findOne({
      where: { verification_token },
    });

    if (data) {
      const completeUrl = getCompleteUrl(data.profile_picture);

      const response = {
        ...data,
        profile_picture: completeUrl,
      };

      return response;
    } else {
      return null;
    }
  }

  async getUsersGoals() {
    const result = await this.appUserRepository.find();

    const goals = result
      .map((res) => res.step_goal) // Extract step goals
      .filter((goal) => !isEmpty(goal)) // Filter out empty goals
      .reduce((acc, curr) => (isEmpty(acc) ? curr : acc), undefined); // Return the first non-empty goal

    return { goal: isEmpty(goals) ? null : goals };
  }
}
