import { Injectable } from '@nestjs/common';
import { DataSource, EntityManager, IsNull, In } from 'typeorm';
import { TemplateFormSectionRepository } from './template-form-section.repository';
import { TemplateQuestionRepository } from 'src/template-question/template-question.repository';
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';
import { CloneFromMasterDto } from './dto/clone-from-master.dto';
import { TemplateFormBuilderDto, SectionInputDto, QuestionInputDto } from './dto/template-form-builder.dto';
import { InifniNotFoundException } from 'src/common/exceptions/infini-notfound-exception';
import InifniBadRequestException from 'src/common/exceptions/infini-badrequest-exception';
import { InifniInternalServerErrorException } from 'src/common/exceptions/infini-internalservererror-exception';
import { ERROR_CODES } from 'src/common/constants/error-string-constants';
import { programTemplateConstMessages } from 'src/common/constants/strings-constants';
import { AppLoggerService } from 'src/common/services/logger.service';
import { MasterFormSectionRepository } from 'src/master-form-section/master-form-section.repository';
import { MasterFormSection } from 'src/common/entities/master-form-section.entity';
import { TemplateFormSection } from 'src/common/entities/template-form-section.entity';
import { TemplateQuestion } from 'src/common/entities/template-question.entity';
import { MasterQuestion } from 'src/common/entities';
import { MasterQuestionRepository } from 'src/master-question/master-question.repository';
import { ProgramTemplateRepository } from 'src/program-template/program-template.repository';
import {
  MAX_LIMIT_FOR_NESTED_SUBSECTIONS,
  MAX_SECTION_NESTING_DEPTH,
} from 'src/common/constants/constants';
import { handleKnownErrors } from 'src/common/utils/handle-error.util';

@Injectable()
export class TemplateFormSectionService {
  constructor(
    private readonly templateFormSectionRepo: TemplateFormSectionRepository,
    private readonly templateQuestionRepo: TemplateQuestionRepository,
    private readonly masterFormSectionRepo: MasterFormSectionRepository,
    private readonly masterQuestionRepo: MasterQuestionRepository,
    private readonly programTemplateRepo: ProgramTemplateRepository,
    private readonly logger: AppLoggerService,
    private readonly dataSource: DataSource,
  ) {}

  /**
   * Get all template form sections with filters
   */
  async findAll(filters: FilterTemplateFormSectionDto) {
    try {
      this.logger.log('Finding all template form sections', { filters });
      return await this.templateFormSectionRepo.findAll(filters);
    } catch (error) {
      this.logger.error('Error finding template form sections', error);
      handleKnownErrors(ERROR_CODES.TEMPLATE_FORM_SECTION_FIND_FAILED, error);
    }
  }

  /**
   * Get a single template form section by ID with questions
   */
  async findById(id: number) {
    try {
      this.logger.log('Finding template form section by ID', { id });
      const section = await this.templateFormSectionRepo.findById(id);

      if (!section) {
        handleKnownErrors(
          ERROR_CODES.TEMPLATE_FORM_SECTION_NOT_FOUND,
          new Error(`Template form section not found: ${id}`)
        );
      }

      // Load questions for this section
      const questions = await this.templateQuestionRepo.findByTemplateSection(section.id);
      section['questions'] = questions;

      return section;
    } catch (error) {
      this.logger.error('Error finding template form section by ID', error);
      handleKnownErrors(ERROR_CODES.TEMPLATE_FORM_SECTION_FIND_BY_ID_FAILED, error);
    }
  }

  /**
   * Get section hierarchy for a program template with questions
   */
  async getHierarchyByTemplate(programTemplateId: number) {
    try {
      this.logger.log('Getting template form section hierarchy', { programTemplateId });
      const hierarchy =
        await this.templateFormSectionRepo.findHierarchyByTemplate(programTemplateId);

      // Recursively load questions for each section in the hierarchy
      const loadQuestionsForSection = async (section: any) => {
        const questions = await this.templateQuestionRepo.findByTemplateSection(section.id);
        section.questions = questions;

        if (section.children && section.children.length > 0) {
          for (const child of section.children) {
            await loadQuestionsForSection(child);
          }
        }
      };

      // Load questions for all root sections and their children
      for (const section of hierarchy) {
        await loadQuestionsForSection(section);
      }

      return hierarchy;
    } catch (error) {
      this.logger.error('Error getting section hierarchy', error);
      handleKnownErrors(ERROR_CODES.TEMPLATE_FORM_SECTION_FIND_FAILED, error);
    }
  }

  /**
   * Get subsections of a template section with questions
   */
  async getChildren(parentId: number) {
    try {
      this.logger.log('Getting subsections of template section', { parentId });

      // Verify parent exists
      await this.findById(parentId);

      const children = await this.templateFormSectionRepo.findChildren(parentId);

      // Load questions for each child section
      for (const child of children) {
        const questions = await this.templateQuestionRepo.findByTemplateSection(child.id);
        child['questions'] = questions;
      }

      return children;
    } catch (error) {
      this.logger.error('Error getting section subsections', error);
      handleKnownErrors(ERROR_CODES.TEMPLATE_FORM_SECTION_FIND_FAILED, error);
    }
  }

  /**
   * Get ancestors of a template section (breadcrumb)
   */
  async getAncestors(sectionId: number) {
    try {
      this.logger.log('Getting ancestors of template section', { sectionId });

      const section = await this.findById(sectionId);
      const ancestors = await this.templateFormSectionRepo.findAncestors(sectionId);

      return {
        section,
        ancestors,
        breadcrumb: [...ancestors.map((a) => a.name), section.name],
      };
    } catch (error) {
      this.logger.error('Error getting section ancestors', error);
      handleKnownErrors(ERROR_CODES.TEMPLATE_FORM_SECTION_FIND_FAILED, error);
    }
  }

  /**
   * Create a template form section
   */
  async create(createDto: CreateTemplateFormSectionDto) {
    try {
      this.logger.log('Creating template form section', { createDto });

      // Check if section key already exists in this template
      const existingSection = await this.templateFormSectionRepo.findBySectionKeyAndTemplate(
        createDto.sectionKey,
        createDto.programTemplateId,
      );

      if (existingSection) {
        handleKnownErrors(
          ERROR_CODES.TEMPLATE_FORM_SECTION_DUPLICATE_KEY,
          new Error(`Duplicate section key: ${createDto.sectionKey}`)
        );
      }

      // Auto-assign display order if not provided
      if (!createDto.displayOrder) {
        createDto.displayOrder = await this.templateFormSectionRepo.getNextDisplayOrder(
          createDto.programTemplateId,
          createDto.parentSectionId,
        );
      }

      const section = await this.templateFormSectionRepo.create(createDto);
      this.logger.log('Template form section created successfully', { sectionId: section.id });

      return section;
    } catch (error) {
      this.logger.error('Error creating template form section', error);
      handleKnownErrors(ERROR_CODES.TEMPLATE_FORM_SECTION_CREATE_FAILED, error);
    }
  }

  /**
   * Update a template form section
   */
  async update(id: number, updateDto: UpdateTemplateFormSectionDto) {
    try {
      this.logger.log('Updating template form section', { id, updateDto });

      // Verify section exists
      const section = await this.findById(id);

      // If updating parent, validate to prevent circular references
      if (updateDto.parentSectionId !== undefined) {
        // Cannot set self as parent
        if (updateDto.parentSectionId === id) {
          handleKnownErrors(
            ERROR_CODES.TEMPLATE_FORM_SECTION_CIRCULAR_REFERENCE,
            new Error(`Cannot set section ${id} as its own parent`)
          );
        }

        // If setting a parent (not null), validate it exists and prevent ancestor loop
        if (updateDto.parentSectionId !== null) {
          const targetParent = await this.templateFormSectionRepo.findById(
            updateDto.parentSectionId,
          );

          if (!targetParent) {
            handleKnownErrors(
              ERROR_CODES.TEMPLATE_FORM_SECTION_NOT_FOUND,
              new Error(`Parent section not found: ${updateDto.parentSectionId}`)
            );
          }

          // Ensure target parent is in the same template
          if (targetParent.programTemplateId !== section.programTemplateId) {
            handleKnownErrors(
              ERROR_CODES.TEMPLATE_FORM_SECTION_INVALID_PARENT,
              new Error('Parent section must be in the same template')
            );
          }

          // Check if target parent is a descendant of current section (would create circular reference)
          const isDescendant = await this.isDescendantOf(updateDto.parentSectionId, id);
          if (isDescendant) {
            handleKnownErrors(
              ERROR_CODES.TEMPLATE_FORM_SECTION_CIRCULAR_REFERENCE,
              new Error('Cannot create circular reference in section hierarchy')
            );
          }
        }
      }

      // If updating section key, check uniqueness
      if (updateDto.sectionKey) {
        const existingSection = await this.templateFormSectionRepo.findBySectionKeyAndTemplate(
          updateDto.sectionKey,
          section.programTemplateId,
        );

        if (existingSection && existingSection.id !== id) {
          handleKnownErrors(
            ERROR_CODES.TEMPLATE_FORM_SECTION_DUPLICATE_KEY,
            new Error(`Duplicate section key: ${updateDto.sectionKey}`)
          );
        }
      }

      const updated = await this.templateFormSectionRepo.update(id, updateDto);
      this.logger.log('Template form section updated successfully', { sectionId: id });

      return updated;
    } catch (error) {
      this.logger.error('Error updating template form section', error);
      handleKnownErrors(ERROR_CODES.TEMPLATE_FORM_SECTION_UPDATE_FAILED, error);
    }
  }

  /**
   * Check if sectionId is a descendant of potentialAncestorId
   */
  private async isDescendantOf(sectionId: number, potentialAncestorId: number): Promise<boolean> {
    let current = await this.templateFormSectionRepo.findById(sectionId);

    while (current?.parentSectionId) {
      if (current.parentSectionId === potentialAncestorId) {
        return true;
      }
      current = await this.templateFormSectionRepo.findById(current.parentSectionId);
    }

    return false;
  }

  /**
   * Delete a template form section
   */
  async delete(id: number) {
    try {
      this.logger.log('Deleting template form section', { id });

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

      // Check if section has children
      const children = await this.templateFormSectionRepo.findChildren(id);
      if (children.length > 0) {
        handleKnownErrors(
          ERROR_CODES.TEMPLATE_FORM_SECTION_HAS_CHILDREN,
          new Error(`Cannot delete section ${id} that has children`)
        );
      }

      await this.templateFormSectionRepo.softDelete(id);
      this.logger.log('Template form section deleted successfully', { sectionId: id });

      return { deleted: true };
    } catch (error) {
      this.logger.error('Error deleting template form section', error);
      handleKnownErrors(ERROR_CODES.TEMPLATE_FORM_SECTION_DELETE_FAILED, error);
    }
  }

  /**
   * Clone master section(s) to template level
   * If masterFormSectionId is not provided, clones all root sections
   */
  async cloneFromMaster(cloneDto: CloneFromMasterDto) {
    try {
      return await this.dataSource.transaction(async (manager) => {
        this.logger.log('Starting clone from master', { cloneDto });

        // Validate that programTemplateId is provided
        if (!cloneDto.programTemplateId) {
          handleKnownErrors(
            ERROR_CODES.PROGRAM_TEMPLATE_ID_REQUIRED,
            new Error('Program template ID is required')
          );
        }

        // Validate that the program template exists
        const programTemplate = await this.programTemplateRepo.findById(cloneDto.programTemplateId);
        if (!programTemplate) {
          handleKnownErrors(
            ERROR_CODES.PROGRAM_TEMPLATE_NOT_FOUND,
            new Error(`Program template not found: ${cloneDto.programTemplateId}`)
          );
        }

        this.logger.log('Program template validated', {
          programTemplateId: cloneDto.programTemplateId,
          templateName: programTemplate.name,
          templateStatus: programTemplate.status,
        });

        // Determine which master sections to clone
        let masterSectionsToClone: MasterFormSection[] = [];

        if (cloneDto.masterFormSectionId) {
          // 1. Clone specific master section
          const masterSection = await this.masterFormSectionRepo.findById(
            cloneDto.masterFormSectionId,
          );
          if (!masterSection) {
            handleKnownErrors(
              ERROR_CODES.MASTER_FORM_SECTION_NOT_FOUND,
              new Error(`Master form section not found: ${cloneDto.masterFormSectionId}`)
            );
          }
          masterSectionsToClone = [masterSection];
        } else if (cloneDto.specificSectionIds && cloneDto.specificSectionIds.length > 0) {
          // 2. Clone specific sections by IDs (treat them as individual roots)
          this.logger.log('Cloning specific master sections', {
            specificSectionIds: cloneDto.specificSectionIds,
          });

          for (const sectionId of cloneDto.specificSectionIds) {
            const masterSection = await this.masterFormSectionRepo.findById(sectionId);
            if (masterSection) {
              masterSectionsToClone.push(masterSection);
            } else {
              this.logger.warn('Master section not found, skipping', { sectionId });
            }
          }

          if (masterSectionsToClone.length === 0) {
            handleKnownErrors(
              ERROR_CODES.MASTER_FORM_SECTION_NOT_FOUND,
              new Error(`None of the specified master sections were found: ${cloneDto.specificSectionIds.join(', ')}`)
            );
          }

          this.logger.log('Found specific sections to clone', {
            count: masterSectionsToClone.length,
            ids: masterSectionsToClone.map((s) => s.id),
            cloningMode: cloneDto.deepClone ? 'with_children' : 'flat_without_children',
          });
        } else {
          // 3. Clone all root sections (sections with no parent)
          this.logger.log(
            'No masterFormSectionId or specificSectionIds provided - cloning all root sections',
          );
          const allRootSections = await manager.find(MasterFormSection, {
            where: {
              parentSectionId: IsNull(),
              deletedAt: IsNull(),
            },
          });

          if (!allRootSections || allRootSections.length === 0) {
            handleKnownErrors(
              ERROR_CODES.MASTER_FORM_SECTION_NOT_FOUND,
              new Error('No root sections found in master')
            );
          }

          masterSectionsToClone = allRootSections;
          this.logger.log('Found root sections to clone', { count: allRootSections.length });
        }

        const allClonedSections: TemplateFormSection[] = [];

        // Collect all manually specified display orders from sectionOverrides to avoid conflicts
        const manuallyAssignedSectionOrders = new Set<number>();
        if (cloneDto.sectionOverrides && cloneDto.sectionOverrides.length > 0) {
          for (const override of cloneDto.sectionOverrides) {
            if (override.displayOrder !== undefined) {
              manuallyAssignedSectionOrders.add(override.displayOrder);
            }
          }
          this.logger.log('Manual section display orders detected', {
            manualOrders: Array.from(manuallyAssignedSectionOrders).sort((a, b) => a - b),
          });
        }

        // Helper to get next available display order (skipping manually assigned ones)
        const getNextAvailableSectionOrder = (currentOrder: number): number => {
          while (manuallyAssignedSectionOrders.has(currentOrder)) {
            currentOrder++;
          }
          return currentOrder;
        };

        // Track auto display order counter
        let autoSectionOrderCounter = 1;

        // 2. Process each master section
        for (let i = 0; i < masterSectionsToClone.length; i++) {
          const masterSection = masterSectionsToClone[i];

          // Check if this section has a manual displayOrder override
          const masterSectionIdNum =
            typeof masterSection.id === 'string' ? parseInt(masterSection.id) : masterSection.id;
          const sectionOverride = cloneDto.sectionOverrides?.find(
            (o) => o.masterSectionId === masterSectionIdNum,
          );

          // Calculate display order (manual override or auto-calculated)
          let desiredDisplayOrder: number;
          if (sectionOverride?.displayOrder !== undefined) {
            desiredDisplayOrder = sectionOverride.displayOrder;
          } else {
            desiredDisplayOrder = getNextAvailableSectionOrder(autoSectionOrderCounter);
            autoSectionOrderCounter = desiredDisplayOrder + 1;
          }

          // Check if already cloned (for selective cloning, always include the root even if cloned)
          const existingClone = await this.templateFormSectionRepo.findByMasterSectionAndTemplate(
            masterSection.id,
            cloneDto.programTemplateId,
          );

          if (existingClone && !cloneDto.override) {
            // For selective cloning (specificSectionIds provided), always include the root
            // but skip already-cloned descendants unless override is true
            if (!cloneDto.specificSectionIds || cloneDto.specificSectionIds.length === 0) {
              if (cloneDto.masterFormSectionId) {
                // Single section mode - throw error
                handleKnownErrors(
                  ERROR_CODES.TEMPLATE_FORM_SECTION_ALREADY_CLONED,
                  new Error(`Section ${masterSection.id} already cloned to template ${cloneDto.programTemplateId}`)
                );
              } else {
                // Multi-section mode - skip this one
                this.logger.log('Section already cloned, skipping', {
                  masterSectionId: masterSection.id,
                  existingCloneId: existingClone.id,
                });
                continue;
              }
            }
            // Continue with selective cloning - root will be included
            this.logger.log(
              'Root section already cloned, but continuing with selective cloning of descendants',
              {
                rootSectionId: masterSection.id,
                existingCloneId: existingClone.id,
              },
            );
          }

          if (existingClone && cloneDto.override) {
            // Delete existing clone and all its descendants
            this.logger.log('Override flag set - deleting existing clones', {
              existingCloneId: existingClone.id,
              masterSectionId: masterSection.id,
            });
            await this.deleteTemplateSectionWithDescendants(existingClone.id, manager);
          }

          // 3. Clone the section hierarchy
          // Only filter by specificSectionIds if masterFormSectionId was provided
          // When cloning specific sections as independent roots:
          // - deepClone: true → clone with their children
          // - deepClone: false → clone as flat sections without children
          const shouldFilterDescendants = cloneDto.masterFormSectionId !== undefined;

          const clonedSections = await this.cloneSectionHierarchy(
            masterSection,
            cloneDto.programTemplateId,
            null, // Root has no parent
            cloneDto.deepClone ?? true, // Respect the deepClone flag
            shouldFilterDescendants ? cloneDto.specificSectionIds : undefined,
            cloneDto.createdBy ?? -1,
            manager,
            cloneDto.sectionOverrides ?? [],
            desiredDisplayOrder, // Pass array position as display order
          );

          allClonedSections.push(...clonedSections);
        }

        // 4. Clone questions if requested
        if (cloneDto.cloneQuestions) {
          this.logger.log('Cloning questions for sections', {
            sectionsCount: allClonedSections.length,
            specificQuestionIds: cloneDto.specificQuestionIds,
          });
          await this.cloneQuestionsForSections(
            allClonedSections,
            cloneDto.createdBy ?? -1,
            manager,
            cloneDto.questionOverrides ?? [],
            cloneDto.specificQuestionIds,
          );
        }

        this.logger.log('Clone from master completed successfully', {
          masterSectionIds: masterSectionsToClone.map((s) => s.id),
          clonedSectionsCount: allClonedSections.length,
          questionsCloned: cloneDto.cloneQuestions,
        });

        return {
          success: true,
          clonedSections: allClonedSections.length,
          sections: allClonedSections,
          message: cloneDto.cloneQuestions
            ? 'Sections and questions cloned successfully'
            : 'Sections cloned successfully. Use clone-questions endpoint to clone questions separately if needed.',
        };
      });
    } catch (error) {
      this.logger.error('Error cloning from master', error);
      handleKnownErrors(ERROR_CODES.TEMPLATE_FORM_SECTION_CLONE_FAILED, error);
    }
  }

  /**
   * Recursively clone section hierarchy
   */
  private async cloneSectionHierarchy(
    masterSection: MasterFormSection,
    programTemplateId: number,
    parentTemplateSectionId: number | null,
    deepClone: boolean,
    specificSectionIds: number[] | undefined,
    createdBy: number,
    manager: any,
    sectionOverrides: Array<{
      masterSectionId: number;
      name?: string;
      description?: string;
      displayOrder?: number;
      parentSectionId?: number | null;
      conditionalConfig?: any;
    }>,
    desiredDisplayOrder?: number, // Display order based on array position
  ): Promise<TemplateFormSection[]> {
    const clonedSections: TemplateFormSection[] = [];

    try {
      // Check if this section is already cloned (to prevent duplicates in descendants)
      const existingClone = await manager.findOne(TemplateFormSection, {
        where: {
          masterFormSectionId: masterSection.id,
          programTemplateId,
          deletedAt: null,
        },
      });

      if (existingClone) {
        // Skip this already-cloned section but continue with its children if needed
        this.logger.log('Section already cloned, skipping', {
          masterSectionId: masterSection.id,
          existingCloneId: existingClone.id,
        });

        // Add to result so parent tracking works
        clonedSections.push(existingClone);

        // Continue with children if deep clone
        if (deepClone) {
          const children = await this.masterFormSectionRepo.findChildren(masterSection.id);

          for (const child of children) {
            if (specificSectionIds && !specificSectionIds.includes(child.id)) {
              continue;
            }

            const childClones = await this.cloneSectionHierarchy(
              child,
              programTemplateId,
              existingClone.id, // Use existing clone as parent
              deepClone,
              specificSectionIds,
              createdBy,
              manager,
              sectionOverrides,
            );

            clonedSections.push(...childClones);
          }
        }

        return clonedSections;
      }

      // Generate unique section key (transaction-aware)
      const sectionKey = await this.generateUniqueSectionKey(
        masterSection.sectionKey,
        programTemplateId,
        manager,
      );

      // Find if there are any overrides for this specific section
      // Handle bigint ID conversion (database returns string, DTO has number)
      const masterSectionIdNum =
        typeof masterSection.id === 'string' ? parseInt(masterSection.id) : masterSection.id;
      const overridesForThisSection = sectionOverrides.find(
        (o) => o.masterSectionId === masterSectionIdNum,
      );

      if (overridesForThisSection) {
        this.logger.log('Applying section overrides', {
          masterSectionId: masterSectionIdNum,
          overrides: overridesForThisSection,
        });
      }

      // Determine parent section ID (with override support)
      let finalParentSectionId: number | undefined = parentTemplateSectionId ?? undefined;
      if (overridesForThisSection?.parentSectionId !== undefined) {
        // Override specified - use it (null becomes undefined for DTO)
        finalParentSectionId = overridesForThisSection.parentSectionId ?? undefined;
        this.logger.log('Parent section ID overridden', {
          masterSectionId: masterSectionIdNum,
          originalParent: parentTemplateSectionId,
          overriddenParent: finalParentSectionId,
        });
      }

      // Get display order (priority: override > desiredDisplayOrder > auto-calculated)
      let displayOrder: number;
      if (overridesForThisSection?.displayOrder !== undefined) {
        displayOrder = overridesForThisSection.displayOrder;
      } else if (desiredDisplayOrder !== undefined) {
        displayOrder = desiredDisplayOrder;
      } else {
        displayOrder = await this.templateFormSectionRepo.getNextDisplayOrder(
          programTemplateId,
          finalParentSectionId,
        );
      }

      // Validate nesting depth if MAX_SECTION_NESTING_DEPTH is configured and this is a nested section
      if (
        finalParentSectionId &&
        MAX_SECTION_NESTING_DEPTH !== null &&
        MAX_SECTION_NESTING_DEPTH !== undefined
      ) {
        const currentDepth = await this.calculateSectionDepth(
          finalParentSectionId,
          manager,
          'template',
        );

        if (currentDepth >= MAX_SECTION_NESTING_DEPTH) {
          handleKnownErrors(
            ERROR_CODES.FORM_SECTION_NESTING_DEPTH_EXCEEDED,
            new Error(`Section nesting depth limit exceeded for template. Maximum allowed depth is ${MAX_SECTION_NESTING_DEPTH} level(s), but attempting to clone section at depth ${currentDepth + 1}.`)
          );
        }
      }

      // Create template section with overrides applied (if any exist for this section)
      const now = new Date();
      const templateSectionDto: CreateTemplateFormSectionDto = {
        programTemplateId,
        masterFormSectionId: masterSection.id,
        sectionKey,
        name: overridesForThisSection?.name ?? masterSection.name,
        description: overridesForThisSection?.description ?? masterSection.description ?? undefined,
        parentSectionId: finalParentSectionId,
        conditionalConfig:
          overridesForThisSection?.conditionalConfig ??
          masterSection.conditionalConfig ??
          undefined,
        displayOrder,
        createdBy,
      };

      const clonedSection = await manager.save(TemplateFormSection, {
        ...templateSectionDto,
        createdAt: now,
        updatedAt: now,
      });

      clonedSections.push(clonedSection);

      // Clone children if deep clone enabled
      if (deepClone) {
        const children = await this.masterFormSectionRepo.findChildren(masterSection.id);

        // Filter children by specificSectionIds if provided
        const childrenToProcess = specificSectionIds
          ? children.filter((child) => specificSectionIds.includes(child.id))
          : children;

        // Collect manual displayOrder overrides for children to avoid conflicts
        const childManualOrders = new Set<number>();
        for (const child of childrenToProcess) {
          const childIdNum = typeof child.id === 'string' ? parseInt(child.id) : child.id;
          const childOverride = sectionOverrides.find((o) => o.masterSectionId === childIdNum);
          if (childOverride?.displayOrder !== undefined) {
            childManualOrders.add(childOverride.displayOrder);
          }
        }

        // Get starting display order for children
        let autoChildOrder = await this.templateFormSectionRepo.getNextDisplayOrder(
          programTemplateId,
          clonedSection.id,
        );

        // Helper to get next available order (skipping manual overrides)
        const getNextAvailableChildOrder = (): number => {
          while (childManualOrders.has(autoChildOrder)) {
            autoChildOrder++;
          }
          const result = autoChildOrder;
          autoChildOrder++;
          return result;
        };

        for (const child of childrenToProcess) {
          // Determine desiredDisplayOrder for this child (manual or auto)
          const childIdNum = typeof child.id === 'string' ? parseInt(child.id) : child.id;
          const childOverride = sectionOverrides.find((o) => o.masterSectionId === childIdNum);
          const childDesiredOrder =
            childOverride?.displayOrder !== undefined
              ? childOverride.displayOrder
              : getNextAvailableChildOrder();

          const childClones = await this.cloneSectionHierarchy(
            child,
            programTemplateId,
            clonedSection.id,
            deepClone,
            specificSectionIds,
            createdBy,
            manager,
            sectionOverrides, // Pass section overrides to children
            childDesiredOrder, // Pass calculated display order
          );

          clonedSections.push(...childClones);
        }
      }

      return clonedSections;
    } catch (error) {
      this.logger.error('Error in cloneSectionHierarchy', error);
      handleKnownErrors(ERROR_CODES.TEMPLATE_FORM_SECTION_CLONE_FAILED, error);
    }
  }

  /**
   * Clone questions for cloned sections
   * Note: Questions are cloned directly to the database to avoid circular dependency
   */
  private async cloneQuestionsForSections(
    sections: TemplateFormSection[],
    createdBy: number,
    manager: any,
    questionOverrides: Array<{
      masterQuestionId: number;
      questionText?: string;
      questionType?: any;
      answerType?: any;
      answerLocation?: string;
      optionConfig?: Record<string, any>[];
      config?: Record<string, any>;
      conditionalConfig?: any;
      displayOrder?: number;
    }>,
    specificQuestionIds?: number[],
  ) {
    try {
      this.logger.log('Starting to clone questions', {
        sectionsCount: sections.length,
        hasSpecificQuestions: !!specificQuestionIds,
        specificQuestionIdsLength: specificQuestionIds?.length || 0,
        specificQuestionIds,
        overridesCount: questionOverrides?.length || 0,
      });

      for (const section of sections) {
        if (!section.masterFormSectionId) {
          this.logger.log('Skipping section without masterFormSectionId', {
            sectionId: section.id,
          });
          continue;
        }

        // Get questions from master section
        const masterQuestions = await this.masterQuestionRepo.findBySection(
          section.masterFormSectionId,
        );

        this.logger.log('Found master questions for section', {
          sectionId: section.id,
          masterFormSectionId: section.masterFormSectionId,
          totalQuestions: masterQuestions.length,
          questionIds: masterQuestions.map((q) => q.id),
        });

        // Filter to specific questions if specified
        let questionsToClone = masterQuestions;

        if (specificQuestionIds && specificQuestionIds.length > 0) {
          // Convert bigint string IDs to numbers for comparison and sort by specificQuestionIds order
          const questionMap = new Map(
            masterQuestions.map((q) => [typeof q.id === 'string' ? parseInt(q.id) : q.id, q]),
          );

          // Sort questions by the order they appear in specificQuestionIds
          questionsToClone = specificQuestionIds
            .map((id) => questionMap.get(id))
            .filter((q): q is (typeof masterQuestions)[0] => q !== undefined);

          this.logger.log('Filtering by specific question IDs', {
            sectionId: section.id,
            specificQuestionIds,
            masterQuestionIds: masterQuestions.map((q) => q.id),
            matchedCount: questionsToClone.length,
            orderedQuestionIds: questionsToClone.map((q) => q.id),
          });

          // Warn if no questions matched
          if (questionsToClone.length === 0) {
            this.logger.warn('No master questions matched the specified question IDs', {
              sectionId: section.id,
              masterFormSectionId: section.masterFormSectionId,
              specificQuestionIds,
              availableQuestionIds: masterQuestions.map((q) => q.id),
            });
          }
        }

        this.logger.log('Questions to clone after filtering', {
          sectionId: section.id,
          filteredCount: questionsToClone.length,
          filteredQuestionIds: questionsToClone.map((q) => q.id),
        });

        if (questionsToClone.length === 0) {
          this.logger.warn('No questions to clone for this section', {
            sectionId: section.id,
            masterFormSectionId: section.masterFormSectionId,
          });
          continue;
        }

        // Collect all manually specified display orders for this section's questions
        const manuallyAssignedOrders = new Set<number>();
        for (const question of questionsToClone) {
          const questionIdNum =
            typeof question.id === 'string' ? parseInt(question.id) : question.id;
          const override = questionOverrides.find((o) => o.masterQuestionId === questionIdNum);
          if (override?.displayOrder !== undefined) {
            manuallyAssignedOrders.add(override.displayOrder);
          }
        }

        this.logger.log('Manual display orders detected', {
          sectionId: section.id,
          manuallyAssignedOrders: Array.from(manuallyAssignedOrders).sort((a, b) => a - b),
        });

        // Determine display order logic
        // If specificQuestionIds provided, use array order (1, 2, 3...) but skip manually assigned orders
        // Otherwise, use next available order in the section
        let nextDisplayOrder = 1;
        if (!specificQuestionIds || specificQuestionIds.length === 0) {
          // Get current max display order in the section to start from
          const result = await manager
            .createQueryBuilder(TemplateQuestion, 'tq')
            .select('MAX(tq.displayOrder)', 'maxOrder')
            .where('tq.templateFormSectionId = :sectionId', { sectionId: section.id })
            .andWhere('tq.deletedAt IS NULL')
            .getRawOne();
          nextDisplayOrder = (result?.maxOrder || 0) + 1;
        }

        // Helper function to get next available display order
        const getNextAvailableDisplayOrder = (currentOrder: number): number => {
          while (manuallyAssignedOrders.has(currentOrder)) {
            currentOrder++;
          }
          return currentOrder;
        };

        // Track the next auto display order (used for questions without overrides)
        let autoDisplayOrderCounter =
          specificQuestionIds && specificQuestionIds.length > 0 ? 1 : nextDisplayOrder;

        for (let i = 0; i < questionsToClone.length; i++) {
          const masterQuestion = questionsToClone[i];

          // Check if this question has a manual displayOrder override FIRST
          const masterQuestionIdNum =
            typeof masterQuestion.id === 'string' ? parseInt(masterQuestion.id) : masterQuestion.id;
          const overrideForQuestion = questionOverrides.find(
            (o) => o.masterQuestionId === masterQuestionIdNum,
          );

          // Calculate display order only for questions WITHOUT override
          // For questions WITH override, they'll use their manual value later
          let displayOrder: number;
          if (overrideForQuestion?.displayOrder !== undefined) {
            // This question has a manual override - use it
            displayOrder = overrideForQuestion.displayOrder;
          } else {
            // Auto-calculate and skip reserved display orders
            displayOrder = getNextAvailableDisplayOrder(autoDisplayOrderCounter);
            autoDisplayOrderCounter = displayOrder + 1; // Move to next for the next auto-calculated question
          }
          // Check if this master question is already cloned to this template section
          const existingClone = await manager.findOne(TemplateQuestion, {
            where: {
              masterQuestionId: masterQuestion.id,
              templateFormSectionId: section.id,
              deletedAt: null,
            },
          });

          if (existingClone) {
            this.logger.log('Question already cloned to this section, skipping', {
              masterQuestionId: masterQuestion.id,
              sectionId: section.id,
              existingCloneId: existingClone.id,
            });
            continue;
          }

          // overrideForQuestion was already looked up earlier for display order calculation
          if (overrideForQuestion) {
            this.logger.log('Applying overrides to question', {
              masterQuestionId: masterQuestion.id,
              overrides: overrideForQuestion,
            });
          }

          // Generate unique question code (transaction-aware)
          const questionCode = await this.generateUniqueQuestionCode(
            masterQuestion.questionCode,
            section.programTemplateId,
            manager,
          );

          // Create template question with overrides applied
          // Note: displayOrder was already calculated above (either from override or auto-calculated)
          const clonedQuestion = await manager.save(TemplateQuestion, {
            templateFormSectionId: section.id,
            masterQuestionId: masterQuestion.id,
            questionCode,
            bindingKey: masterQuestion.bindingKey, // bindingKey cannot be overridden
            questionText: overrideForQuestion?.questionText ?? masterQuestion.questionText,
            questionType: overrideForQuestion?.questionType ?? masterQuestion.questionType,
            answerType: overrideForQuestion?.answerType ?? masterQuestion.answerType,
            answerLocation: masterQuestion.answerLocation, // answerLocation cannot be overridden
            optionConfig: overrideForQuestion?.optionConfig ?? masterQuestion.optionConfig,
            config: overrideForQuestion?.config ?? masterQuestion.config ?? {},
            conditionalConfig: overrideForQuestion?.conditionalConfig ?? masterQuestion?.conditionalConfig,
            displayOrder: displayOrder, // Already calculated to avoid conflicts
            createdBy,
            createdAt: new Date(),
            updatedAt: new Date(),
          });

          this.logger.log('Successfully cloned question', {
            masterQuestionId: masterQuestion.id,
            clonedQuestionId: clonedQuestion.id,
            questionCode,
            displayOrder: displayOrder,
            hadOverrides: !!overrideForQuestion,
          });
        }
      }
    } catch (error) {
      this.logger.error('Error cloning questions', error);
      handleKnownErrors(ERROR_CODES.TEMPLATE_QUESTION_CLONE_FAILED, error);
    }
  }

  /**
   * Generate unique section key for template (transaction-aware)
   */
  private async generateUniqueSectionKey(
    baseSectionKey: string,
    programTemplateId: number,
    manager?: any,
  ): Promise<string> {
    let counter = 1;
    let sectionKey = baseSectionKey;

    while (true) {
      // Use query runner if in transaction, otherwise use repository
      let existing;
      if (manager) {
        existing = await manager.findOne(TemplateFormSection, {
          where: {
            sectionKey,
            programTemplateId,
            deletedAt: null,
          },
        });
      } else {
        existing = await this.templateFormSectionRepo.findBySectionKeyAndTemplate(
          sectionKey,
          programTemplateId,
        );
      }

      if (!existing) {
        return sectionKey;
      }

      sectionKey = `${baseSectionKey}_T${counter}`;
      counter++;

      // Prevent infinite loop
      if (counter > 100) {
        handleKnownErrors(
          ERROR_CODES.TEMPLATE_FORM_SECTION_KEY_GENERATION_FAILED,
          new Error('Section key generation exceeded maximum attempts')
        );
      }
    }
  }

  /**
   * Generate unique question code for template
   */
  private async generateUniqueQuestionCode(
    baseQuestionCode: string,
    programTemplateId: number,
    manager: any,
  ): Promise<string> {
    let counter = 1;
    let questionCode = baseQuestionCode;

    while (true) {
      // Check if question code exists in this template
      const existing = await manager
        .createQueryBuilder(TemplateQuestion, 'tq')
        .leftJoinAndSelect('tq.templateFormSection', 'tfs')
        .where('tq.question_code = :questionCode', { questionCode })
        .andWhere('tfs.program_template_id = :programTemplateId', { programTemplateId })
        .andWhere('tq.deleted_at IS NULL')
        .getOne();

      if (!existing) {
        return questionCode;
      }

      questionCode = `${baseQuestionCode}_T${counter}`;
      counter++;

      // Prevent infinite loop
      if (counter > 100) {
        handleKnownErrors(
          ERROR_CODES.TEMPLATE_QUESTION_CODE_GENERATION_FAILED,
          new Error('Question code generation exceeded maximum attempts')
        );
      }
    }
  }

  /**
   * Recursively delete a template section and all its descendants
   */
  private async deleteTemplateSectionWithDescendants(sectionId: number, manager: any) {
    try {
      // Get all child sections
      const children = await this.templateFormSectionRepo.findChildren(sectionId);

      // Recursively delete children first
      for (const child of children) {
        await this.deleteTemplateSectionWithDescendants(child.id, manager);
      }

      // Delete questions in this section
      await manager.delete(TemplateQuestion, { templateFormSectionId: sectionId });

      // Delete the section itself
      await manager.delete(TemplateFormSection, { id: sectionId });

      this.logger.log('Deleted template section with descendants', { sectionId });
    } catch (error) {
      this.logger.error('Error deleting template section with descendants', error);
      handleKnownErrors(ERROR_CODES.TEMPLATE_FORM_SECTION_DELETE_FAILED, error);
    }
  }

  /**
   * Helper method to recursively process sections and subsections
   */
  private async processSectionRecursively(
    manager: EntityManager,
    sectionInput: any, // SectionInputDto
    programTemplateId: number,
    createdBy: number,
    createdSections: TemplateFormSection[],
    createdQuestions: TemplateQuestion[],
    parentSectionId: number | null = null,
  ): Promise<void> {
    // Validate section input (must have exactly ONE of: masterFormSectionId OR sectionName OR templateFormSectionId)
    const hasCloneSource = !!sectionInput.masterFormSectionId;
    const hasCreateData = !!(sectionInput.sectionName && sectionInput.sectionKey);
    const hasExistingSection = !!sectionInput.templateFormSectionId;
    const hasPartialCreateData = !!sectionInput.sectionName || !!sectionInput.sectionKey;
    const resolvedParentSectionId = parentSectionId ?? sectionInput.parentSectionId ?? null;

    const sourceCount = [hasCloneSource, hasCreateData, hasExistingSection].filter(Boolean).length;

    if (sourceCount > 1) {
      handleKnownErrors(
        ERROR_CODES.TEMPLATE_FORM_SECTION_DUPLICATE_KEY,
        new Error('Section input must have exactly ONE of: masterFormSectionId (clone), sectionName (create), or templateFormSectionId (existing)')
      );
    }

    if (sourceCount === 0) {
      handleKnownErrors(
        ERROR_CODES.TEMPLATE_FORM_SECTION_DUPLICATE_KEY,
        new Error('Section input must have either masterFormSectionId, both sectionName+sectionKey, or templateFormSectionId')
      );
    }

    if (!hasCreateData && hasPartialCreateData) {
      handleKnownErrors(
        ERROR_CODES.TEMPLATE_FORM_SECTION_DUPLICATE_KEY,
        new Error('Both sectionName and sectionKey are required when creating a new section')
      );
    }

    // Validate questions array is present and not empty for NEW or CLONED sections
    if (
      (hasCloneSource || hasCreateData) &&
      (!sectionInput.questions || sectionInput.questions.length === 0)
    ) {
      handleKnownErrors(
        ERROR_CODES.TEMPLATE_FORM_SECTION_DUPLICATE_KEY,
        new Error('Questions array is required and cannot be empty when creating or cloning sections')
      );
    }

    if (resolvedParentSectionId !== null) {
      await this.validateTemplateParentSection(manager, resolvedParentSectionId, programTemplateId);
    }

    let templateSection: TemplateFormSection;

    if (hasExistingSection) {
      // CASE: Reference existing template section
      const existingSection = await manager.findOne(TemplateFormSection, {
        where: { id: sectionInput.templateFormSectionId, deletedAt: IsNull() },
      });

      if (!existingSection) {
        handleKnownErrors(
          ERROR_CODES.TEMPLATE_FORM_SECTION_NOT_FOUND,
          new Error(`Template form section not found: ${sectionInput.templateFormSectionId}`)
        );
      }

      // Validate section belongs to the same template
      if (existingSection.programTemplateId !== programTemplateId) {
        handleKnownErrors(
          ERROR_CODES.TEMPLATE_FORM_SECTION_DUPLICATE_KEY,
          new Error(`Section ${sectionInput.templateFormSectionId} belongs to a different template`)
        );
      }

      // Update parentSectionId if provided
      if (
        resolvedParentSectionId !== null &&
        existingSection.parentSectionId !== resolvedParentSectionId
      ) {
        const oldParentId = existingSection.parentSectionId;
        if (resolvedParentSectionId === existingSection.id) {
          handleKnownErrors(
            ERROR_CODES.TEMPLATE_FORM_SECTION_CIRCULAR_REFERENCE,
            new Error('Cannot set section as its own parent')
          );
        }

        const isDescendant = await this.isDescendantOfInTransaction(
          manager,
          resolvedParentSectionId,
          existingSection.id,
        );
        if (isDescendant) {
          handleKnownErrors(
            ERROR_CODES.TEMPLATE_FORM_SECTION_CIRCULAR_REFERENCE,
            new Error('Cannot create circular reference in section hierarchy')
          );
        }

        // Validate nesting depth before re-parenting
        if (MAX_SECTION_NESTING_DEPTH !== null && MAX_SECTION_NESTING_DEPTH !== undefined) {
          const currentDepth = await this.calculateSectionDepth(
            resolvedParentSectionId,
            manager,
            'template',
          );

          if (currentDepth >= MAX_SECTION_NESTING_DEPTH) {
            handleKnownErrors(
              ERROR_CODES.FORM_SECTION_NESTING_DEPTH_EXCEEDED,
              new Error(`Section nesting depth limit exceeded for template. Maximum allowed depth is ${MAX_SECTION_NESTING_DEPTH} level(s), but attempting to create section at depth ${currentDepth + 1}.`)
            );
          }
        }

        existingSection.parentSectionId = resolvedParentSectionId;
        existingSection.updatedBy = createdBy;
        existingSection.updatedAt = new Date();
        templateSection = await manager.save(TemplateFormSection, existingSection);

        this.logger.log('Updated existing section with new parent', {
          templateSectionId: templateSection.id,
          oldParentId,
          newParentId: resolvedParentSectionId,
        });
      } else {
        templateSection = existingSection;
      }

      this.logger.log('Referenced existing section', {
        templateSectionId: templateSection.id,
        parentSectionId: resolvedParentSectionId,
      });
    } else if (hasCloneSource) {
      // CASE: Clone from master section
      const masterSection = await this.masterFormSectionRepo.findById(
        sectionInput.masterFormSectionId,
      );
      if (!masterSection) {
        handleKnownErrors(
          ERROR_CODES.MASTER_FORM_SECTION_NOT_FOUND,
          new Error(`Master form section not found: ${sectionInput.masterFormSectionId}`)
        );
      }

      const sectionKey = await this.generateUniqueSectionKey(
        masterSection.sectionKey,
        programTemplateId,
        manager,
      );

      // Create cloned section
      templateSection = manager.create(TemplateFormSection, {
        programTemplateId: programTemplateId,
        masterFormSectionId: masterSection.id,
        name: sectionInput.sectionOverride?.sectionName || masterSection.name,
        description: sectionInput.sectionOverride?.sectionDescription || masterSection.description,
        sectionKey,
        displayOrder: sectionInput.sectionOverride?.displayOrder || sectionInput.displayOrder || 1,
        parentSectionId: resolvedParentSectionId,
        conditionalConfig:
          sectionInput.sectionOverride?.conditionalConfig || masterSection.conditionalConfig,
        createdBy: createdBy,
        updatedBy: createdBy,
        createdAt: new Date(),
        updatedAt: new Date(),
      });

      templateSection = await manager.save(TemplateFormSection, templateSection);
      this.logger.log('Cloned section from master', {
        masterSectionId: masterSection.id,
        templateSectionId: templateSection.id,
        parentSectionId: resolvedParentSectionId,
      });
    } else {
      // CASE: Create new section

      // Validate nesting depth if parentSectionId is provided
      if (
        resolvedParentSectionId &&
        MAX_SECTION_NESTING_DEPTH !== null &&
        MAX_SECTION_NESTING_DEPTH !== undefined
      ) {
        const currentDepth = await this.calculateSectionDepth(
          resolvedParentSectionId,
          manager,
          'template',
        );

        if (currentDepth >= MAX_SECTION_NESTING_DEPTH) {
          handleKnownErrors(
            ERROR_CODES.FORM_SECTION_NESTING_DEPTH_EXCEEDED,
            new Error(`Section nesting depth limit exceeded for template. Maximum allowed depth is ${MAX_SECTION_NESTING_DEPTH} level(s), but attempting to create section at depth ${currentDepth + 1}.`)
          );
        }
      }

      templateSection = manager.create(TemplateFormSection, {
        programTemplateId: programTemplateId,
        masterFormSectionId: null,
        name: sectionInput.sectionName!,
        sectionKey: await this.generateUniqueSectionKey(
          sectionInput.sectionKey,
          programTemplateId,
          manager,
        ),
        description: sectionInput.sectionDescription || null,
        displayOrder: sectionInput.displayOrder || 1,
        parentSectionId: resolvedParentSectionId,
        conditionalConfig: sectionInput.conditionalConfig,
        createdBy: createdBy,
        updatedBy: createdBy,
        createdAt: new Date(),
        updatedAt: new Date(),
      });

      templateSection = await manager.save(TemplateFormSection, templateSection);
      this.logger.log('Created new section', {
        templateSectionId: templateSection.id,
        parentSectionId: resolvedParentSectionId,
      });
    }

    createdSections.push(templateSection);

    // Process questions for this section (only if questions are provided - new/cloned sections)
    if (sectionInput.questions && sectionInput.questions.length > 0) {
      for (const questionInput of sectionInput.questions) {
        // Validate question input (must have exactly ONE of: masterQuestionId OR label+type)
        const hasQuestionCloneSource = !!questionInput.masterQuestionId;
        const hasQuestionCreateData = !!(
          questionInput.label &&
          questionInput.type &&
          questionInput.answerType
        );

        if (hasQuestionCloneSource && hasQuestionCreateData) {
          handleKnownErrors(
            ERROR_CODES.TEMPLATE_QUESTION_DUPLICATE_CODE,
            new Error('Question input must have either masterQuestionId (to clone) OR (label+type+answerType) to create, not both')
          );
        }

        if (!hasQuestionCloneSource && !hasQuestionCreateData) {
          handleKnownErrors(
            ERROR_CODES.TEMPLATE_QUESTION_DUPLICATE_CODE,
            new Error('Question input must have either masterQuestionId OR (label, type, answerType) for creation')
          );
        }

        let templateQuestion: TemplateQuestion;

        if (hasQuestionCloneSource) {
          // CASE: Clone question from master
          const masterQuestion = await this.masterQuestionRepo.findById(
            questionInput.masterQuestionId,
          );
          if (!masterQuestion) {
            handleKnownErrors(
              ERROR_CODES.MASTER_QUESTION_NOT_FOUND,
              new Error(`Master question not found: ${questionInput.masterQuestionId}`)
            );
          }

          // Merge config if override provided
          const mergedConfig = questionInput.override?.config
            ? { ...masterQuestion.config, ...questionInput.override.config }
            : masterQuestion.config;

          // Generate question code
          const questionCode = await this.generateUniqueQuestionCode(
            masterQuestion.questionCode,
            programTemplateId,
            manager,
          );

          templateQuestion = manager.create(TemplateQuestion, {
            templateFormSectionId: templateSection.id,
            masterQuestionId: masterQuestion.id,
            questionCode: questionCode,
            questionText: questionInput.override?.label || masterQuestion.questionText,
            questionType: masterQuestion.questionType, // Cannot override
            answerType: masterQuestion.answerType, // Cannot override
            displayOrder: questionInput.override?.displayOrder || questionInput.displayOrder || 1,
            config: mergedConfig ?? {},
            optionConfig: masterQuestion.optionConfig,
            bindingKey: masterQuestion.bindingKey,
            answerLocation: masterQuestion.answerLocation,
            conditionalConfig: questionInput.override?.conditionalConfig ?? masterQuestion.conditionalConfig ?? null,
            createdBy: createdBy,
            updatedBy: createdBy,
            createdAt: new Date(),
            updatedAt: new Date(),
          });

          templateQuestion = await manager.save(TemplateQuestion, templateQuestion);
          this.logger.log('Cloned question from master', {
            masterQuestionId: masterQuestion.id,
            templateQuestionId: templateQuestion.id,
          });
        } else {
          // CASE: Create new question
          // Generate unique binding key and code
          const bindingKey = `${templateSection.sectionKey}_${questionInput.label!.replace(/\s+/g, '_').toUpperCase()}_${Date.now()}`;
          const questionCode = await this.generateUniqueQuestionCode(
            `TQ_${Date.now()}`,
            programTemplateId,
            manager,
          );

          templateQuestion = manager.create(TemplateQuestion, {
            templateFormSectionId: templateSection.id,
            masterQuestionId: null,
            questionCode: questionCode,
            questionText: questionInput.label!,
            questionType: questionInput.type!,
            answerType: questionInput.answerType!,
            displayOrder: questionInput.displayOrder || 1,
            config: questionInput.config ?? {},
            optionConfig: questionInput.optionConfig,
            bindingKey: bindingKey,
            answerLocation: null,
            conditionalConfig: questionInput.conditionalConfig ?? null,
            createdBy: createdBy,
            updatedBy: createdBy,
            createdAt: new Date(),
            updatedAt: new Date(),
          });

          templateQuestion = await manager.save(TemplateQuestion, templateQuestion);
          this.logger.log('Created new question', { templateQuestionId: templateQuestion.id });
        }

        createdQuestions.push(templateQuestion);
      }
    }

    // Process subsections recursively
    if (sectionInput.subsections && sectionInput.subsections.length > 0) {
      this.logger.log('Processing subsections', {
        parentSectionId: templateSection.id,
        subsectionCount: sectionInput.subsections.length,
      });

      for (const subsectionInput of sectionInput.subsections) {
        await this.processSectionRecursively(
          manager,
          subsectionInput,
          programTemplateId,
          createdBy,
          createdSections,
          createdQuestions,
          templateSection.id, // Parent is the current section
        );
      }
    }
  }

  /**
   * Unified Template Form Builder (Master → Template)
   * Handles cloning sections/questions from master OR creating new ones
   */
  async buildTemplateForm(dto: TemplateFormBuilderDto) {
    try {
      return await this.dataSource.transaction(async (manager) => {
        this.logger.log('Starting template form builder', { dto });

        // Validate that programTemplateId is provided
        if (!dto.programTemplateId) {
          handleKnownErrors(
            ERROR_CODES.PROGRAM_TEMPLATE_NOT_FOUND,
            new Error('Program template ID is required')
          );
        }

        const template = await this.programTemplateRepo.findById(dto.programTemplateId);
        if (!template) {
          handleKnownErrors(
            ERROR_CODES.PROGRAM_TEMPLATE_NOT_FOUND,
            new Error(`Program template not found: ${dto.programTemplateId}`)
          );
        }

        // ========================================
        // DIRECT MASTER CLONING (if enabled)
        // ========================================
        let sectionsToProcess: SectionInputDto[] = dto.sections || [];
        
        if (dto.cloneFromMaster) {
          this.logger.log('Direct master cloning enabled', { masterFormId: dto.masterFormId });

          // Validate masterFormId is provided
          if (!dto.masterFormId) {
            handleKnownErrors(
              ERROR_CODES.MASTER_FORM_SECTION_NOT_FOUND,
              new Error('Master form ID is required when cloneFromMaster is true. Please provide masterFormId.')
            );
          }

          // Fetch all root master sections (parentSectionId = null)
          const masterSections = await manager.find(MasterFormSection, {
            where: {
              id: dto.masterFormId,
              parentSectionId: IsNull(),
              deletedAt: IsNull(),
            },
            order: { id: 'ASC' },
          });

          // If masterFormId is actually a section ID, try to fetch it directly
          if (masterSections.length === 0) {
            const masterSection = await manager.findOne(MasterFormSection, {
              where: { id: dto.masterFormId, deletedAt: IsNull() },
            });

            if (masterSection) {
              masterSections.push(masterSection);
            }
          }

          this.logger.log(`Found ${masterSections.length} root sections in master form ${dto.masterFormId}`);

          if (masterSections.length === 0) {
            this.logger.warn('Master form has no sections to clone', { masterFormId: dto.masterFormId });
          }

          // Fetch all master questions for these sections
          const masterSectionIdsForCloning = masterSections.map(ms => Number(ms.id));
          const masterQuestions = await manager.find(MasterQuestion, {
            where: {
              masterFormSectionId: In(masterSectionIdsForCloning),
              deletedAt: IsNull(),
            },
            order: { id: 'ASC' },
          });

          this.logger.log(`Found ${masterQuestions.length} questions in master sections`);

          // Group questions by section
          const questionsBySection = new Map<number, MasterQuestion[]>();
          masterQuestions.forEach(mq => {
            const sectionId = Number(mq.masterFormSectionId);
            if (!questionsBySection.has(sectionId)) {
              questionsBySection.set(sectionId, []);
            }
            questionsBySection.get(sectionId)!.push(mq);
          });

          // Build sections array from master
          const clonedSections: SectionInputDto[] = masterSections.map((ms, index) => {
            const sectionId = Number(ms.id);
            const questions = questionsBySection.get(sectionId) || [];

            // Build questions array for this section
            const questionInputs: QuestionInputDto[] = questions.map((mq, qIndex) => ({
              masterQuestionId: Number(mq.id),
              displayOrder: qIndex + 1,
            }));

            // If section has no questions, log warning but still include it
            if (questionInputs.length === 0) {
              this.logger.warn(`Master section ${sectionId} has no questions`, {
                sectionName: ms.name,
                sectionKey: ms.sectionKey,
              });
              // Skip sections with no questions as they violate validation
              return null;
            }

            return {
              masterFormSectionId: sectionId,
              displayOrder: index + 1,
              questions: questionInputs,
            } as SectionInputDto;
          }).filter(s => s !== null) as SectionInputDto[];

          this.logger.log(`Built ${clonedSections.length} sections from master for cloning`);

          // Merge with manually provided sections (master sections first, then manual)
          sectionsToProcess = [...clonedSections, ...sectionsToProcess];
        }

        // Validate we have sections to process
        if (!sectionsToProcess || sectionsToProcess.length === 0) {
          handleKnownErrors(
            ERROR_CODES.FORM_SECTION_MISSING_SOURCE,
            new Error('No sections to process. Either provide sections array or enable cloneFromMaster with a valid masterFormId.')
          );
        }

        const createdSections: TemplateFormSection[] = [];
        const createdQuestions: TemplateQuestion[] = [];

        // Process each top-level section recursively
        for (const sectionInput of sectionsToProcess) {
          await this.processSectionRecursively(
            manager,
            sectionInput,
            dto.programTemplateId,
            dto.createdBy,
            createdSections,
            createdQuestions,
            null, // No parent for top-level sections
          );
        }

        this.logger.log('Template form builder completed successfully', {
          sectionsCreated: createdSections.length,
          questionsCreated: createdQuestions.length,
        });

        return {
          success: true,
          message: programTemplateConstMessages.TEMPLATE_FORM_BUILT,
          data: {
            templateFormSections: createdSections,
            totalSectionsCreated: createdSections.length,
            totalQuestionsCreated: createdQuestions.length,
          },
        };
      });
    } catch (error) {
      this.logger.error('Error building template form', error);
      handleKnownErrors(ERROR_CODES.TEMPLATE_FORM_SECTION_CLONE_FAILED, error);
    }
  }

  /**
   * Calculate section nesting depth by traversing parent chain
   * @param sectionId - The section ID to calculate depth for
   * @param manager - Transaction manager
   * @param type - 'template' or 'program' to determine which table to query
   * @returns depth (0 = root level, 1 = first child, etc.)
   */
  private async calculateSectionDepth(
    sectionId: number,
    manager: any,
    type: 'template' | 'program' = 'template',
  ): Promise<number> {
    let currentDepth = 0;
    let currentParentId: number | null = sectionId;

    while (currentParentId) {
      const parentSection = await manager.findOne(TemplateFormSection, {
        where: { id: currentParentId, deletedAt: null },
      });

      if (!parentSection) break; // Safety check

      currentDepth++;
      currentParentId = parentSection.parentSectionId;

      // Prevent infinite loops (safety measure)
      if (currentDepth > MAX_LIMIT_FOR_NESTED_SUBSECTIONS) {
        handleKnownErrors(
          ERROR_CODES.FORM_SECTION_CIRCULAR_REFERENCE,
          new Error(`Section hierarchy depth exceeded safety limit of ${MAX_LIMIT_FOR_NESTED_SUBSECTIONS} levels. This may indicate a circular reference in the section parent-child relationships.`)
        );
      }
    }

    return currentDepth;
  }

  /**
   * Validate that parent section exists and belongs to the same template
   */
  private async validateTemplateParentSection(
    manager: EntityManager,
    parentSectionId: number,
    programTemplateId: number,
  ): Promise<void> {
    const parentSection = await manager.findOne(TemplateFormSection, {
      where: { id: parentSectionId, deletedAt: IsNull() },
    });

    if (!parentSection) {
      handleKnownErrors(
        ERROR_CODES.TEMPLATE_FORM_SECTION_NOT_FOUND,
        new Error(`Parent section not found: ${parentSectionId}`)
      );
    }

    if (parentSection.programTemplateId !== programTemplateId) {
      handleKnownErrors(
        ERROR_CODES.TEMPLATE_FORM_SECTION_INVALID_PARENT,
        new Error('Parent section must belong to the same template')
      );
    }
  }

  /**
   * Check if sectionId is a descendant of potentialAncestorId within current transaction
   */
  private async isDescendantOfInTransaction(
    manager: EntityManager,
    sectionId: number,
    potentialAncestorId: number,
  ): Promise<boolean> {
    let current = await manager.findOne(TemplateFormSection, {
      where: { id: sectionId, deletedAt: IsNull() },
    });

    while (current?.parentSectionId) {
      if (current.parentSectionId === potentialAncestorId) {
        return true;
      }

      current = await manager.findOne(TemplateFormSection, {
        where: { id: current.parentSectionId, deletedAt: IsNull() },
      });
    }

    return false;
  }
}
