import {
  Controller,
  Get,
  Post,
  Body,
  Patch,
  Param,
  Delete,
  ValidationPipe,
  UseGuards,
  UsePipes,
  Res,
  HttpStatus,
  NotFoundException,
  BadRequestException,
  Query,
  UnauthorizedException,
  Headers,
} from '@nestjs/common';
import {
  errorResponse,
  successResponse,
  validationErrorMessage,
} from 'src/common/errors/response-config';
import { Response as ExpressResponse } from 'express';
import { saveError } from 'src/api-logs/api-error-log';
import { InjectRepository } from '@nestjs/typeorm';
import { ApiLog } from 'src/api-logs/entities/api-log.entity';
import { Repository } from 'typeorm';
import { AuthGuardMiddleware } from 'src/common/middleware/auth.middleware';
import { lan } from 'src/lan';
import { PostsService } from './posts.service';
import { CreatePostDto } from './dto/create-post.dto';
import { UpdatePostDto } from './dto/update-post.dto';
import { ReportPostDto } from './dto/report-post.dto';

@Controller('posts')
@UseGuards(AuthGuardMiddleware)
export class PostsController {
  constructor(
    private readonly postsService: PostsService,
    @InjectRepository(ApiLog)
    private readonly apiLogRepository: Repository<ApiLog>,
  ) {}

  @Post('add')
  @UsePipes(
    new ValidationPipe({
      exceptionFactory: validationErrorMessage,
      transform: true,
      transformOptions: {
        enableImplicitConversion: true,
      },
    }),
  )
  async create(
    @Body() createPostDto: CreatePostDto,
    @Res() res: ExpressResponse,
  ) {
    try {
      const post = await this.postsService.create(createPostDto);

      await this.postsService.sendPushNotificationToUsers(
        createPostDto.title,
        createPostDto.description,
      );
      res.send(successResponse(201, lan('common.request_submitted'), post));
    } catch (error) {
      saveError(error, this.apiLogRepository);

      res
        .status(HttpStatus.INTERNAL_SERVER_ERROR)
        .send(errorResponse(500, lan('common.internal_server_error'), error));
    }
  }

  @Get('get')
  async findAll(
    @Query('take') take: number,
    @Query('skip') skip: number,
    @Query('search') search: string,
    @Query('start_date') startDate: string,
    @Query('end_date') endDate: string,
    @Res() res: ExpressResponse,
    @Headers() headers: any,
  ) {
    try {
      const filterParams = {
        search,
        startDate,
        endDate,
      };

      const cities = await this.postsService.findAll(
        take,
        skip,
        filterParams,
        headers,
      );

      if (cities) {
        res
          .status(HttpStatus.OK)
          .send(
            successResponse(
              200,
              lan('common.data_retrieved_successfully'),
              cities,
            ),
          );
      } else {
        res.send(successResponse(404, lan('common.data_not_found')));
      }
    } catch (error) {
      saveError(error, this.apiLogRepository);

      res
        .status(HttpStatus.INTERNAL_SERVER_ERROR)
        .send(errorResponse(500, lan('common.internal_server_error'), error));
    }
  }

  @Get('get/:id')
  async findOne(@Param('id') id: string, @Res() res: ExpressResponse) {
    try {
      const course = await this.postsService.findOne(id);

      if (course) {
        res
          .status(HttpStatus.OK)
          .send(
            successResponse(
              200,
              lan('common.data_retrieved_successfully'),
              course,
            ),
          );
      }
    } catch (error) {
      if (
        error instanceof NotFoundException ||
        error instanceof BadRequestException
      ) {
        res
          .status(HttpStatus.NOT_FOUND)
          .send(errorResponse(404, error.message));
      } else {
        saveError(error, this.apiLogRepository);

        res
          .status(HttpStatus.INTERNAL_SERVER_ERROR)
          .send(errorResponse(500, lan('common.internal_server_error'), error));
      }
    }
  }

  @Patch('user/:user_id/update/:id')
  async update(
    @Param('id') id: string,
    @Param('user_id') userId: string,
    @Body() updatePostDto: UpdatePostDto,
    @Res() res: ExpressResponse,
  ) {
    try {
      const updatedBlog = await this.postsService.update(
        id,
        userId,
        updatePostDto,
      );

      res
        .status(HttpStatus.OK)
        .send(successResponse(200, lan('common.data_updated'), updatedBlog));
    } catch (error) {
      saveError(error, this.apiLogRepository);
      if (error instanceof UnauthorizedException) {
        res
          .status(HttpStatus.UNAUTHORIZED)
          .send(errorResponse(401, error.message));
      } else if (
        error instanceof NotFoundException ||
        error instanceof BadRequestException
      ) {
        res
          .status(HttpStatus.NOT_FOUND)
          .send(errorResponse(404, error.message));
      } else {
        res
          .status(HttpStatus.INTERNAL_SERVER_ERROR)
          .send(errorResponse(500, lan('common.internal_server_error'), error));
      }
    }
  }

  @Patch('update/:id')
  async updateForAdmin(
    @Param('id') id: string,
    @Body() updatePostDto: UpdatePostDto,
    @Res() res: ExpressResponse,
  ) {
    try {
      const updatedBlog = await this.postsService.updateForAdmin(
        id,
        updatePostDto,
      );

      res
        .status(HttpStatus.OK)
        .send(successResponse(200, lan('common.data_updated'), updatedBlog));
    } catch (error) {
      saveError(error, this.apiLogRepository);

      if (
        error instanceof NotFoundException ||
        error instanceof BadRequestException
      ) {
        res
          .status(HttpStatus.NOT_FOUND)
          .send(errorResponse(404, error.message));
      } else {
        res
          .status(HttpStatus.INTERNAL_SERVER_ERROR)
          .send(errorResponse(500, lan('common.internal_server_error'), error));
      }
    }
  }

  @Patch('report/:id')
  async updateForReport(
    @Param('id') id: string,
    @Body() reportPostDto: ReportPostDto,
    @Res() res: ExpressResponse,
  ) {
    try {
      const updatedPost = await this.postsService.report(id, reportPostDto);

      res
        .status(HttpStatus.OK)
        .send(successResponse(200, lan('common.data_updated'), updatedPost));
    } catch (error) {
      saveError(error, this.apiLogRepository);

      if (
        error instanceof NotFoundException ||
        error instanceof BadRequestException
      ) {
        res
          .status(HttpStatus.NOT_FOUND)
          .send(errorResponse(404, error.message));
      } else {
        res
          .status(HttpStatus.INTERNAL_SERVER_ERROR)
          .send(errorResponse(500, lan('common.internal_server_error'), error));
      }
    }
  }

  @Get('report')
  async GetAllReportedPost(
    @Query('take') take: number,
    @Query('skip') skip: number,
    @Query('search') search: string,
    @Query('start_date') startDate: string,
    @Query('end_date') endDate: string,
    @Res() res: ExpressResponse,
  ) {
    try {
      const filterParams = {
        search: search,
        startDate: startDate,
        endDate: endDate,
      };
      const updatedPost = await this.postsService.findAllReport(
        take,
        skip,
        filterParams,
      );

      res
        .status(HttpStatus.OK)
        .send(successResponse(200, lan('common.data_updated'), updatedPost));
    } catch (error) {
      saveError(error, this.apiLogRepository);

      if (
        error instanceof NotFoundException ||
        error instanceof BadRequestException
      ) {
        res
          .status(HttpStatus.NOT_FOUND)
          .send(errorResponse(404, error.message));
      } else {
        res
          .status(HttpStatus.INTERNAL_SERVER_ERROR)
          .send(errorResponse(500, lan('common.internal_server_error'), error));
      }
    }
  }

  @Delete('user/:user_id/delete/:id')
  async remove(
    @Param('id') id: string,
    @Param('user_id') userId: string,
    @Res() res: ExpressResponse,
  ) {
    try {
      const post = await this.postsService.remove(id, userId);

      res
        .status(HttpStatus.OK)
        .send(successResponse(200, lan('common.data_deleted'), post));
    } catch (error) {
      saveError(error, this.apiLogRepository);
      if (error instanceof UnauthorizedException) {
        res
          .status(HttpStatus.UNAUTHORIZED)
          .send(errorResponse(401, error.message));
      } else if (
        error instanceof NotFoundException ||
        error instanceof BadRequestException
      ) {
        res
          .status(HttpStatus.NOT_FOUND)
          .send(errorResponse(404, error.message));
      } else {
        res
          .status(HttpStatus.INTERNAL_SERVER_ERROR)
          .send(errorResponse(500, lan('common.internal_server_error'), error));
      }
    }
  }

  @Delete('delete/:id')
  async removeForAdmin(@Param('id') id: string, @Res() res: ExpressResponse) {
    try {
      const post = await this.postsService.removeForAdmin(id);

      res
        .status(HttpStatus.OK)
        .send(successResponse(200, lan('common.data_deleted'), post));
    } catch (error) {
      saveError(error, this.apiLogRepository);

      if (
        error instanceof NotFoundException ||
        error instanceof BadRequestException
      ) {
        res
          .status(HttpStatus.NOT_FOUND)
          .send(errorResponse(404, error.message));
      } else {
        res
          .status(HttpStatus.INTERNAL_SERVER_ERROR)
          .send(errorResponse(500, lan('common.internal_server_error'), error));
      }
    }
  }
}
