
import { Injectable, Logger } from '@nestjs/common';
import { ERROR_MESSAGES } from 'src/common/i18n/error-messages';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { CommunicationTemplatesMaster } from 'src/common/entities/communication-templates-master.entity';
import { CommunicationTypeEnum } from 'src/common/enum/communication-type.enum';
import { handleKnownErrors } from 'src/common/utils/handle-error.util';
import { ERROR_CODES } from 'src/common/constants/error-string-constants';

@Injectable()
export class CommunicationTemplatesMasterRepository {
  private readonly logger = new Logger(CommunicationTemplatesMasterRepository.name);

  constructor(
    @InjectRepository(CommunicationTemplatesMaster)
    private readonly repository: Repository<CommunicationTemplatesMaster>,
  ) {}

  /**
   * Get the appropriate template ID based on environment configuration
   * Checks EMAIL_USE_SANDBOX to determine if sandbox template ID should be used for email templates
   * @param template - The template object from database
   * @returns The appropriate template ID (sandbox or production)
   */
  getTemplateIdBasedOnEnvironment(template: CommunicationTemplatesMaster): string {
    const useSandbox = process.env.EMAIL_USE_SANDBOX === 'true';
    const isEmailTemplate = template.templateType === CommunicationTypeEnum.EMAIL;
    
    if (useSandbox && isEmailTemplate && template.sandboxTemplateId) {
      this.logger.log(
        `Using sandbox template ID for email template: ${template.sandboxTemplateId} (production: ${template.templateId})`
      );
      return template.sandboxTemplateId;
    }
    
    return template.templateId;
  }

  /**
   * Lightweight active master rows for a program type. Returns only the fields the
   * Add Program read endpoint and per-step validator need — not the full entity (no
   * subject/body/merge_field_map/etc.). Use this for read-side traffic; use
   * `findAllByProgramTypeId` for the clone path which needs the full snapshot.
   */
  async findActiveSummariesByProgramTypeId(
    programTypeId: number,
  ): Promise<CommunicationTemplatesMaster[]> {
    try {
      return await this.repository.find({
        where: { isActive: true, programTypeId },
        select: [
          'id',
          'step',
          'templateAccessKey',
          'templateName',
          'templateType',
          'templateId',
          'sandboxTemplateId',
        ],
        order: { templateKey: 'ASC' },
      });
    } catch (error) {
      this.logger.error(
        `Error finding master summaries by programTypeId ${programTypeId}: ${error.message}`,
        error.stack,
      );
      handleKnownErrors(ERROR_CODES.TEMPLATE_NOT_FOUND, error);
    }
  }

  /**
   * Find all active master templates by programTypeId and usage type
   */
  async findAllByProgramTypeId(
    programTypeId: number,
    templateUsageType?: string,
  ): Promise<CommunicationTemplatesMaster[]> {
    try {
      const where: any = { isActive: true, programTypeId };
      if (templateUsageType) {
        where.templateUsageType = templateUsageType;
      }
      return await this.repository.find({
        where,
        order: { templateKey: 'ASC' },
      });
    } catch (error) {
      this.logger.error(
        `Error finding master templates by programTypeId ${programTypeId}: ${error.message}`,
        error.stack,
      );
      handleKnownErrors(ERROR_CODES.TEMPLATE_NOT_FOUND, error);
    }
  }

  /**
   * Find all active master templates
   */
  async findAll(limit: number = 50, offset: number = 0): Promise<any> {
    try {
      const [templates, total] = await this.repository.findAndCount({
        where: { isActive: true },
        take: limit,
        skip: offset,
        order: { createdAt: 'DESC' },
      });

      return {
        data: templates,
        pagination: {
          totalPages: Math.ceil(total / limit),
          pageNumber: Math.floor(offset / limit) + 1,
          pageSize: limit,
          totalRecords: total,
          numberOfRecords: templates.length,
        },
      };
    } catch (error) {
      this.logger.error(`Error fetching all master templates: ${error.message}`, error.stack);
      handleKnownErrors(ERROR_CODES.TEMPLATE_NOT_FOUND, error);
    }
  }

  /**
   * Find master template by ID
   */
  async findById(id: number): Promise<CommunicationTemplatesMaster | null> {
    try {
      return await this.repository.findOne({
        where: { id },
      });
    } catch (error) {
      this.logger.error(`Error finding master template by ID ${id}: ${error.message}`, error.stack);
      handleKnownErrors(ERROR_CODES.TEMPLATE_NOT_FOUND, error);
    }
  }

  /**
   * Find master template by template key
   */
  async findByTemplateKey(
    templateKey: string,
  ): Promise<CommunicationTemplatesMaster | null> {
    try {
      return await this.repository.findOne({
        where: { templateKey, isActive: true },
      });
    } catch (error) {
      this.logger.error(
        `Error finding master template by key ${templateKey}: ${error.message}`,
        error.stack,
      );
      handleKnownErrors(ERROR_CODES.TEMPLATE_NOT_FOUND, error);
    }
  }

  /**
   * Find master template by key and type
   */
  async findByTemplateKeyAndType(
    templateKey: string,
    templateType: CommunicationTypeEnum,
  ): Promise<CommunicationTemplatesMaster | null> {
    try {
      return await this.repository.findOne({
        where: { templateKey, templateType, isActive: true },
      });
    } catch (error) {
      this.logger.error(
        `Error finding master template by key ${templateKey} and type ${templateType}: ${error.message}`,
        error.stack,
      );
      handleKnownErrors(ERROR_CODES.TEMPLATE_NOT_FOUND, error);
    }
  }

  /**
   * Find all master templates by type
   */
  async findByType(templateType: CommunicationTypeEnum): Promise<CommunicationTemplatesMaster[]> {
    try {
      return await this.repository.find({
        where: { templateType, isActive: true },
        order: { templateName: 'ASC' },
      });
    } catch (error) {
      this.logger.error(
        `Error finding master templates by type ${templateType}: ${error.message}`,
        error.stack,
      );
      handleKnownErrors(ERROR_CODES.TEMPLATE_NOT_FOUND, error);
    }
  }

  /**
   * Create a new master template
   */
  async create(
    data: Partial<CommunicationTemplatesMaster>,
  ): Promise<CommunicationTemplatesMaster> {
    try {
      const template = this.repository.create(data);
      return await this.repository.save(template);
    } catch (error) {
      this.logger.error(`Error creating master template: ${error.message}`, error.stack);
      handleKnownErrors(ERROR_CODES.TEMPLATE_CREATE_FAILED, error);
    }
  }

  /**
   * Update a master template
   */
  async update(
    id: number,
    data: Partial<CommunicationTemplatesMaster>,
  ): Promise<CommunicationTemplatesMaster | null> {
    try {
      const { programType, ...updateData } = data;
      await this.repository.update(id, updateData as any);
      return await this.findById(id);
    } catch (error) {
      this.logger.error(`Error updating master template ${id}: ${error.message}`, error.stack);
      handleKnownErrors(ERROR_CODES.TEMPLATE_UPDATE_FAILED, error);
    }
  }

  /**
   * Soft delete a master template (set isActive to false)
   */
  async softDelete(id: number): Promise<void> {
    try {
      await this.repository.update(id, { isActive: false });
    } catch (error) {
      this.logger.error(`Error soft deleting master template ${id}: ${error.message}`, error.stack);
      handleKnownErrors(ERROR_CODES.TEMPLATE_DELETE_FAILED, error);
    }
  }

  /**
   * Get all templates with their merge field maps
   */
  async findAllWithMergeFields(): Promise<CommunicationTemplatesMaster[]> {
    try {
      return await this.repository.find({
        where: { isActive: true },
        order: { templateKey: 'ASC' },
      });
    } catch (error) {
      this.logger.error(
        `Error finding all master templates with merge fields: ${error.message}`,
        error.stack,
      );
      handleKnownErrors(ERROR_CODES.TEMPLATE_NOT_FOUND, error);
    }
  }

  /**
   * Validate that every id references an active master row whose program_type_id
   * matches the given program type. Returns the ids that are NOT valid.
   */
  async findInvalidMasterIdsForProgramType(
    programTypeId: number,
    masterTemplateIds: number[],
  ): Promise<number[]> {
    try {
      if (!masterTemplateIds || masterTemplateIds.length === 0) {
        return [];
      }
      const rows = await this.repository
        .createQueryBuilder('master')
        .select('master.id', 'id')
        .where('master.id IN (:...ids)', { ids: masterTemplateIds })
        .andWhere('master.programTypeId = :programTypeId', { programTypeId })
        .andWhere('master.isActive = true')
        .getRawMany();
      const validIds = new Set(rows.map((row) => Number(row.id)));
      return masterTemplateIds.filter((id) => !validIds.has(Number(id)));
    } catch (error) {
      this.logger.error(
        `Error validating master ids for programTypeId ${programTypeId}: ${error.message}`,
        error.stack,
      );
      handleKnownErrors(ERROR_CODES.TEMPLATE_NOT_FOUND, error);
    }
  }
}
