import { Injectable, NotFoundException, BadRequestException, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Blog } from './entities/blog.entity';
import { BlogTranslation } from './entities/blog-translation.entity';
import { CreateBlogDto } from './dto/create-blog.dto';
import { UpdateBlogDto } from './dto/update-blog.dto';
import { QueryBlogDto } from './dto/query-blog.dto';
import { UserService } from 'modules/user/users.service';
import { IBlogResponseDto, IBlogResponsePaginationDto } from './dto/blog-response';
import { FileUploadService } from 'modules/file-upload/file-upload.service';

@Injectable()
export class BlogService {
  private readonly logger = new Logger(BlogService.name);
  constructor(
    @InjectRepository(Blog)
    private blogRepository: Repository<Blog>,
    @InjectRepository(BlogTranslation)
    private blogTranslationRepository: Repository<BlogTranslation>,
    private userService: UserService,
    private readonly fileuploadService: FileUploadService,
  ) {}

  async create(userId: number, createBlogDto: CreateBlogDto): Promise<IBlogResponseDto> {
    // Check if user exists
    const user = await this.userService.findUserById(userId);
    if (!user) {
      throw new NotFoundException('User not found');
    }

    // Check if author names are provided
    if (!createBlogDto.authorNames || createBlogDto.authorNames.length === 0) {
      throw new BadRequestException('At least one author name is required');
    }

    // Check if slug is provided
    if (!createBlogDto.slug) {
      throw new BadRequestException('Slug is required');
    }

    // Check if slug is unique-> database constraint is setup for this
    const existingBlog = await this.blogRepository.findOne({ where: { slug: createBlogDto.slug } });
    if (existingBlog) {
      throw new BadRequestException('Slug must be unique');
    }

    // Check if translations are provided
    if (!createBlogDto.translations || createBlogDto.translations.length === 0) {
      throw new BadRequestException('At least one translation is required');
    }

    // Create blog entity
    const blog = this.blogRepository.create({
      ...createBlogDto,
      translations: [],
    });

    // Save blog first to get blogId
    const savedBlog = await this.blogRepository.save(blog);

    // Create and save translations
    const translations = createBlogDto.translations.map(translationDto =>
      this.blogTranslationRepository.create({
        blog: savedBlog,
        blogId: savedBlog.blogId,
        ...translationDto,
      }),
    );

    savedBlog.translations = await this.blogTranslationRepository.save(translations);

    return this.findOne(savedBlog.blogId);
  }

  async findAll(queryParams: QueryBlogDto): Promise<IBlogResponsePaginationDto> {
    this.logger.log(`Querying blogs with params:`, queryParams);
    const { page = 1, limit = 10, sortBy = 'createdAt', sortDirection = 'DESC', isPublished, language, search } = queryParams;

    try {
      const skip = (page - 1) * limit;

      // Start building query
      this.logger;
      const queryBuilder = this.blogRepository.createQueryBuilder('blog').leftJoinAndSelect('blog.translations', 'translations');

      if (isPublished !== undefined) {
        queryBuilder.andWhere('blog.isPublished = :isPublished', { isPublished });
      }

      if (language) {
        queryBuilder.andWhere('translations.language = :language', { language });
      }

      if (search) {
        queryBuilder.andWhere('(translations.title LIKE :search OR translations.content LIKE :search)', { search: `%${search}%` });
      }

      // Apply sorting and pagination
      queryBuilder.orderBy(`blog.${sortBy}`, sortDirection).skip(skip).take(limit);

      const [blogs, total] = await queryBuilder.getManyAndCount();
      this.logger.log(`Found ${blogs.length} blogs`);
      return {
        data: blogs.map(blog => ({
          blogId: blog.blogId,
          isPublished: blog.isPublished,
          tags: blog.tags,
          featuredImageUrl: blog.featuredImageUrl,
          viewCount: blog.viewCount,
          authorNames: blog.authorNames,
          slug: blog.slug,
          translations: blog.translations.map(translation => ({
            translationId: translation.translationId,
            blogId: translation.blogId,
            content: translation.content,
            language: translation.language,
            title: translation.title,
          })),
          createdAt: blog.createdAt,
          updatedAt: blog.updatedAt,
        })),
        pagination: {
          page,
          limit,
          total,
          totalPages: Math.ceil(total / limit),
        },
      };
    } catch (error) {
      this.logger.error(error.message);
      throw new BadRequestException(error.message);
    }
  }

  async findOne(id: number): Promise<IBlogResponseDto> {
    this.logger.log(`Querying blog with ID: ${id}`);
    const blog = await this.blogRepository.findOne({
      where: { blogId: id },
      relations: ['translations'],
    });

    if (!blog) {
      throw new NotFoundException(`Blog with ID ${id} not found`);
    }

    const response: IBlogResponseDto = {
      blogId: blog.blogId,
      isPublished: blog.isPublished,
      tags: blog.tags,
      featuredImageUrl: blog.featuredImageUrl,
      viewCount: blog.viewCount,
      authorNames: blog.authorNames,
      slug: blog.slug,
      translations: blog.translations.map(translation => ({
        translationId: translation.translationId,
        blogId: translation.blogId,
        content: translation.content,
        language: translation.language,
        title: translation.title,
      })),
      createdAt: blog.createdAt,
      updatedAt: blog.updatedAt,
    };
    return response;
  }

  async findBySlug(slug: string, language?: string): Promise<Blog> {
    this.logger.log(`Querying blog with slug: ${slug}`);
    const queryBuilder = this.blogRepository
      .createQueryBuilder('blog')
      .leftJoinAndSelect('blog.translations', 'translations')
      .where('blog.slug = :slug', { slug });

    if (language) {
      queryBuilder.andWhere('translations.language = :language', { language });
    }

    const blog = await queryBuilder.getOne();

    if (!blog) {
      throw new NotFoundException(`Blog with slug ${slug} not found`);
    }

    return blog;
  }

  async update(id: number, userId: number, updateBlogDto: UpdateBlogDto): Promise<IBlogResponseDto> {
    try {
      const blog = await this.blogRepository.findOne({ where: { blogId: id }, relations: ['translations'] });

      // Check if the blog exists
      if (!blog) {
        throw new Error('Blog not found');
      }

      // Update basic blog fields
      if (updateBlogDto.isPublished !== undefined) blog.isPublished = updateBlogDto.isPublished;
      if (updateBlogDto.tags !== undefined) blog.tags = updateBlogDto.tags;
      if (updateBlogDto.featuredImageUrl !== undefined) blog.featuredImageUrl = updateBlogDto.featuredImageUrl;
      if (updateBlogDto.slug !== undefined) blog.slug = updateBlogDto.slug; // Add this line

      // Save updated blog
      await this.blogRepository.save(blog);

      this.logger.log(`Updated blog with ID: ${id}`);

      // Update translations if provided
      if (updateBlogDto.translations && updateBlogDto.translations.length > 0) {
        for (const translationDto of updateBlogDto.translations) {
          // Find if this translation exists
          const existingTranslation = blog.translations.find(translation => translation.language === translationDto.language);

          if (existingTranslation) {
            // Update existing translation
            if (translationDto.title) existingTranslation.title = translationDto.title;
            if (translationDto.content) existingTranslation.content = translationDto.content;
            await this.blogTranslationRepository.save(existingTranslation);
          } else {
            // Create new translation
            const newTranslation = this.blogTranslationRepository.create({
              blog,
              blogId: blog.blogId,
              ...translationDto,
            });
            blog.translations.push(await this.blogTranslationRepository.save(newTranslation));
          }
        }
      }

      return this.findOne(id);
    } catch (error) {
      this.logger.error(error.message);
      throw new BadRequestException(error.message);
    }
  }

  async incrementViewCount(slug: string): Promise<void> {
    await this.blogRepository.increment({ slug }, 'viewCount', 1);
  }

  async remove(id: number): Promise<void> {
    const blog = await this.blogRepository.findOne({ where: { blogId: id } });

    if (!blog) {
      throw new NotFoundException(`Blog with ID ${id} not found`);
    }

    await this.blogRepository.remove(blog);
  }

  async uploadBlogCoverImage(file: Express.Multer.File): Promise<string> {
    if (!file) {
      throw new BadRequestException('File must be provided');
    }

    if (!file.buffer) {
      throw new BadRequestException('Invalid file format');
    }

    const imageUrl = await this.fileuploadService.uploadImageToCloudinary(file, 'blog');
    return imageUrl;
  }
}
