import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { UserEntity } from './entities/user.entity';
import { Brackets, FindOneOptions, ILike, Repository } from 'typeorm';
import { isEmpty } from 'src/common/helper';
import { IsEmail } from 'class-validator';

@Injectable()
export class UserRepository {
  constructor(
    @InjectRepository(UserEntity)
    private readonly userRepository: Repository<UserEntity>,
  ) {}

  async findAllActiveUser(
    take: number,
    skip: number,
    search: string,
    years: number[],
    courseIds: string[],
    dateOfBirth: string,
    cityIds: string[],
    jobIds: string[],
    key?: string,
    order?: string,
  ) {
    let query = this.userRepository
      .createQueryBuilder('user')
      .where('user.status = :status AND user.is_admin = :isAdmin', {
        status: 1,
        isAdmin: 0,
      })
      .leftJoinAndSelect('user.job', 'job')
      .leftJoinAndSelect('user.city', 'city')
      .leftJoinAndSelect('user.posts', 'post');

    if (search) {
      query = query.andWhere(
        new Brackets((qb) => {
          qb.where(
            `user.first_name ILIKE :search 
             OR user.last_name ILIKE :search 
             OR CAST(user.batch AS VARCHAR) ILIKE :search 
             OR CAST(user.course AS VARCHAR) ILIKE :search 
             OR city.name ILIKE :search 
             OR job.name ILIKE :search  
             OR CONCAT(user.first_name, ' ', user.last_name) ILIKE :search
             OR user.designation ILIKE :search
             OR CAST(user.contact_no AS VARCHAR) ILIKE :search`,

            { search: `%${search}%` },
          ).andWhere('user.status = :status', { status: 1 });
        }),
      );
    }

    if (years && years.length > 0) {
      query = query.andWhere('user.batch && :years', { years });
    }

    if (courseIds && courseIds.length > 0) {
      query = query.andWhere('user.course && :courseIds', { courseIds });
    }

    if (!isEmpty(dateOfBirth)) {
      query = query.andWhere('user.date_of_birth = :dateOfBirth', {
        dateOfBirth,
      });
    }

    if (!isEmpty(cityIds) && cityIds.length > 0) {
      query = query.andWhere('user.city_id IN (:...cityIds)', { cityIds });
    }

    if (!isEmpty(jobIds) && jobIds.length > 0) {
      query = query.andWhere('user.job_id IN (:...jobIds)', { jobIds });
    }

    if (key && order) {
      const validKeys = [
        'first_name',
        'email',
        'contact_no',
        'city',
        'course',
        'status',
      ];

      const isValidKey = validKeys.includes(key);
      const validOrder = order.toUpperCase() === 'ASC' ? 'ASC' : 'DESC';

      if (isValidKey) {
        if (key === 'city') {
          query = query.orderBy('city.name', validOrder);
        } else {
          query = query.orderBy(`user.${key}`, validOrder);
        }
      } else {
        query = query.orderBy('user.created_at', 'DESC');
      }
    } else {
      query = query.orderBy('user.created_at', 'DESC');
    }

    const [data, count] = await query.skip(skip).take(take).getManyAndCount();

    const completeData = data.map((result) => {
      const { job, city, ...rest } = result;
      return {
        ...rest,
        profile_picture: isEmpty(result.profile_picture)
          ? null
          : `${process.env.DOMAIN}${result.profile_picture}`,
        city_id: city
          ? {
              id: result.city.id,
              name: result.city.name,
              state: city.state,
            }
          : null,

        job_id: job
          ? {
              id: result.job.id,
              name: result.job.name,
            }
          : null,
      };
    });

    const response = {
      count,
      data: completeData,
    };

    return response;
  }

  async findOne(id: string) {
    const data = await this.userRepository.findOne({
      where: { id },
      relations: ['job', 'city', 'posts'],
    });

    if (data) {
      const { city, job, ...rest } = data;

      const response = {
        ...rest,
        profile_picture: isEmpty(data.profile_picture)
          ? null
          : `${process.env.DOMAIN}${data.profile_picture}`,
        city_id: city
          ? {
              id: city.id,
              name: city.name,
              state: city.state,
            }
          : null,

        job_id: job
          ? {
              id: job.id,
              name: job.name,
            }
          : null,
      };

      return response;
    }
  }

  async findAllForApproval(take: any, skip: any) {
    const [data, count] = await this.userRepository.findAndCount({
      where: { status: 0 },
      relations: ['job', 'city'],
      skip,
      take,
    });

    const completeData = data.map((result) => {
      const { city, job, ...rest } = result;

      return {
        ...rest,
        profile_picture: isEmpty(result.profile_picture)
          ? null
          : `${process.env.DOMAIN}${result.profile_picture}`,
        city_id: city
          ? {
              id: city.id,
              name: city.name,
              state: city.state,
            }
          : null,

        job_id: job
          ? {
              id: job.id,
              name: job.name,
            }
          : null,
      };
    });

    const response = {
      count,
      data: completeData,
    };
    return response;
  }

  async filterUsersByYearsAndCourse(years: number[], courseIds: string[]) {
    let queryBuilder = this.userRepository
      .createQueryBuilder('user')
      .where('user.batch && :years', { years })
      .where('user.course && :courseIds', { courseIds });

    const filteredUsers = await queryBuilder.getMany();

    const filteredUsersWithUrl = filteredUsers.map((user) => ({
      ...user,
      profile_picture: isEmpty(user.profile_picture)
        ? null
        : `${process.env.DOMAIN}${user.profile_picture}`,
    }));

    return filteredUsersWithUrl;
  }

  async findOneForApproval(id: string) {
    const data = await this.userRepository.findOne({ where: { id } });

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

      return response;
    }
  }

  async findUserTokenExpiry(access_token: string) {
    const actualToken = access_token.replace(/^Bearer\s/, '');

    const user = await this.userRepository.findOne({
      where: { access_token: actualToken },
    });

    return user;
  }

  async findOneForPassword(identifier: any): Promise<UserEntity | undefined> {
    const isEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(identifier);

    if (isEmail) {
      return await this.userRepository.findOne({
        where: { email: ILike(identifier.toLowerCase()) },
      });
    } else {
      return await this.userRepository.findOne({
        where: { contact_no: Number(identifier) },
      });
    }
  }

  async findByEmail(email: string) {
    const data = await this.userRepository.findOne({ where: { email } });

    if (data) {
      const completeUrl = isEmpty(data.profile_picture)
        ? null
        : `${process.env.DOMAIN}${data.profile_picture}`;
      const response = {
        ...data,
        profile_picture: completeUrl,
      };

      return response;
    }
  }

  // async findOneByEmail(email: string) {
  //   const data = await this.userRepository.findOne({ where: { email } });

  //   return data;
  // }

  async findOneByEmail(email: string) {
    const data = await this.userRepository.findOne({
      where: { email: ILike(email) },
    });

    return data;
  }

  // async findByIdentifier(
  //   identifier: string | number,
  //   column: 'email' | 'contact_no',
  // ) {
  //   const whereCondition = { [column]: identifier };

  //   const data = await this.userRepository.findOne({
  //     where: whereCondition,
  //     withDeleted: true,
  //   });

  //   return data;
  // }
  async findByIdentifier(
    identifier: string | number,
    column: 'email' | 'contact_no',
  ) {
    const query = this.userRepository.createQueryBuilder('user').withDeleted();

    if (column === 'email' && typeof identifier === 'string') {
      query.where('LOWER(user.email) = LOWER(:email)', {
        email: identifier,
      });
    } else {
      query.where(`user.${column} = :value`, {
        value: identifier,
      });
    }

    return await query.getOne();
  }

  async findAll() {
    let query = this.userRepository
      .createQueryBuilder('user')
      .where('user.status = :status AND user.is_admin = :isAdmin', {
        status: 1,
        isAdmin: 0,
      })
      .leftJoinAndSelect('user.job', 'job')
      .leftJoinAndSelect('user.city', 'city')
      .leftJoinAndSelect('user.posts', 'post');

    const data = await query.getMany();

    const completeData = data.map((result) => {
      const { job, course, city, ...rest } = result;
      return {
        ...rest,
        profile_picture: isEmpty(result.profile_picture)
          ? null
          : `${process.env.DOMAIN}${result.profile_picture}`,
        city_id: city
          ? {
              id: result.city.id,
              name: result.city.name,
              state: city.state,
            }
          : null,

        job_id: job
          ? {
              id: result.job.id,
              name: result.job.name,
            }
          : null,
      };
    });

    return completeData;
  }
}
