import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, IsNull } from 'typeorm';
import { TemplateQuestion } from 'src/common/entities/template-question.entity';
import { handleKnownErrors } from 'src/common/utils/handle-error.util';
import { ERROR_CODES } from 'src/common/constants/error-string-constants';
import { CreateTemplateQuestionDto } from './dto/create-template-question.dto';
import { UpdateTemplateQuestionDto } from './dto/update-template-question.dto';
import { FilterTemplateQuestionDto } from './dto/filter-template-question.dto';

@Injectable()
export class TemplateQuestionRepository {
  constructor(
    @InjectRepository(TemplateQuestion)
    private readonly templateQuestionRepo: Repository<TemplateQuestion>,
  ) {}

  /**
   * Find all template questions with pagination and filters
   */
  async findAll(filters: FilterTemplateQuestionDto) {
    try {
      const { 
        limit = 10, 
        offset = 0, 
        searchText, 
        templateFormSectionId, 
        programTemplateId,
        masterQuestionId, 
        questionType, 
        answerType 
      } = filters;

      // Use query builder for complex queries with joins
      const queryBuilder = this.templateQuestionRepo
        .createQueryBuilder('tq')
        .leftJoinAndSelect('tq.templateFormSection', 'tfs')
        .where('tq.deletedAt IS NULL')
        .andWhere('(tfs.deletedAt IS NULL OR tfs.id IS NULL)'); // Filter out questions from deleted sections

      if (searchText) {
        queryBuilder.andWhere('tq.questionText ILIKE :searchText', { searchText: `%${searchText}%` });
      }

      if (templateFormSectionId !== undefined) {
        queryBuilder.andWhere('tq.templateFormSectionId = :templateFormSectionId', { templateFormSectionId });
      }

      if (programTemplateId !== undefined) {
        queryBuilder.andWhere('tfs.programTemplateId = :programTemplateId', { programTemplateId });
      }

      if (masterQuestionId !== undefined) {
        if (masterQuestionId === null) {
          queryBuilder.andWhere('tq.masterQuestionId IS NULL');
        } else {
          queryBuilder.andWhere('tq.masterQuestionId = :masterQuestionId', { masterQuestionId });
        }
      }

      if (questionType) {
        queryBuilder.andWhere('tq.questionType = :questionType', { questionType });
      }

      if (answerType) {
        queryBuilder.andWhere('tq.answerType = :answerType', { answerType });
      }

      const total = await queryBuilder.getCount();
      const data = await queryBuilder
        .orderBy('tq.displayOrder', 'ASC')
        .addOrderBy('tq.id', 'ASC')
        .skip(offset)
        .take(limit)
        .getMany();

      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.TEMPLATE_QUESTION_FIND_FAILED, error);
    }
  }

  /**
   * Find a template question by ID
   */
  async findById(id: number): Promise<TemplateQuestion | null> {
    try {
      return await this.templateQuestionRepo.findOne({
        where: { id, deletedAt: IsNull() },
        relations: ['templateFormSection', 'masterQuestion'],
      });
    } catch (error) {
      handleKnownErrors(ERROR_CODES.TEMPLATE_QUESTION_FIND_BY_ID_FAILED, error);
    }
  }

  /**
   * Find a template question by question code (within a program template)
   */
  async findByQuestionCodeAndTemplate(questionCode: string, programTemplateId: number): Promise<TemplateQuestion | null> {
    try {
      return await this.templateQuestionRepo
        .createQueryBuilder('tq')
        .leftJoinAndSelect('tq.templateFormSection', 'tfs')
        .where('tq.questionCode = :questionCode', { questionCode })
        .andWhere('tfs.programTemplateId = :programTemplateId', { programTemplateId })
        .andWhere('tq.deletedAt IS NULL')
        .andWhere('(tfs.deletedAt IS NULL OR tfs.id IS NULL)')
        .getOne();
    } catch (error) {
      handleKnownErrors(ERROR_CODES.TEMPLATE_QUESTION_FIND_BY_CODE_FAILED, error);
    }
  }

  /**
   * Find questions by template section ID
   */
  async findByTemplateSection(templateFormSectionId: number): Promise<TemplateQuestion[]> {
    try {
      return await this.templateQuestionRepo
        .createQueryBuilder('tq')
        .select([
          'tq.id',
          'tq.templateFormSectionId',
          'tq.masterQuestionId',
          'tq.questionCode',
          'tq.bindingKey',
          'tq.questionText',
          'tq.questionType',
          'tq.answerType',
          'tq.answerLocation',
          'tq.optionConfig',
          'tq.config',
          'tq.conditionalConfig',
          'tq.displayOrder',
          'tq.createdAt',
          'tq.updatedAt',
        ])
        .where('tq.templateFormSectionId = :templateFormSectionId', { templateFormSectionId })
        .andWhere('tq.deletedAt IS NULL')
        .orderBy('tq.displayOrder', 'ASC')
        .addOrderBy('tq.id', 'ASC')
        .getMany();
    } catch (error) {
      handleKnownErrors(ERROR_CODES.TEMPLATE_QUESTION_FIND_BY_SECTION_FAILED, error);
    }
  }

  /**
   * Find questions by program template ID
   */
  async findByProgramTemplate(programTemplateId: number): Promise<TemplateQuestion[]> {
    try {
      return await this.templateQuestionRepo
        .createQueryBuilder('tq')
        .leftJoinAndSelect('tq.templateFormSection', 'tfs')
        .where('tfs.programTemplateId = :programTemplateId', { programTemplateId })
        .andWhere('tq.deletedAt IS NULL')
        .andWhere('(tfs.deletedAt IS NULL OR tfs.id IS NULL)')
        .orderBy('tfs.displayOrder', 'ASC')
        .addOrderBy('tq.displayOrder', 'ASC')
        .getMany();
    } catch (error) {
      handleKnownErrors(ERROR_CODES.TEMPLATE_QUESTION_FIND_FAILED, error);
    }
  }

  /**
   * Check if question from master already cloned to template section
   */
  async findByMasterQuestionAndTemplateSection(
    masterQuestionId: number, 
    templateFormSectionId: number
  ): Promise<TemplateQuestion | null> {
    try {
      return await this.templateQuestionRepo.findOne({
        where: { 
          masterQuestionId, 
          templateFormSectionId, 
          deletedAt: IsNull() 
        },
      });
    } catch (error) {
      handleKnownErrors(ERROR_CODES.TEMPLATE_QUESTION_FIND_FAILED, error);
    }
  }

  /**
   * Get next display order for a template section
   */
  async getNextDisplayOrder(templateFormSectionId: number): Promise<number> {
    try {
      const maxOrder = await this.templateQuestionRepo.findOne({
        where: { templateFormSectionId, deletedAt: IsNull() },
        order: { displayOrder: 'DESC' },
        select: ['displayOrder'],
      });

      return (maxOrder?.displayOrder || 0) + 1;
    } catch (error) {
      handleKnownErrors(ERROR_CODES.TEMPLATE_QUESTION_FIND_FAILED, error);
    }
  }

  /**
   * Create a template question
   */
  async create(createDto: CreateTemplateQuestionDto): Promise<TemplateQuestion> {
    try {
      const question = this.templateQuestionRepo.create(createDto);
      return await this.templateQuestionRepo.save(question);
    } catch (error) {
      handleKnownErrors(ERROR_CODES.TEMPLATE_QUESTION_CREATE_FAILED, error);
    }
  }

  /**
   * Bulk create template questions (for cloning)
   */
  async bulkCreate(questions: CreateTemplateQuestionDto[]): Promise<TemplateQuestion[]> {
    try {
      const entities = this.templateQuestionRepo.create(questions);
      return await this.templateQuestionRepo.save(entities);
    } catch (error) {
      handleKnownErrors(ERROR_CODES.TEMPLATE_QUESTION_CREATE_FAILED, error);
    }
  }

  /**
   * Update a template question
   */
  async update(id: number, updateDto: UpdateTemplateQuestionDto): Promise<TemplateQuestion> {
    try {
      await this.templateQuestionRepo.update(id, updateDto);
      const updated = await this.findById(id);
      if (!updated) {
        throw new Error('Question not found after update');
      }
      return updated;
    } catch (error) {
      handleKnownErrors(ERROR_CODES.TEMPLATE_QUESTION_UPDATE_FAILED, error);
    }
  }

  /**
   * Soft delete a template question
   */
  async softDelete(id: number, userId?: number): Promise<boolean> {
    try {
      // Update the updatedBy field before soft deleting if userId is provided
      if (userId) {
        await this.templateQuestionRepo.update(id, { updatedBy: userId });
      }
      const result = await this.templateQuestionRepo.softDelete(id);
      return (result.affected ?? 0) > 0;
    } catch (error) {
      handleKnownErrors(ERROR_CODES.TEMPLATE_QUESTION_DELETE_FAILED, error);
    }
  }
}
