import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, In } from 'typeorm';
import { Series } from './entities/series.entity';
import { Season } from './entities/season.entity';
import { Episode } from './entities/episode.entity';
import { Category } from '../categories/entities/category.entity';
import { CreateSeriesDto } from './dto/create-series.dto';
import { CreateSeasonDto } from './dto/create-season.dto';
import { CreateEpisodeDto } from './dto/create-episode.dto';
import { PaginationOptions } from '../common/interfaces';

@Injectable()
export class SeriesService {
  constructor(
    @InjectRepository(Series)
    private readonly seriesRepository: Repository<Series>,
    @InjectRepository(Season)
    private readonly seasonRepository: Repository<Season>,
    @InjectRepository(Episode)
    private readonly episodeRepository: Repository<Episode>,
    @InjectRepository(Category)
    private readonly categoryRepository: Repository<Category>,
  ) {}

  async create(createSeriesDto: CreateSeriesDto): Promise<Series> {
    const { categoryIds, ...seriesData } = createSeriesDto;

    const series = this.seriesRepository.create(seriesData);

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

    return this.seriesRepository.save(series);
  }

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

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

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

  async findById(id: string): Promise<Series> {
    const series = await this.seriesRepository.findOne({
      where: { id },
      relations: ['categories', 'seasons', 'seasons.episodes'],
    });

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

    // Sort seasons and episodes by number
    if (series.seasons) {
      series.seasons.sort((a, b) => a.number - b.number);
      series.seasons.forEach((season) => {
        if (season.episodes) {
          season.episodes.sort((a, b) => a.number - b.number);
        }
      });
    }

    return series;
  }

  async update(id: string, updateData: Partial<CreateSeriesDto>): Promise<Series> {
    const series = await this.findById(id);
    const { categoryIds, ...seriesData } = updateData;

    Object.assign(series, seriesData);

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

    return this.seriesRepository.save(series);
  }

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

  // Season operations
  async createSeason(seriesId: string, createSeasonDto: CreateSeasonDto): Promise<Season> {
    const series = await this.seriesRepository.findOne({ where: { id: seriesId } });
    if (!series) {
      throw new NotFoundException(`Series with ID "${seriesId}" not found`);
    }

    const season = this.seasonRepository.create({
      ...createSeasonDto,
      seriesId,
    });

    return this.seasonRepository.save(season);
  }

  async findSeasonById(seasonId: string): Promise<Season> {
    const season = await this.seasonRepository.findOne({
      where: { id: seasonId },
      relations: ['episodes'],
    });

    if (!season) {
      throw new NotFoundException(`Season with ID "${seasonId}" not found`);
    }

    return season;
  }

  // Episode operations
  async createEpisode(
    seriesId: string,
    seasonId: string,
    createEpisodeDto: CreateEpisodeDto,
  ): Promise<Episode> {
    const season = await this.seasonRepository.findOne({
      where: { id: seasonId, seriesId },
    });

    if (!season) {
      throw new NotFoundException(
        `Season with ID "${seasonId}" not found in series "${seriesId}"`,
      );
    }

    const episode = this.episodeRepository.create({
      ...createEpisodeDto,
      seasonId,
    });

    return this.episodeRepository.save(episode);
  }

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

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