import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, In } from 'typeorm';
import { Movie } from './entities/movie.entity';
import { Category } from '../categories/entities/category.entity';
import { CreateMovieDto } from './dto/create-movie.dto';
import { UpdateMovieDto } from './dto/update-movie.dto';
import { PaginationOptions } from '../common/interfaces';

@Injectable()
export class MoviesService {
  constructor(
    @InjectRepository(Movie)
    private readonly movieRepository: Repository<Movie>,
    @InjectRepository(Category)
    private readonly categoryRepository: Repository<Category>,
  ) {}

  async create(createMovieDto: CreateMovieDto): Promise<Movie> {
    const { categoryIds, ...movieData } = createMovieDto;

    const movie = this.movieRepository.create(movieData);

    if (categoryIds && categoryIds.length > 0) {
      movie.categories = await this.categoryRepository.findBy({
        id: In(categoryIds),
      });
    }

    return this.movieRepository.save(movie);
  }

  async findAll(pagination: PaginationOptions) {
    const { page, limit } = pagination;
    const skip = (page - 1) * limit;

    const [movies, total] = await this.movieRepository.findAndCount({
      where: { isActive: true },
      order: { createdAt: 'DESC' },
      skip,
      take: limit,
    });

    return {
      data: movies,
      total,
      page,
      limit,
      totalPages: Math.ceil(total / limit),
    };
  }

  async findById(id: string): Promise<Movie> {
    const movie = await this.movieRepository.findOne({
      where: { id },
    });

    if (!movie) {
      throw new NotFoundException(`Movie with ID "${id}" not found`);
    }

    return movie;
  }

  async update(id: string, updateMovieDto: UpdateMovieDto): Promise<Movie> {
    const movie = await this.findById(id);
    const { categoryIds, ...movieData } = updateMovieDto;

    Object.assign(movie, movieData);

    if (categoryIds !== undefined) {
      if (categoryIds.length > 0) {
        movie.categories = await this.categoryRepository.findBy({
          id: In(categoryIds),
        });
      } else {
        movie.categories = [];
      }
    }

    return this.movieRepository.save(movie);
  }

  async remove(id: string): Promise<void> {
    const movie = await this.findById(id);
    await this.movieRepository.remove(movie);
  }

  async count(): Promise<number> {
    return this.movieRepository.count({ where: { isActive: true } });
  }

  async search(query: string) {
    return this.movieRepository
      .createQueryBuilder('movie')
      .leftJoinAndSelect('movie.categories', 'category')
      .where('movie.isActive = :isActive', { isActive: true })
      .andWhere(
        '(LOWER(movie.title) LIKE LOWER(:query) OR LOWER(movie.synopsis) LIKE LOWER(:query))',
        { query: `%${query}%` },
      )
      .orderBy('movie.title', 'ASC')
      .getMany();
  }
}
