import { Inject, Injectable, Scope } from '@nestjs/common';
import { BaseReportService } from '../shared/services/base-report.service';
import { CreateReportBookmarkDto } from '../shared/dto/report-bookmark.create.dto';
import {
  GetLabelReportBookmarkDto,
  GetReportBookmarkDto,
} from '../shared/dto/report-bookmark.get.dto';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { ReportBookmarkModel } from '../shared/models/report-bookmark.model';
import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants';
import { UserProvider } from 'src/core/sessions';

@Injectable({ scope: Scope.REQUEST })
export class ReportBookmarkService extends BaseReportService {
  @Inject()
  protected userProvider: UserProvider;

  constructor(
    @InjectRepository(ReportBookmarkModel, CONNECTION_NAME.DEFAULT)
    private repo: Repository<ReportBookmarkModel>,
  ) {
    super();
  }

  async create(body: CreateReportBookmarkDto) {
    const newPayload = this.injectDefaultColumnCreate(body);
    const result = await this.repo.save(newPayload);
    if (body.applied) {
      await this.appliedFalseBookmark(result.id);
    }
    return result;
  }

  async getAll(query: GetReportBookmarkDto) {
    const modelName = ReportBookmarkModel.name;

    const creator_id = this.getUser().id;
    const unique_names = query.unique_names;
    const group_names = query.group_names;
    const types = query.types;

    const qb = this.repo
      .createQueryBuilder(modelName)
      .where((query) => {
        if (unique_names) {
          query.andWhere(`unique_name IN (:...unique_names)`, { unique_names });
        }
        if (group_names) {
          query.andWhere(`group_name IN (:...group_names)`, { group_names });
        }

        if (types) {
          query.andWhere(`type IN (:...types)`, { types });
        }

        query.andWhere(`creator_id = :creator_id`, { creator_id });
      })
      .orderBy(`${modelName}.created_at`, 'DESC');

    return await qb.getMany();
  }

  async getOne(id: string) {
    return await this.repo.findOneBy({ id });
  }

  async getAllLabelHistory(query: GetLabelReportBookmarkDto) {
    const modelName = ReportBookmarkModel.name;

    const creator_id = this.getUser().id;
    const unique_names = query.unique_names;
    const group_names = query.group_names;
    const types = query.types;

    const qb = this.repo
      .createQueryBuilder(modelName)
      .select(`${modelName}.label`)
      .where((query) => {
        if (unique_names) {
          query.andWhere(`unique_name IN (:...unique_names)`, { unique_names });
        }
        if (group_names) {
          query.andWhere(`group_name IN (:...group_names)`, { group_names });
        }

        if (types) {
          query.andWhere(`type IN (:...types)`, { types });
        }

        query.andWhere(`creator_id = :creator_id`, { creator_id });
      })
      .distinct(true);

    const newData = await qb.getRawMany();
    return newData.map((el) => el.ReportBookmarkModel_label);
  }

  async getCurrentAppliedBookmark(query: GetLabelReportBookmarkDto) {
    const modelName = ReportBookmarkModel.name;

    const creator_id = this.getUser().id;
    const unique_names = query.unique_names;
    const group_names = query.group_names;
    const types = query.types;
    const qb = this.repo
      .createQueryBuilder(modelName)
      .where((query) => {
        if (unique_names) {
          query.andWhere(`unique_name IN (:...unique_names)`, { unique_names });
        }
        if (group_names) {
          query.andWhere(`group_name IN (:...group_names)`, { group_names });
        }

        if (types) {
          query.andWhere(`type IN (:...types)`, { types });
        }

        query.andWhere(`creator_id = :creator_id`, { creator_id });
        query.andWhere(`applied = :applied`, { applied: true });
      })
      .orderBy(`${modelName}.created_at`, 'DESC');

    return await qb.getMany();
  }

  async applied(id: string) {
    await this.repo
      .createQueryBuilder()
      .update(ReportBookmarkModel)
      .set({ applied: true })
      .where((query) => {
        query.andWhere(`id = :id`, { id });
      })
      .execute();

    const data = await this.appliedFalseBookmark(id);
    const group_name = data.group_name;
    const unique_name = data.unique_name;
    const type = data.type;

    return await this.getAll({
      group_names: [group_name],
      unique_names: [unique_name],
      types: [type],
    });
  }

  async unapplied(id: string) {
    await this.repo
      .createQueryBuilder()
      .update(ReportBookmarkModel)
      .set({ applied: false })
      .where((query) => {
        query.andWhere(`id = :id`, { id });
      })
      .execute();

    const data = await this.getOne(id);
    const group_name = data.group_name;
    const unique_name = data.unique_name;
    const type = data.type;

    return await this.getAll({
      group_names: [group_name],
      unique_names: [unique_name],
      types: [type],
    });
  }

  async delete(id: string) {
    await this.repo.delete(id);
    return {
      success: true,
      message: `Successfully deleted bookmark with id "${id}"`,
    };
  }

  async appliedFalseBookmark(id: string) {
    const data = await this.getOne(id);
    const creator_id = data.creator_id;
    const type = data.type;
    const group_name = data.group_name;
    const unique_name = data.unique_name;

    await this.repo
      .createQueryBuilder()
      .update(ReportBookmarkModel)
      .set({ applied: false })
      .where((query) => {
        query.andWhere(`id != :id`, { id });
        query.andWhere(`group_name = :group_name`, { group_name });
        query.andWhere(`unique_name = :unique_name`, { unique_name });
        query.andWhere(`creator_id = :creator_id`, { creator_id });
        query.andWhere(`type = :type`, { type });
      })
      .execute();

    return data;
  }
}
