import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, IsNull, ILike, FindOptionsWhere } from 'typeorm';
import { ProgramTemplate } from 'src/common/entities/program-template.entity';
import { CommonDataService } from 'src/common/services/commonData.service';
import { handleKnownErrors } from 'src/common/utils/handle-error.util';
import { ERROR_CODES } from 'src/common/constants/error-string-constants';
import { CreateProgramTemplateDto } from './dto/create-program-template.dto';
import { UpdateProgramTemplateDto } from './dto/update-program-template.dto';
import { FilterProgramTemplateDto } from './dto/filter-program-template.dto';
import { TemplateStatus } from 'src/common/enum/template-status.enum';

@Injectable()
export class ProgramTemplateRepository {
  constructor(
    @InjectRepository(ProgramTemplate)
    private readonly programTemplateRepo: Repository<ProgramTemplate>,
    private readonly commonDataService: CommonDataService,
  ) {}

  /**
   * Find all program templates with pagination and filters
   */
  async findAll(filters: FilterProgramTemplateDto) {
    try {
      const { limit = 10, offset = 0, searchText, programTypeId, status, isActive } = filters;

      const selectFields: (keyof ProgramTemplate)[] = [
        'id',
        'programTypeId',
        'name',
        'description',
        'version',
        'status',
        'isActive',
        'createdAt',
        'updatedAt',
        'createdBy',
        'updatedBy',
      ];

      const whereClause: FindOptionsWhere<ProgramTemplate> = { deletedAt: IsNull() };

      if (searchText) {
        whereClause.name = ILike(`%${searchText}%`);
      }

      if (programTypeId !== undefined) {
        whereClause.programTypeId = programTypeId;
      }

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

      if (isActive !== undefined) {
        whereClause.isActive = isActive;
      }

      const data = await this.commonDataService.get(
        this.programTemplateRepo,
        selectFields,
        whereClause,
        limit,
        offset,
        { createdAt: 'DESC', id: 'DESC' },
      );

      const total = await this.programTemplateRepo.count({ where: whereClause });
      const totalPages = Math.ceil(total / limit);

      return {
        data,
        pagination: {
          totalPages,
          pageNumber: Math.floor(offset / limit) + 1,
          pageSize: +limit,
          totalRecords: total,
        },
      };
    } catch (error) {
      handleKnownErrors(ERROR_CODES.PROGRAM_TEMPLATE_FIND_FAILED, error);
    }
  }

  /**
   * Find a program template by ID with sections (questions loaded separately)
   */
  async findById(id: number): Promise<ProgramTemplate | null> {
    try {
      return await this.programTemplateRepo
        .createQueryBuilder('pt')
        .select([
          'pt.id',
          'pt.programTypeId',
          'pt.name',
          'pt.description',
          'pt.version',
          'pt.status',
          'pt.isActive',
          'pt.createdAt',
          'pt.createdBy',
          'pt.updatedAt',
          'pt.updatedBy'
        ])
        .leftJoinAndSelect('pt.programType', 'programType')
        .leftJoinAndSelect('pt.sections', 'sections', 'sections.deletedAt IS NULL')
        .addSelect([
          'sections.id',
          'sections.programTemplateId',
          'sections.masterFormSectionId',
          'sections.sectionKey',
          'sections.name',
          'sections.description',
          'sections.parentSectionId',
          'sections.conditionalConfig',
          'sections.displayOrder',
          'sections.createdAt',
          'sections.updatedAt'
        ])
        .where('pt.id = :id', { id })
        .andWhere('pt.deletedAt IS NULL')
        .orderBy('sections.displayOrder', 'ASC')
        .getOne();
    } catch (error) {
      handleKnownErrors(ERROR_CODES.PROGRAM_TEMPLATE_FIND_BY_ID_FAILED, error);
    }
  }

  /**
   * Find templates by program type
   */
  async findByProgramType(programTypeId: number): Promise<ProgramTemplate[]> {
    try {
      return await this.programTemplateRepo.find({
        where: { programTypeId, deletedAt: IsNull() },
        order: { version: 'DESC', createdAt: 'DESC' },
      });
    } catch (error) {
      handleKnownErrors(ERROR_CODES.PROGRAM_TEMPLATE_FIND_FAILED, error);
    }
  }

  /**
   * Create a program template
   */
  async create(createDto: CreateProgramTemplateDto): Promise<ProgramTemplate> {
    try {
      const now = new Date();
      const template = this.programTemplateRepo.create({
        ...createDto,
        version: 1,
        status: TemplateStatus.DRAFT,
        isActive: true,
        createdAt: now,
        updatedAt: now,
      });
      return await this.programTemplateRepo.save(template);
    } catch (error) {
      handleKnownErrors(ERROR_CODES.PROGRAM_TEMPLATE_CREATE_FAILED, error);
    }
  }

  /**
   * Update a program template
   */
  async update(id: number, updateDto: UpdateProgramTemplateDto): Promise<ProgramTemplate> {
    try {
      const now = new Date();
      await this.programTemplateRepo.update(id, {
        ...updateDto,
        updatedAt: now,
      });
      const updated = await this.findById(id);
      if (!updated) {
        throw new Error('Template not found after update');
      }
      return updated;
    } catch (error) {
      handleKnownErrors(ERROR_CODES.PROGRAM_TEMPLATE_UPDATE_FAILED, error);
    }
  }

  /**
   * Soft delete a program template
   */
  async softDelete(id: number): Promise<boolean> {
    try {
      const result = await this.programTemplateRepo.softDelete(id);
      return (result.affected ?? 0) > 0;
    } catch (error) {
      handleKnownErrors(ERROR_CODES.PROGRAM_TEMPLATE_DELETE_FAILED, error);
    }
  }
}
