import {
  BadRequestException,
  Injectable,
  NotFoundException,
  UnauthorizedException,
} from '@nestjs/common';
import { CreatePostDto } from './dto/create-post.dto';
import { UpdatePostDto } from './dto/update-post.dto';
import { PostEntity } from './entities/post.entity';
import { InjectRepository } from '@nestjs/typeorm';
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 { PostRepository } from './posts.repository';
import { UserRepository } from 'src/user/user.repository';
import * as admin from 'firebase-admin';
import { ReportPostDto } from './dto/report-post.dto';

require('dotenv').config();

const serviceAccount = {
  projectId: process.env.PROJECT_ID,
  clientEmail: process.env.CLIENT_EMAIL,
  privateKey: process.env.PRIVATE_KEY.replace(/\\n/g, '\n'),
};

admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
});

@Injectable()
export class PostsService {
  constructor(
    @InjectRepository(PostEntity)
    private readonly postService: Repository<PostEntity>,
    private readonly postRepository: PostRepository,
    private readonly userRepository: UserRepository,
  ) {}

  async create(createCourseDto: CreatePostDto) {
    const createCourse: CreatePostDto = new PostEntity();

    Object.assign(createCourse, createCourseDto);

    const returnData = await this.postService.save(createCourse);

    return returnData;
  }

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

  async findAllReport(take: number, skip: number, search: any) {
    return await this.postRepository.findAllReportedPOST(take, skip, search);
  }

  async sendPushNotificationToUsers(title: string, body: string) {
    const users = await this.userRepository.findAll();

    const tokens = users.map((user) => user.fcm_token).filter((token) => token);

    const message = {
      notification: {
        title: title,
        body: body,
      },
      tokens,
    };

    try {
      const response = await admin.messaging().sendEachForMulticast(message);

      return 'Push notification sent successfully!';
    } catch (error) {
      return `Error sending push notification: ${
        error.message || JSON.stringify(error)
      }`;
    }
  }

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

    const post = await this.postRepository.findOne(id);

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

    return post;
  }

  async update(id: string, userID: string, updatePostDto: UpdatePostDto) {
    if (!isValidUuidV4(id)) {
      throw new BadRequestException(lan('common.invalid_uuid_format'));
    }

    const postObj = await this.postRepository.findOne(id);

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

    if (postObj.user_id !== userID) {
      throw new UnauthorizedException('Access Denied');
    }

    if (postObj.title) {
      postObj.title = updatePostDto.title;
    }

    if (postObj.user_id) {
      const userID = updatePostDto.user_id;
      const user = await this.userRepository.findOne(userID);

      if (user) {
        postObj.user_id = updatePostDto.user_id;
      } else {
        throw new NotFoundException(lan('common.data_not_found'));
      }
    }

    if (updatePostDto.status !== undefined && updatePostDto.status !== null) {
      postObj.status = updatePostDto.status;
    }

    await this.postService.update(id, updatePostDto);

    return postObj;
  }

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

    const postObj = await this.postRepository.findOne(id);

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

    if (postObj.title) {
      postObj.title = updatePostDto.title;
    }

    if (postObj.user_id) {
      const userID = updatePostDto.user_id;
      const user = await this.userRepository.findOne(userID);

      if (user) {
        postObj.user_id = updatePostDto.user_id;
      } else {
        throw new NotFoundException(lan('common.data_not_found'));
      }
    }

    if (updatePostDto.status !== undefined && updatePostDto.status !== null) {
      postObj.status = updatePostDto.status;
    }

    await this.postService.update(id, updatePostDto);

    return postObj;
  }

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

    const postObj = await this.postRepository.findOne(id);

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

    if (postObj.reason) {
      postObj.reason = reportPostDto.reason;
    }

    if (postObj.message) {
      postObj.message = reportPostDto.message;
    }

    if (reportPostDto.status !== undefined && reportPostDto.status !== null) {
      postObj.status = reportPostDto.status;
    }

    await this.postService.update(id, reportPostDto);

    return postObj;
  }

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

    const post = await this.postRepository.findOne(id);

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

    if (userID !== post.user_id) {
      throw new UnauthorizedException('Access Denied');
    }

    return this.postService.softDelete(id);
  }

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

    const post = await this.postRepository.findOne(id);

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

    return this.postService.softDelete(id);
  }
}
