import {
  BadRequestException,
  Injectable,
  NotFoundException,
} from '@nestjs/common';
import { CreateNewsDto } from './dto/create-news.dto';
import { UpdateNewsDto } from './dto/update-news.dto';
import { InjectRepository } from '@nestjs/typeorm';
import { News } from './entities/news.entity';
import { NewsRepository } from './news.repository';
import { Repository } from 'typeorm';
import isValidUuidV4 from 'src/common/uuid validator/uuid-validate';
import { lan } from 'src/lan';
import { isEmpty } from 'src/common/helper';
import { CreateNewsImageDto } from './dto/create-news-image.dto';
import { NewsMedia } from './entities/news_media.entity';
import * as path from 'path';
import * as fs from 'fs';
import { GroupsRepository } from 'src/groups/groups.repository';

@Injectable()
export class NewsService {
  constructor(
    @InjectRepository(News)
    private readonly newsEntity: Repository<News>,
    private readonly newsRepository: NewsRepository,
    @InjectRepository(NewsMedia)
    private readonly newsMediaEntity: Repository<NewsMedia>,
    private readonly groupsRepository: GroupsRepository,
  ) {}

  async create(
    createNewsDto: CreateNewsDto,
    createNewsImageDto: CreateNewsImageDto,
  ): Promise<NewsMedia[]> {
    const { group_id, ...rest } = createNewsDto;
    const news: News = await this.newsEntity.create(rest);
    const createdNews: News = await this.newsEntity.save(news);

    // Create records in the pivot table for each group_id
    if (group_id) {
      for (const groupId of group_id) {
        await this.newsEntity.manager
          .createQueryBuilder()
          .relation(News, 'groups')
          .of(createdNews)
          .add(groupId);
      }
    }

    const newsImages: NewsMedia[] = [];

    for (const attachment of createNewsImageDto.media) {
      const newsImage: NewsMedia = new NewsMedia();
      newsImage.media = attachment.path;
      newsImage.news = createdNews;
      newsImage.media_type = attachment.mimetype[0];

      const savedImage = await this.newsMediaEntity.save(newsImage);
      newsImages.push(savedImage);
    }

    return newsImages;
  }

  async findAllNews(take: number, skip: number, headers: any) {
    return await this.newsRepository.findAll(take, skip, headers);
  }

  async findNews(id: string) {
    if (!isValidUuidV4(id)) {
      throw new BadRequestException(lan('common.invalid_uuid_format'));
    }

    const news = await this.newsRepository.findOne(id);

    if (isEmpty(news)) {
      throw new NotFoundException(lan('common.data_not_found'));
    }

    return news;
  }

  async updateNews(
    id: string,
    updateNewsDto: UpdateNewsDto,
    updateNewsImageDto: CreateNewsImageDto,
  ): Promise<NewsMedia[]> {
    if (!isValidUuidV4(id)) {
      throw new BadRequestException(lan('common.invalid_uuid_format'));
    }

    const news = await this.newsRepository.findOneById(id);

    if (isEmpty(news)) {
      throw new NotFoundException(lan('common.data_not_found'));
    }

    if (updateNewsDto.group_id) {
      for (const groupId of updateNewsDto.group_id) {
        if (!isValidUuidV4(groupId)) {
          throw new BadRequestException(lan('common.invalid_uuid_format'));
        }

        const group = await this.groupsRepository.findOneById(groupId);

        if (isEmpty(group)) {
          throw new NotFoundException(lan('common.data_not_found'));
        }

        if (isEmpty(news.groups)) {
          news.groups = []; // Initialize as an empty array if undefined
        }

        // Check if the group is already associated, if not, add it
        if (!news.groups.find((g) => g.id === groupId)) {
          news.groups.push(group);
        }
      }

      // Remove groups that are not present in the updated group_id array
      news.groups = news.groups.filter((group) =>
        updateNewsDto.group_id.includes(group.id),
      );
    }

    Object.assign(news, updateNewsDto);

    const updatedNews = await this.newsEntity.save(news);
    const newsImages: NewsMedia[] = [];

    if (!isEmpty(updateNewsImageDto)) {
      for (const attachment of updateNewsImageDto.media) {
        let newsImage = news.news_media.find(
          (img) => img.media === attachment.path,
        );

        if (isEmpty(newsImage)) {
          newsImage = new NewsMedia();
          newsImage.media = attachment.path;
          newsImage.news = updatedNews;
          newsImage.media_type = attachment.mimetype[0];
        }

        if (newsImage) {
          const savedImage = await this.newsMediaEntity.save(newsImage);
          newsImages.push(savedImage);
        }
      }
    }

    return newsImages;
  }

  async removeNews(id: string) {
    if (!isValidUuidV4(id)) {
      throw new BadRequestException(lan('common.invalid_uuid_format'));
    }

    const news = await this.newsRepository.findOneById(id);

    if (isEmpty(news)) {
      throw new NotFoundException(lan('common.data_not_found'));
    }

    for (const image of news.news_media) {
      const filePath = path.join(`public/${image.media}`);
      if (fs.existsSync(filePath)) {
        fs.unlinkSync(filePath);
      }
    }

    await this.newsMediaEntity.softRemove(news.news_media);
    await this.newsEntity.softDelete(id);
  }

  async deleteImage(newsId: string, imageId: string): Promise<void> {
    const news = await this.newsRepository.findOne(newsId);

    if (isEmpty(news)) {
      throw new NotFoundException(lan('common.data_not_found'));
    }

    const image = news.news_media.find((img) => img.id === imageId);

    if (isEmpty(image)) {
      throw new NotFoundException(lan('common.data_not_found'));
    }

    await this.newsMediaEntity.softDelete(imageId);
  }

  async removeNewsFromGroup(newsId: string, groupId: string) {
    const news = await this.newsRepository.findOneById(newsId);

    if (isEmpty(news)) {
      throw new NotFoundException(lan('common.data_not_found'));
    }

    const group = await this.groupsRepository.findOneById(groupId);

    if (isEmpty(group)) {
      throw new NotFoundException(lan('common.data_not_found'));
    }

    news.groups = news.groups.filter((g) => g.id !== groupId);
    await this.newsEntity.save(news);
  }
}
