import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, FindOptionsWhere } from 'typeorm';
import { Idea } from './entities/idea.entity';
import { CreateIdeaDto, UpdateIdeaDto, QueryIdeasDto } from './dto';

@Injectable()
export class IdeasService {
  constructor(
    @InjectRepository(Idea)
    private readonly ideasRepository: Repository<Idea>,
  ) {}

  async create(createIdeaDto: CreateIdeaDto): Promise<Idea> {
    const maxOrder = await this.ideasRepository.maximum('order');
    const idea = this.ideasRepository.create({
      ...createIdeaDto,
      order: (maxOrder ?? -1) + 1,
    });
    return this.ideasRepository.save(idea);
  }

  async findAll(query: QueryIdeasDto) {
    const {
      status,
      priority,
      module,
      search,
      sortBy = 'order',
      sortOrder = 'ASC',
      page = 1,
      limit = 20,
    } = query;

    const where: FindOptionsWhere<Idea> = {};

    if (status) {
      where.status = status;
    }

    if (priority) {
      where.priority = priority;
    }

    if (module) {
      where.module = module;
    }

    const queryBuilder = this.ideasRepository.createQueryBuilder('idea');

    if (status) {
      queryBuilder.andWhere('idea.status = :status', { status });
    }

    if (priority) {
      queryBuilder.andWhere('idea.priority = :priority', { priority });
    }

    if (module) {
      queryBuilder.andWhere('idea.module = :module', { module });
    }

    if (search) {
      queryBuilder.andWhere(
        '(idea.title ILIKE :search OR idea.description ILIKE :search)',
        { search: `%${search}%` },
      );
    }

    const validSortFields = [
      'order',
      'title',
      'status',
      'priority',
      'createdAt',
      'updatedAt',
    ];
    const sortField = validSortFields.includes(sortBy) ? sortBy : 'order';

    queryBuilder
      .orderBy(`idea.${sortField}`, sortOrder)
      .skip((page - 1) * limit)
      .take(limit);

    const [data, total] = await queryBuilder.getManyAndCount();

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

  async findOne(id: string): Promise<Idea> {
    const idea = await this.ideasRepository.findOne({ where: { id } });
    if (!idea) {
      throw new NotFoundException(`Idea with ID "${id}" not found`);
    }
    return idea;
  }

  async update(id: string, updateIdeaDto: UpdateIdeaDto): Promise<Idea> {
    const idea = await this.findOne(id);
    Object.assign(idea, updateIdeaDto);
    return this.ideasRepository.save(idea);
  }

  async remove(id: string): Promise<void> {
    const idea = await this.findOne(id);
    await this.ideasRepository.remove(idea);
  }

  async getModules(): Promise<string[]> {
    const result = await this.ideasRepository
      .createQueryBuilder('idea')
      .select('DISTINCT idea.module', 'module')
      .where('idea.module IS NOT NULL')
      .getRawMany<{ module: string }>();

    return result.map((r) => r.module).filter(Boolean);
  }

  async reorder(items: { id: string; order: number }[]): Promise<void> {
    await this.ideasRepository.manager.transaction(async (manager) => {
      for (const item of items) {
        await manager.update(Idea, item.id, { order: item.order });
      }
    });
  }
}
