import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, IsNull, ILike, FindOptionsWhere } from 'typeorm';
import { TemplateFormSection } from 'src/common/entities/template-form-section.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 { CreateTemplateFormSectionDto } from './dto/create-template-form-section.dto';
import { UpdateTemplateFormSectionDto } from './dto/update-template-form-section.dto';
import { FilterTemplateFormSectionDto } from './dto/filter-template-form-section.dto';

@Injectable()
export class TemplateFormSectionRepository {
  constructor(
    @InjectRepository(TemplateFormSection)
    private readonly templateFormSectionRepo: Repository<TemplateFormSection>,
    private readonly commonDataService: CommonDataService,
  ) {}

  /**
   * Find all template form sections with pagination and filters
   */
  async findAll(filters: FilterTemplateFormSectionDto) {
    try {
      const { limit = 10, offset = 0, searchText, programTemplateId, parentSectionId, masterFormSectionId } = filters;

      const selectFields: (keyof TemplateFormSection)[] = [
        'id',
        'programTemplateId',
        'masterFormSectionId',
        'sectionKey',
        'name',
        'description',
        'parentSectionId',
        'conditionalConfig',
        'displayOrder',
        'createdAt',
        'updatedAt',
        'createdBy',
        'updatedBy',
      ];

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

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

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

      if (parentSectionId !== undefined) {
        whereClause.parentSectionId = parentSectionId === null ? IsNull() : parentSectionId;
      }

      if (masterFormSectionId !== undefined) {
        whereClause.masterFormSectionId = masterFormSectionId === null ? IsNull() : masterFormSectionId;
      }

      const data = await this.commonDataService.get(
        this.templateFormSectionRepo,
        selectFields,
        whereClause,
        limit,
        offset,
        { displayOrder: 'ASC', id: 'ASC' },
      );

      const total = await this.templateFormSectionRepo.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.TEMPLATE_FORM_SECTION_FIND_FAILED, error);
    }
  }

  /**
   * Find a template form section by ID (without questions - loaded separately)
   */
  async findById(id: number): Promise<TemplateFormSection | null> {
    try {
      return await this.templateFormSectionRepo.findOne({
        where: { id, deletedAt: IsNull() },
        relations: ['parentSection'],
      });
    } catch (error) {
      handleKnownErrors(ERROR_CODES.TEMPLATE_FORM_SECTION_FIND_BY_ID_FAILED, error);
    }
  }

  /**
   * Find template sections by program template ID
   */
  async findByProgramTemplateId(programTemplateId: number): Promise<TemplateFormSection[]> {
    try {
      return await this.templateFormSectionRepo.find({
        where: { programTemplateId, deletedAt: IsNull() },
        order: { displayOrder: 'ASC', id: 'ASC' },
      });
    } catch (error) {
      handleKnownErrors(ERROR_CODES.TEMPLATE_FORM_SECTION_FIND_FAILED, error);
    }
  }

  /**
   * Find template section by section key and program template
   */
  async findBySectionKeyAndTemplate(sectionKey: string, programTemplateId: number): Promise<TemplateFormSection | null> {
    try {
      return await this.templateFormSectionRepo.findOne({
        where: { sectionKey, programTemplateId, deletedAt: IsNull() },
      });
    } catch (error) {
      handleKnownErrors(ERROR_CODES.TEMPLATE_FORM_SECTION_FIND_FAILED, error);
    }
  }

  /**
   * Check if master section already cloned to template
   */
  async findByMasterSectionAndTemplate(masterFormSectionId: number, programTemplateId: number): Promise<TemplateFormSection | null> {
    try {
      return await this.templateFormSectionRepo.findOne({
        where: { masterFormSectionId, programTemplateId, deletedAt: IsNull() },
      });
    } catch (error) {
      handleKnownErrors(ERROR_CODES.TEMPLATE_FORM_SECTION_FIND_FAILED, error);
    }
  }

  /**
   * Find children of a template section
   */
  async findChildren(parentId: number): Promise<TemplateFormSection[]> {
    try {
      return await this.templateFormSectionRepo
        .createQueryBuilder('tfs')
        .select([
          'tfs.id',
          'tfs.programTemplateId',
          'tfs.masterFormSectionId',
          'tfs.sectionKey',
          'tfs.name',
          'tfs.description',
          'tfs.parentSectionId',
          'tfs.conditionalConfig',
          'tfs.displayOrder',
          'tfs.createdAt',
          'tfs.updatedAt'
        ])
        .where('tfs.parentSectionId = :parentId', { parentId })
        .andWhere('tfs.deletedAt IS NULL')
        .orderBy('tfs.displayOrder', 'ASC')
        .addOrderBy('tfs.id', 'ASC')
        .getMany();
    } catch (error) {
      handleKnownErrors(ERROR_CODES.TEMPLATE_FORM_SECTION_FIND_FAILED, error);
    }
  }

  /**
   * Get ancestors of a template section (breadcrumb)
   */
  async findAncestors(sectionId: number): Promise<TemplateFormSection[]> {
    try {
      const ancestors: TemplateFormSection[] = [];
      let currentSection = await this.findById(sectionId);

      while (currentSection?.parentSectionId) {
        const parent = await this.findById(currentSection.parentSectionId);
        if (parent) {
          ancestors.unshift(parent);
          currentSection = parent;
        } else {
          break;
        }
      }

      return ancestors;
    } catch (error) {
      handleKnownErrors(ERROR_CODES.TEMPLATE_FORM_SECTION_FIND_FAILED, error);
    }
  }

  /**
   * Get hierarchy tree for a specific program template
   */
  async findHierarchyByTemplate(programTemplateId: number): Promise<TemplateFormSection[]> {
    try {
      // Get all root sections (no parent) for this template
      const rootSections = await this.templateFormSectionRepo
        .createQueryBuilder('tfs')
        .select([
          'tfs.id',
          'tfs.programTemplateId',
          'tfs.masterFormSectionId',
          'tfs.sectionKey',
          'tfs.name',
          'tfs.description',
          'tfs.parentSectionId',
          'tfs.conditionalConfig',
          'tfs.displayOrder',
          'tfs.createdAt',
          'tfs.updatedAt'
        ])
        .where('tfs.programTemplateId = :programTemplateId', { programTemplateId })
        .andWhere('tfs.parentSectionId IS NULL')
        .andWhere('tfs.deletedAt IS NULL')
        .orderBy('tfs.displayOrder', 'ASC')
        .getMany();

      // Recursively build hierarchy
      const buildHierarchy = async (section: TemplateFormSection) => {
        const children = await this.findChildren(section.id);
        return {
          ...section,
          children: await Promise.all(children.map(buildHierarchy)),
        };
      };

      return await Promise.all(rootSections.map(buildHierarchy));
    } catch (error) {
      handleKnownErrors(ERROR_CODES.TEMPLATE_FORM_SECTION_FIND_FAILED, error);
    }
  }

  /**
   * Get next display order for a program template
   */
  async getNextDisplayOrder(programTemplateId: number, parentSectionId?: number): Promise<number> {
    try {
      const whereClause: FindOptionsWhere<TemplateFormSection> = {
        programTemplateId,
        deletedAt: IsNull(),
      };

      if (parentSectionId) {
        whereClause.parentSectionId = parentSectionId;
      } else {
        whereClause.parentSectionId = IsNull();
      }

      const maxOrder = await this.templateFormSectionRepo.findOne({
        where: whereClause,
        order: { displayOrder: 'DESC' },
        select: ['displayOrder'],
      });

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

  /**
   * Create a template form section
   */
  async create(createDto: CreateTemplateFormSectionDto): Promise<TemplateFormSection> {
    try {
      const section = this.templateFormSectionRepo.create(createDto);
      return await this.templateFormSectionRepo.save(section);
    } catch (error) {
      handleKnownErrors(ERROR_CODES.TEMPLATE_FORM_SECTION_CREATE_FAILED, error);
    }
  }

  /**
   * Bulk create template sections (for cloning)
   */
  async bulkCreate(sections: CreateTemplateFormSectionDto[]): Promise<TemplateFormSection[]> {
    try {
      const entities = this.templateFormSectionRepo.create(sections);
      return await this.templateFormSectionRepo.save(entities);
    } catch (error) {
      handleKnownErrors(ERROR_CODES.TEMPLATE_FORM_SECTION_CREATE_FAILED, error);
    }
  }

  /**
   * Update a template form section
   */
  async update(id: number, updateDto: UpdateTemplateFormSectionDto): Promise<TemplateFormSection> {
    try {
      await this.templateFormSectionRepo.update(id, updateDto);
      const updated = await this.findById(id);
      if (!updated) {
        throw new Error('Section not found after update');
      }
      return updated;
    } catch (error) {
      handleKnownErrors(ERROR_CODES.TEMPLATE_FORM_SECTION_UPDATE_FAILED, error);
    }
  }

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