import { v4 as uuid } from 'uuid';
import * as path from 'path';
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { ErrorService } from '../../error/error.service';
import { Repository } from 'typeorm';
import {
  applyFiltersToBuilder,
  applySortingToBuilder,
  paginationResult,
} from 'src/utils';
import { NewsEntity } from './entities/news.entity';
import { CreateNewsDto } from './dto/create-news.dto';
import { UpdateNewsDto } from './dto/update-news.dto';
import {
  NewscommentEntity,
  NewsCommentStatus,
} from './entities/news-comment.entity';
import { UpdateStatusCommentDto } from './dto/update-status-comment.dto';
import { optimizeImage } from '../../utils/sharp';
import { getLastDisplayOrder } from 'src/utils/last-display-order';

@Injectable()
export class NewsService {
  constructor(
    @InjectRepository(NewsEntity)
    private newsRepository: Repository<NewsEntity>,

    @InjectRepository(NewscommentEntity)
    private newsCommentRepository: Repository<NewscommentEntity>,

    private error: ErrorService,
  ) {}

  /**
   * -------------------------------------------------------
   * GET /news
   */
  async getAll(page = 1, limit = 20, filters = null, sorts = null) {
    let builder = this.newsRepository.createQueryBuilder('news');
    builder.leftJoin(
      'news.newscomments',
      'comments',
      'comments.status = :accepted ',
      { accepted: NewsCommentStatus.accepted },
    );

    builder
      .select([
        'news.id',
        'news.title',
        'news.image',
        'news.createdAt',
        'news.displayOrder',

        'COUNT(comments.id) AS commentCount',
      ])
      .groupBy('news.id')
      .take(limit)
      .skip((page - 1) * limit);

    if (sorts) {
      builder = applySortingToBuilder(builder, sorts);
    } else {
      builder.orderBy('news.displayOrder', 'ASC');
    }

    builder = applyFiltersToBuilder(builder, filters);

    const [items, totalItems] = await builder.getManyAndCount();

    return {
      // items: items.map((item) => ({
      //   ...item,
      //   commentCount: item.newscomments.length,
      // })),
      items,
      pagination: paginationResult(page, limit, totalItems),
    };
  }

  /**
   * -------------------------------------------------------
   * GET /news/1
   */
  async getById(id: number) {
    const builder = await this.newsRepository.createQueryBuilder('news');
    builder.leftJoin('news.user', 'user');

    builder
      .select([
        'news.id',
        'news.title',
        'news.image',
        'news.description',
        'news.metaKeywords',
        'news.metaDescription',
        'news.metaTitle',
        'news.createdAt',

        'user.id',
        'user.name',
        'user.surName',
      ])
      .andWhere({ id });
    return builder.getOne();
  }

  /**
   * -------------------------------------------------------
   * POST /news
   */
  async addNews(
    dto: CreateNewsDto,
    userId: string,
    image: Express.Multer.File,
  ) {
    const newNews = new NewsEntity();
    for (const key in dto) {
      newNews[key] = dto[key];
    }

    // Resize and change quality of image
    const finalImagePath = `${uuid()}${path.extname(image.originalname)}`;
    await optimizeImage(image.path, `${image.destination}/${finalImagePath}`);

    newNews.image = `/uploads/news/${finalImagePath}`;
    newNews.displayOrder = await getLastDisplayOrder(NewsEntity.name);

    newNews.userId = userId;
    newNews.createdAt = new Date();
    newNews.updatedAt = new Date();

    const { identifiers } = await this.newsRepository
      .createQueryBuilder()
      .insert()
      .values(newNews)
      .execute();

    const newNewsId = identifiers[0].id;
    return await this.getById(newNewsId);
  }

  /**
   * -------------------------------------------------------
   * PUT /news/1
   */
  async updateNews(id: number, dto: UpdateNewsDto, image: Express.Multer.File) {
    if (image) {
      // Resize and change quality of image
      const finalImagePath = `${uuid()}${path.extname(image.originalname)}`;
      await optimizeImage(image.path, `${image.destination}/${finalImagePath}`);

      dto.image = `/uploads/news/${finalImagePath}`;
    }

    return await this.newsRepository
      .createQueryBuilder()
      .update()
      .set({ ...dto, updatedAt: new Date() })
      .where({ id })
      .execute();
  }

  /**
   * -------------------------------------------------------
   * DELETE /news/1/admin
   */
  async deleteNews(id: number) {
    return await this.newsRepository
      .createQueryBuilder()
      .update()
      .set({ deleted: true, updatedAt: new Date() })
      .where({ id })
      .execute();
  }

  /**
   * -------------------------------------------------------
   * GET /news/comments/admin
   */
  async getCommentslist(page = 1, limit = 20, filters = null, sorts = null) {
    let builder = this.newsCommentRepository.createQueryBuilder('comments');
    builder.leftJoin('comments.user', 'user');
    builder.leftJoin('comments.news', 'news');

    builder
      .select([
        'comments.id',
        'comments.message',
        'comments.status',
        'comments.reasonRejected',
        'comments.createdAt',

        'user.id',
        'user.avatar',
        'user.name',
        'user.surName',
        'user.mobile',
        'user.email',

        'news.id',
        'news.title',
      ])

      .take(limit)
      .skip((page - 1) * limit);

    if (sorts) {
      builder = applySortingToBuilder(builder, sorts);
    } else {
      builder.orderBy('comments.createdAt', 'DESC');
    }

    builder = applyFiltersToBuilder(builder, filters);

    const [items, totalItems] = await builder.getManyAndCount();

    return {
      items,
      pagination: paginationResult(page, limit, totalItems),
    };
  }

  /**
   * -------------------------------------------------------
   * GET /news/comments/1/admin
   */
  async getCommentById(id: number) {
    const builder = await this.newsCommentRepository.createQueryBuilder(
      'comment',
    );
    builder.leftJoin('comment.user', 'user');
    builder.leftJoin('comment.news', 'news');

    builder
      .select([
        'comment.id',
        'comment.fullName',
        'comment.mobile',
        'comment.email',
        'comment.message',
        'comment.status',
        'comment.reasonRejected',
        'comment.createdAt',

        'user.id',
        'user.name',
        'user.surName',

        'news.id',
        'news.title',
      ])
      .andWhere({ id });
    return builder.getOne();
  }

  /**
   * -------------------------------------------------------
   * PUT /news/comments/1/admin
   */
  async updateStatusComment(id: number, dto: UpdateStatusCommentDto) {
    return await this.newsCommentRepository
      .createQueryBuilder()
      .update()
      .set({ ...dto, updatedAt: new Date() })
      .where({ id })
      .execute();
  }
}
