import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { DataSource, FindOptionsWhere, Like, Repository } from 'typeorm';
import { Module } from './module.entity';
import { CreateModuleDto } from './dto/create-module.dto';
import { UpdateModuleDto } from './dto/update-module.dto';
import { ReorderItemDto } from '../common/dto/reorder.dto';

export interface ModuleQuery {
  sectionId?: number;
  status?: string;
  search?: string;
}

@Injectable()
export class ModulesService {
  constructor(
    @InjectRepository(Module)
    private readonly modulesRepo: Repository<Module>,
    private readonly dataSource: DataSource,
  ) {}

  async create(dto: CreateModuleDto): Promise<Module> {
    const orderIndex = dto.orderIndex ?? (await this.nextOrderIndex(dto.sectionId));
    return this.modulesRepo.save(this.modulesRepo.create({ ...dto, orderIndex }));
  }

  findAll(query: ModuleQuery = {}): Promise<Module[]> {
    const where: FindOptionsWhere<Module> = {};
    if (query.sectionId) where.sectionId = query.sectionId;
    if (query.status) where.status = query.status;
    if (query.search) where.title = Like(`%${query.search}%`);

    return this.modulesRepo.find({
      where,
      relations: ['section'],
      order: { orderIndex: 'ASC', id: 'ASC' },
    });
  }

  findBySection(sectionId: number): Promise<Module[]> {
    return this.modulesRepo.find({
      where: { sectionId },
      relations: ['lessons'],
      order: { orderIndex: 'ASC' },
    });
  }

  findAllPublished(): Promise<Module[]> {
    return this.modulesRepo.find({
      where: { status: 'PUBLISHED' },
      relations: ['section'],
      order: { orderIndex: 'ASC' },
    });
  }

  findById(id: number): Promise<Module | null> {
    return this.modulesRepo.findOne({
      where: { id },
      relations: ['section', 'lessons', 'lessons.activities'],
    });
  }

  /** Full tree including content blocks - what the mobile app renders. */
  async findTree(id: number, publishedOnly = false): Promise<Module | null> {
    const module = await this.modulesRepo.findOne({
      where: publishedOnly ? { id, status: 'PUBLISHED' } : { id },
      relations: [
        'section',
        'lessons',
        'lessons.activities',
        'lessons.activities.contentBlocks',
      ],
    });
    if (!module) return null;

    module.lessons = (module.lessons || [])
      .filter((lesson) => !publishedOnly || lesson.status === 'PUBLISHED')
      .sort((a, b) => a.orderIndex - b.orderIndex)
      .map((lesson) => {
        lesson.activities = (lesson.activities || [])
          .filter((activity) => !publishedOnly || activity.status === 'PUBLISHED')
          .sort((a, b) => a.orderIndex - b.orderIndex)
          .map((activity) => {
            activity.contentBlocks = (activity.contentBlocks || []).sort(
              (a, b) => a.orderIndex - b.orderIndex,
            );
            return activity;
          });
        return lesson;
      });

    return module;
  }

  async update(id: number, dto: UpdateModuleDto): Promise<Module | null> {
    await this.modulesRepo.update(id, dto);
    return this.findById(id);
  }

  async delete(id: number): Promise<boolean> {
    const result = await this.modulesRepo.delete(id);
    return (result.affected || 0) > 0;
  }

  async reorder(items: ReorderItemDto[]): Promise<void> {
    await this.dataSource.transaction(async (manager) => {
      for (const item of items) {
        await manager.update(Module, item.id, { orderIndex: item.orderIndex });
      }
    });
  }

  search(term: string): Promise<Module[]> {
    return this.modulesRepo.find({
      where: [
        { title: Like(`%${term}%`), status: 'PUBLISHED' },
        { description: Like(`%${term}%`), status: 'PUBLISHED' },
      ],
      relations: ['section'],
      order: { orderIndex: 'ASC' },
      take: 50,
    });
  }

  private async nextOrderIndex(sectionId: number): Promise<number> {
    const last = await this.modulesRepo.findOne({
      where: { sectionId },
      order: { orderIndex: 'DESC' },
    });
    return last ? last.orderIndex + 1 : 0;
  }
}
