import { Injectable } from '@nestjs/common';
import { MasterQuestionRepository } from './master-question.repository';
import { MasterFormSectionRepository } from 'src/master-form-section/master-form-section.repository';
import { CreateMasterQuestionDto } from './dto/create-master-question.dto';
import { UpdateMasterQuestionDto } from './dto/update-master-question.dto';
import { FilterMasterQuestionDto } from './dto/filter-master-question.dto';
import { InifniNotFoundException } from 'src/common/exceptions/infini-notfound-exception';
import InifniBadRequestException from 'src/common/exceptions/infini-badrequest-exception';
import { ERROR_CODES } from 'src/common/constants/error-string-constants';
import { AppLoggerService } from 'src/common/services/logger.service';
import { QuestionType } from 'src/common/enum/question-type.enum';
import { handleKnownErrors } from 'src/common/utils/handle-error.util';

@Injectable()
export class MasterQuestionService {
  constructor(
    private readonly masterQuestionRepo: MasterQuestionRepository,
    private readonly masterFormSectionRepo: MasterFormSectionRepository,
    private readonly logger: AppLoggerService,
  ) {}

  /**
   * Get all master questions with filters
   */
  async findAll(filters: FilterMasterQuestionDto) {
    try {
      this.logger.log('Finding all master questions', { filters });
      return await this.masterQuestionRepo.findAll(filters);
    } catch (error) {
      this.logger.error('Error finding master questions', error);
      handleKnownErrors(ERROR_CODES.MASTER_QUESTION_FIND_FAILED, error);
    }
  }

  /**
   * Get a single master question by ID
   */
  async findById(id: number) {
    try {
      this.logger.log('Finding master question by ID', { id });
      const question = await this.masterQuestionRepo.findById(id);

      if (!question) {
        handleKnownErrors(
          ERROR_CODES.MASTER_QUESTION_NOT_FOUND,
          new Error(`Master question not found: ${id}`)
        );
      }

      return question;
    } catch (error) {
      this.logger.error('Error finding master question by ID', error);
      handleKnownErrors(ERROR_CODES.MASTER_QUESTION_FIND_BY_ID_FAILED, error);
    }
  }

  /**
   * Get questions by section ID
   */
  async findBySection(sectionId: number) {
    try {
      this.logger.log('Finding questions by section', { sectionId });

      // Verify section exists
      const section = await this.masterFormSectionRepo.findById(sectionId);
      if (!section) {
        handleKnownErrors(
          ERROR_CODES.MASTER_FORM_SECTION_NOT_FOUND,
          new Error(`Master form section not found: ${sectionId}`)
        );
      }

      return await this.masterQuestionRepo.findBySection(sectionId);
    } catch (error) {
      this.logger.error('Error finding questions by section', error);
      handleKnownErrors(ERROR_CODES.MASTER_QUESTION_FIND_BY_SECTION_FAILED, error);
    }
  }

  /**
   * Create a new master question
   */
  async create(createDto: CreateMasterQuestionDto) {
    try {
      this.logger.log('Creating master question', { createDto });

      // Check if question code already exists
      const existing = await this.masterQuestionRepo.findByQuestionCode(createDto.questionCode);
      if (existing) {
        handleKnownErrors(
          ERROR_CODES.MASTER_QUESTION_CODE_EXISTS,
          new Error(`Question code already exists: ${createDto.questionCode}`)
        );
      }

      // Verify section exists
      const section = await this.masterFormSectionRepo.findById(createDto.masterFormSectionId);
      if (!section) {
        handleKnownErrors(
          ERROR_CODES.MASTER_FORM_SECTION_NOT_FOUND,
          new Error(`Master form section not found: ${createDto.masterFormSectionId}`)
        );
      }

      // Validate option_config for choice-type questions
      this.validateOptionConfig(createDto.questionType, createDto.optionConfig);

      // Validate config.is_required exists
      this.validateConfig(createDto.config);

      return await this.masterQuestionRepo.create(createDto);
    } catch (error) {
      this.logger.error('Error creating master question', error);
      handleKnownErrors(ERROR_CODES.MASTER_QUESTION_CREATE_FAILED, error);
    }
  }

  /**
   * Update a master question
   */
  async update(id: number, updateDto: UpdateMasterQuestionDto) {
    try {
      this.logger.log('Updating master question', { id, updateDto });

      // Verify question exists
      await this.findById(id);

      // Verify section exists if being updated
      if (updateDto.masterFormSectionId) {
        const section = await this.masterFormSectionRepo.findById(updateDto.masterFormSectionId);
        if (!section) {
          handleKnownErrors(
            ERROR_CODES.MASTER_FORM_SECTION_NOT_FOUND,
            new Error(`Master form section not found: ${updateDto.masterFormSectionId}`)
          );
        }
      }

      // Validate option_config if question type is being updated
      if (updateDto.questionType) {
        this.validateOptionConfig(updateDto.questionType, updateDto.optionConfig);
      }

      // Validate config if being updated
      if (updateDto.config) {
        this.validateConfig(updateDto.config);
      }

      return await this.masterQuestionRepo.update(id, updateDto);
    } catch (error) {
      this.logger.error('Error updating master question', error);
      handleKnownErrors(ERROR_CODES.MASTER_QUESTION_UPDATE_FAILED, error);
    }
  }

  /**
   * Soft delete a master question
   */
  async delete(id: number, userId?: number) {
    try {
      this.logger.log('Deleting master question', { id, userId });

      // Verify question exists
      await this.findById(id);

      await this.masterQuestionRepo.softDelete(id, userId);
    } catch (error) {
      this.logger.error('Error deleting master question', error);
      handleKnownErrors(ERROR_CODES.MASTER_QUESTION_DELETE_FAILED, error);
    }
  }

  /**
   * Validate option_config for choice-type questions
   */
  private validateOptionConfig(questionType: QuestionType, optionConfig?: Record<string, any>[]) {
    const choiceTypes = [
      QuestionType.RADIO,
      QuestionType.CHECKBOX,
      QuestionType.MULTISELECT,
      QuestionType.SELECT,
    ];

    if (choiceTypes.includes(questionType)) {
      if (!optionConfig || !Array.isArray(optionConfig) || optionConfig.length === 0) {
        handleKnownErrors(
          ERROR_CODES.MASTER_QUESTION_OPTION_CONFIG_REQUIRED,
          new Error('Option config is required for choice-type questions')
        );
      }
    }
  }

  /**
   * Validate config structure (must be a non-array object)
   */
  private validateConfig(config: Record<string, any>) {
    if (!config || typeof config !== 'object' || Array.isArray(config)) {
      handleKnownErrors(
        ERROR_CODES.MASTER_QUESTION_CONFIG_REQUIRED,
        new Error('Config must be a valid object')
      );
    }
    // Config can contain any fields - no specific structure enforced
  }
}
