import {
  Controller,
  Get,
  Post,
  Body,
  Patch,
  Param,
  Delete,
  UseGuards,
  UsePipes,
  ValidationPipe,
  Res,
  HttpStatus,
  Query,
  NotFoundException,
  BadRequestException,
  UseInterceptors,
  UploadedFiles,
  Headers,
} from '@nestjs/common';
import { NewsService } from './news.service';
import { CreateNewsDto } from './dto/create-news.dto';
import { UpdateNewsDto } from './dto/update-news.dto';
import { AuthMiddleware } from 'src/common/middleware/auth.middleware';
import {
  errorResponse,
  successResponse,
  validationErrorMessage,
} from 'src/common/errors/response-config';
import { Response as ExpressResponse } from 'express';
import { lan } from 'src/lan';
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 { FileFieldsInterceptor } from '@nestjs/platform-express';
import { newsImagesConfig } from 'src/common/file upload/multer-config';
import { limits } from 'src/common/file upload/multer-config-helper';
import { CreateNewsImageDto } from './dto/create-news-image.dto';
import { FileValidationInterceptor } from 'src/common/file upload/ValidationInterceptor';

@Controller('news')
export class NewsController {
  constructor(
    private readonly newsService: NewsService,
    @InjectRepository(ApiLog)
    private readonly apiLogRepository: Repository<ApiLog>,
  ) {}

  @Post()
  @UseGuards(AuthMiddleware)
  @UseInterceptors(
    FileFieldsInterceptor(
      [{ name: 'attachments', maxCount: 10 }],
      newsImagesConfig,
    ),
    FileValidationInterceptor,
  )
  @UsePipes(
    new ValidationPipe({
      exceptionFactory: validationErrorMessage,
      transform: true,
      transformOptions: {
        enableImplicitConversion: true,
      },
    }),
  )
  async create(
    @Body() createNewsDto: CreateNewsDto,
    @Res() res: ExpressResponse,
    @UploadedFiles() files,
  ) {
    try {
      const attachments = files['attachments'];
      let imageURLs: any[] = [];

      if (Array.isArray(files.attachments)) {
        for (const file of attachments) {
          if (file.size > limits.fileSize[file.mimetype.split('/')[0]]) {
            return res.send(
              errorResponse(413, lan('common.limit_has_exceeded')),
            );
          }
        }

        imageURLs = attachments.map(async (file: any) => {
          const filePath = `uploads/news-images/${file.filename}`;

          return {
            path: filePath,
            mimetype: file.mimetype.split('/'),
          };
        });
      }

      const createNewsImageDto = new CreateNewsImageDto();
      createNewsImageDto.media = await Promise.all(imageURLs);
      await this.newsService.create(createNewsDto, createNewsImageDto);

      res.send(successResponse(201, lan('common.request_submitted')));
    } catch (error) {
      res
        .status(HttpStatus.INTERNAL_SERVER_ERROR)
        .send(errorResponse(500, lan('common.internal_server_error'), error));

      saveError(error, this.apiLogRepository);
    }
  }

  @Get()
  @UseGuards(AuthMiddleware)
  async findAll(
    @Query('take') take: number,
    @Query('skip') skip: number,
    @Res() res: ExpressResponse,
    @Headers() headers: any,
  ) {
    try {
      const news = await this.newsService.findAllNews(take, skip, headers);

      if (news) {
        res
          .status(HttpStatus.OK)
          .send(
            successResponse(
              200,
              lan('common.data_retrieved_successfully'),
              news,
            ),
          );
      }
    } catch (error) {
      res
        .status(HttpStatus.INTERNAL_SERVER_ERROR)
        .send(errorResponse(500, lan('common.internal_server_error'), error));

      saveError(error, this.apiLogRepository);
    }
  }

  @Get(':id')
  @UseGuards(AuthMiddleware)
  async findOne(@Param('id') id: string, @Res() res: ExpressResponse) {
    try {
      const news = await this.newsService.findNews(id);

      if (news) {
        res
          .status(HttpStatus.OK)
          .send(
            successResponse(
              200,
              lan('common.data_retrieved_successfully'),
              news,
            ),
          );
      }
    } catch (error) {
      if (
        error instanceof NotFoundException ||
        error instanceof BadRequestException
      ) {
        res
          .status(HttpStatus.BAD_REQUEST)
          .send(errorResponse(404, error.message));
      } else {
        res
          .status(HttpStatus.INTERNAL_SERVER_ERROR)
          .send(errorResponse(500, lan('common.internal_server_error'), error));
      }

      saveError(error, this.apiLogRepository);
    }
  }

  @Patch(':id')
  @UseGuards(AuthMiddleware)
  @UseInterceptors(
    FileFieldsInterceptor(
      [{ name: 'attachments', maxCount: 10 }],
      newsImagesConfig,
    ),
  )
  @UsePipes(
    new ValidationPipe({
      exceptionFactory: validationErrorMessage,
    }),
  )
  async update(
    @Param('id') id: string,
    @Body() updateNewsDto: UpdateNewsDto,
    @Res() res: ExpressResponse,
    @UploadedFiles() files,
  ) {
    try {
      const attachments = files['attachments'];
      let imageURLs: any;
      let updateNewsImageDto: any;

      if (Array.isArray(files.attachments)) {
        imageURLs = attachments.map(async (file) => {
          const filePath = `uploads/news-images/${file.filename}`;
          return {
            path: filePath,
            mimetype: file.mimetype.split('/'),
          };
        });

        updateNewsImageDto = new CreateNewsImageDto();
        updateNewsImageDto.media = await Promise.all(imageURLs);
      }

      await this.newsService.updateNews(id, updateNewsDto, updateNewsImageDto);
      res
        .status(HttpStatus.OK)
        .send(successResponse(200, lan('common.data_updated')));
    } catch (error) {
      if (
        error instanceof NotFoundException ||
        error instanceof BadRequestException
      ) {
        res
          .status(HttpStatus.BAD_REQUEST)
          .send(errorResponse(404, error.message));
      } else {
        res
          .status(HttpStatus.INTERNAL_SERVER_ERROR)
          .send(errorResponse(500, lan('common.internal_server_error'), error));
      }

      saveError(error, this.apiLogRepository);
    }
  }

  @Delete(':id')
  @UseGuards(AuthMiddleware)
  async remove(@Param('id') id: string, @Res() res: ExpressResponse) {
    try {
      await this.newsService.removeNews(id);
      res
        .status(HttpStatus.OK)
        .send(successResponse(200, lan('common.data_deleted')));
    } catch (error) {
      if (
        error instanceof NotFoundException ||
        error instanceof BadRequestException
      ) {
        res
          .status(HttpStatus.BAD_REQUEST)
          .send(errorResponse(404, error.message));
      } else {
        res
          .status(HttpStatus.INTERNAL_SERVER_ERROR)
          .send(errorResponse(500, lan('common.internal_server_error'), error));
      }

      saveError(error, this.apiLogRepository);
    }
  }

  @Delete('delete-image/:newsId/:imageId')
  @UseGuards(AuthMiddleware)
  async deleteImage(
    @Param('newsId') newsId: string,
    @Param('imageId') imageId: string,
    @Res() res: ExpressResponse,
  ) {
    try {
      await this.newsService.deleteImage(newsId, imageId);

      res
        .status(HttpStatus.OK)
        .send(successResponse(200, lan('common.data_deleted')));
    } catch (error) {
      if (error instanceof NotFoundException) {
        res
          .status(HttpStatus.BAD_REQUEST)
          .send(errorResponse(404, error.message));
      } else {
        res
          .status(HttpStatus.INTERNAL_SERVER_ERROR)
          .send(errorResponse(500, lan('common.internal_server_error'), error));
      }

      saveError(error, this.apiLogRepository);
    }
  }

  @Delete('delete-news/:newsId/:groupId')
  @UseGuards(AuthMiddleware)
  async deleteNewsFromGroup(
    @Param('newsId') newsId: string,
    @Param('groupId') groupId: string,
    @Res() res: ExpressResponse,
  ) {
    try {
      await this.newsService.removeNewsFromGroup(newsId, groupId);

      res
        .status(HttpStatus.OK)
        .send(successResponse(200, lan('common.data_deleted')));
    } catch (error) {
      if (error instanceof NotFoundException) {
        res
          .status(HttpStatus.BAD_REQUEST)
          .send(errorResponse(404, error.message));
      } else {
        res
          .status(HttpStatus.INTERNAL_SERVER_ERROR)
          .send(errorResponse(500, lan('common.internal_server_error'), error));
      }

      saveError(error, this.apiLogRepository);
    }
  }
}
