/**
 * Data Access Object for DiscreteMaterialPlanData table
 *
 * Mirrors the Python DiscreteMaterialPlanDAO functionality:
 * - Query operations for discrete material plan data
 * - Support for querying by PlanNumber, SourceNumber
 * - Deduplication by MaterialCode
 * - Statistics gathering
 */

import { create, type IDatabaseService } from './index'
import { createDialect, type SqlDialect } from './dialects'
import { createLogger, getRequestId, trackDuration } from '../logger'

const log = createLogger('DiscreteMaterialPlanDAO')

/**
 * Material plan record interface
 */
export interface MaterialPlanRecord {
  id?: number
  factory: string
  materialStatus: string
  planNumber: string
  sourceNumber: string
  materialType: string
  productCode: string
  productName: string
  productUnit: string
  productPlanQuantity: number
  useDepartment: string
  remark: string
  creator: string
  createDate: Date
  approver: string
  approveDate: Date
  sequenceNumber: number
  materialCode: string
  materialName: string
  specification: string
  model: string
  drawingNumber: string
  materialQuality: string
  planQuantity: number
  unit: string
  requiredDate: Date
  warehouse: string
  unitUsage: number
  cumulativeOutputQuantity: number
  bomVersion: string
}

/**
 * Configuration for DiscreteMaterialPlanData table
 */
export const DISCRETE_MATERIAL_PLAN_CONFIG = {
  COLUMNS: {
    ID: 'ID',
    FACTORY: 'Factory',
    MATERIAL_STATUS: 'MaterialStatus',
    PLAN_NUMBER: 'PlanNumber',
    SOURCE_NUMBER: 'SourceNumber',
    MATERIAL_TYPE: 'MaterialType',
    PRODUCT_CODE: 'ProductCode',
    PRODUCT_NAME: 'ProductName',
    PRODUCT_UNIT: 'ProductUnit',
    PRODUCT_PLAN_QUANTITY: 'ProductPlanQuantity',
    USE_DEPARTMENT: 'UseDepartment',
    REMARK: 'Remark',
    CREATOR: 'Creator',
    CREATE_DATE: 'CreateDate',
    APPROVER: 'Approver',
    APPROVE_DATE: 'ApproveDate',
    SEQUENCE_NUMBER: 'SequenceNumber',
    MATERIAL_CODE: 'MaterialCode',
    MATERIAL_NAME: 'MaterialName',
    SPECIFICATION: 'Specification',
    MODEL: 'Model',
    DRAWING_NUMBER: 'DrawingNumber',
    MATERIAL_QUALITY: 'MaterialQuality',
    PLAN_QUANTITY: 'PlanQuantity',
    UNIT: 'Unit',
    REQUIRED_DATE: 'RequiredDate',
    WAREHOUSE: 'Warehouse',
    UNIT_USAGE: 'UnitUsage',
    CUMULATIVE_OUTPUT_QUANTITY: 'CumulativeOutputQuantity',
    BOM_VERSION: 'BOMVersion'
  }
} as const

/**
 * DiscreteMaterialPlanDAO Class
 */
export class DiscreteMaterialPlanDAO {
  private dbService: IDatabaseService | null = null
  private dialect: SqlDialect | null = null

  private getDialect(): SqlDialect {
    if (!this.dialect) {
      this.dialect = createDialect(this.dbService!.type)
    }
    return this.dialect
  }

  /**
   * Get the appropriate table name based on database type
   */
  private getTableName(): string {
    return this.getDialect().quoteTableName('dbo', 'DiscreteMaterialPlanData')
  }

  /**
   * Get database service instance using DatabaseFactory
   */
  private async getDatabaseService(): Promise<IDatabaseService> {
    if (this.dbService && this.dbService.isConnected()) {
      return this.dbService
    }

    this.dbService = await create()
    return this.dbService
  }

  // ==================== QUERY ALL ====================

  /**
   * Query all records from DiscreteMaterialPlanData table
   * @returns List of all records
   */
  async queryAll(): Promise<any[]> {
    try {
      const dbService = await this.getDatabaseService()
      const tableName = this.getTableName()

      const sqlString = `SELECT * FROM ${tableName}`
      const result = await trackDuration(async () => await dbService.query(sqlString), {
        operationName: 'DiscreteMaterialPlanDAO.queryAll',
        context: { tableName, operationType: 'SELECT' }
      })

      return result.result.rows
    } catch (error) {
      log.error('Query all error', {
        tableName: this.getTableName(),
        operationType: 'SELECT',
        requestId: getRequestId(),
        error: error instanceof Error ? error.message : String(error)
      })
      return []
    }
  }

  /**
   * Query all records with deduplication by MaterialCode
   * Strategy: Keep first record for each MaterialCode
   * Order: CreateDate ASC, SequenceNumber ASC
   * @returns List of deduplicated records
   */
  async queryAllDistinctByMaterialCode(): Promise<any[]> {
    try {
      const dbService = await this.getDatabaseService()
      const tableName = this.getTableName()

      const sqlString = `
        WITH RankedRecords AS (
          SELECT
            *,
            ROW_NUMBER() OVER (
              PARTITION BY MaterialCode
              ORDER BY CreateDate ASC, SequenceNumber ASC
            ) AS rn
          FROM ${tableName}
          WHERE MaterialCode IS NOT NULL
        )
        SELECT
          Factory, MaterialStatus, PlanNumber, SourceNumber, MaterialType,
          ProductCode, ProductName, ProductUnit, ProductPlanQuantity,
          UseDepartment, Remark, Creator, CreateDate, Approver, ApproveDate,
          SequenceNumber, MaterialCode, MaterialName, Specification, Model,
          DrawingNumber, MaterialQuality, PlanQuantity, Unit, RequiredDate,
          Warehouse, UnitUsage, CumulativeOutputQuantity, BOMVersion
        FROM RankedRecords
        WHERE rn = 1
      `

      const result = await trackDuration(async () => await dbService.query(sqlString), {
        operationName: 'DiscreteMaterialPlanDAO.queryAllDistinctByMaterialCode',
        context: { tableName: this.getTableName(), operationType: 'SELECT' }
      })
      return result.result.rows
    } catch (error) {
      log.error('Query all distinct by material code error', {
        tableName: this.getTableName(),
        operationType: 'SELECT',
        requestId: getRequestId(),
        error: error instanceof Error ? error.message : String(error)
      })
      return []
    }
  }

  // ==================== QUERY BY SOURCE NUMBER ====================

  /**
   * Query records by SourceNumber list
   * @param sourceNumbers - List of SourceNumber values
   * @returns List of records
   */
  async queryBySourceNumbers(sourceNumbers: string[]): Promise<any[]> {
    if (!sourceNumbers || sourceNumbers.length === 0) {
      return []
    }

    try {
      const dbService = await this.getDatabaseService()
      const tableName = this.getTableName()
      const dialect = this.getDialect()
      const batchSize = 1500
      const allResults: any[] = []

      for (let i = 0; i < sourceNumbers.length; i += batchSize) {
        const batch = sourceNumbers.slice(i, i + batchSize)
        const placeholders = dialect.params(batch.length)

        const sqlString = `
          SELECT *
          FROM ${tableName}
          WHERE SourceNumber IN (${placeholders})
        `

        const result = await trackDuration(async () => await dbService.query(sqlString, batch), {
          operationName: 'DiscreteMaterialPlanDAO.queryBySourceNumbers',
          context: {
            tableName,
            operationType: 'SELECT',
            batchNumber: Math.floor(i / batchSize) + 1,
            batchSize: batch.length
          }
        })
        allResults.push(...result.result.rows)
      }

      return allResults
    } catch (error) {
      log.error('Query by source numbers error', {
        tableName: this.getTableName(),
        operationType: 'SELECT',
        requestId: getRequestId(),
        recordCount: sourceNumbers.length,
        error: error instanceof Error ? error.message : String(error)
      })
      return []
    }
  }

  /**
   * Query records by SourceNumber with deduplication by MaterialCode
   * Strategy: Keep first record for each MaterialCode
   * Order: CreateDate ASC, SequenceNumber ASC
   * @param sourceNumbers - List of SourceNumber values
   * @returns List of deduplicated records
   */
  async queryBySourceNumbersDistinct(sourceNumbers: string[]): Promise<any[]> {
    if (!sourceNumbers || sourceNumbers.length === 0) {
      return []
    }

    try {
      const dbService = await this.getDatabaseService()
      const tableName = this.getTableName()
      const dialect = this.getDialect()
      const batchSize = 1500
      const allResults: any[] = []

      for (let i = 0; i < sourceNumbers.length; i += batchSize) {
        const batch = sourceNumbers.slice(i, i + batchSize)
        const placeholders = dialect.params(batch.length)

        const sqlString = `
          WITH RankedRecords AS (
            SELECT
              *,
              ROW_NUMBER() OVER (
                PARTITION BY MaterialCode
                ORDER BY CreateDate ASC, SequenceNumber ASC
              ) AS rn
            FROM ${tableName}
            WHERE SourceNumber IN (${placeholders})
              AND MaterialCode IS NOT NULL
          )
          SELECT
            Factory, MaterialStatus, PlanNumber, SourceNumber, MaterialType,
            ProductCode, ProductName, ProductUnit, ProductPlanQuantity,
            UseDepartment, Remark, Creator, CreateDate, Approver, ApproveDate,
            SequenceNumber, MaterialCode, MaterialName, Specification, Model,
            DrawingNumber, MaterialQuality, PlanQuantity, Unit, RequiredDate,
            Warehouse, UnitUsage, CumulativeOutputQuantity, BOMVersion
          FROM RankedRecords
          WHERE rn = 1
        `

        const result = await trackDuration(async () => await dbService.query(sqlString, batch), {
          operationName: 'DiscreteMaterialPlanDAO.queryBySourceNumbersDistinct',
          context: {
            tableName,
            operationType: 'SELECT',
            batchNumber: Math.floor(i / batchSize) + 1,
            batchSize: batch.length
          }
        })
        allResults.push(...result.result.rows)
      }

      return allResults
    } catch (error) {
      log.error('Query by source numbers distinct error', {
        tableName: this.getTableName(),
        operationType: 'SELECT',
        requestId: getRequestId(),
        recordCount: sourceNumbers.length,
        error: error instanceof Error ? error.message : String(error)
      })
      return []
    }
  }

  /**
   * Query by production order number (SourceNumber)
   * @param sourceNumber - SourceNumber value
   * @returns List of records
   */
  async queryBySourceNumber(sourceNumber: string): Promise<any[]> {
    try {
      const dbService = await this.getDatabaseService()
      const tableName = this.getTableName()
      const dialect = this.getDialect()

      const placeholder = dialect.param(0)
      const sqlString = `
        SELECT *
        FROM ${tableName}
        WHERE SourceNumber = ${placeholder}
      `

      const result = await trackDuration(
        async () => await dbService.query(sqlString, [sourceNumber]),
        {
          operationName: 'DiscreteMaterialPlanDAO.queryBySourceNumber',
          context: { tableName, operationType: 'SELECT' }
        }
      )
      return result.result.rows
    } catch (error) {
      log.error('Query by source number error', {
        tableName: this.getTableName(),
        operationType: 'SELECT',
        requestId: getRequestId(),
        error: error instanceof Error ? error.message : String(error)
      })
      return []
    }
  }

  // ==================== QUERY BY PLAN NUMBER ====================

  /**
   * Query by plan number
   * @param planNumber - PlanNumber value
   * @returns List of records
   */
  async queryByPlanNumber(planNumber: string): Promise<any[]> {
    try {
      const dbService = await this.getDatabaseService()
      const tableName = this.getTableName()
      const dialect = this.getDialect()

      const placeholder = dialect.param(0)
      const sqlString = `
        SELECT *
        FROM ${tableName}
        WHERE PlanNumber = ${placeholder}
      `

      const result = await trackDuration(
        async () => await dbService.query(sqlString, [planNumber]),
        {
          operationName: 'DiscreteMaterialPlanDAO.queryByPlanNumber',
          context: { tableName, operationType: 'SELECT' }
        }
      )
      return result.result.rows
    } catch (error) {
      log.error('Query by plan number error', {
        tableName: this.getTableName(),
        operationType: 'SELECT',
        requestId: getRequestId(),
        error: error instanceof Error ? error.message : String(error)
      })
      return []
    }
  }

  /**
   * Query by multiple plan numbers
   * @param planNumbers - List of PlanNumber values
   * @returns List of records
   */
  async queryByPlanNumbers(planNumbers: string[]): Promise<any[]> {
    if (!planNumbers || planNumbers.length === 0) {
      return []
    }

    try {
      const dbService = await this.getDatabaseService()
      const tableName = this.getTableName()
      const dialect = this.getDialect()
      const batchSize = 1500
      const allResults: any[] = []

      for (let i = 0; i < planNumbers.length; i += batchSize) {
        const batch = planNumbers.slice(i, i + batchSize)
        const placeholders = dialect.params(batch.length)

        const sqlString = `
          SELECT *
          FROM ${tableName}
          WHERE PlanNumber IN (${placeholders})
        `

        const result = await trackDuration(async () => await dbService.query(sqlString, batch), {
          operationName: 'DiscreteMaterialPlanDAO.queryByPlanNumbers',
          context: {
            tableName,
            operationType: 'SELECT',
            batchNumber: Math.floor(i / batchSize) + 1,
            batchSize: batch.length
          }
        })
        allResults.push(...result.result.rows)
      }

      return allResults
    } catch (error) {
      log.error('Query by plan numbers error', {
        tableName: this.getTableName(),
        operationType: 'SELECT',
        requestId: getRequestId(),
        recordCount: planNumbers.length,
        error: error instanceof Error ? error.message : String(error)
      })
      return []
    }
  }

  // ==================== DELETE OPERATIONS ====================

  /**
   * Delete records by SourceNumber list
   * Uses batch processing for large lists
   * @param sourceNumbers - List of SourceNumber values to delete
   * @returns Number of records deleted
   */
  async deleteBySourceNumbers(sourceNumbers: string[]): Promise<number> {
    if (!sourceNumbers || sourceNumbers.length === 0) {
      return 0
    }

    const batchId = getRequestId() || `delete-${Date.now()}`
    let totalDeleted = 0

    try {
      const dbService = await this.getDatabaseService()
      const tableName = this.getTableName()
      const dialect = this.getDialect()
      const batchSize = 2000

      // Get unique source numbers
      const uniqueSourceNumbers = [...new Set(sourceNumbers.filter(Boolean))]
      const totalBatches = Math.ceil(uniqueSourceNumbers.length / batchSize)

      log.info('Starting batch delete operation', {
        tableName: this.getTableName(),
        operationType: 'DELETE',
        requestId: batchId,
        totalRecords: uniqueSourceNumbers.length,
        batchSize,
        totalBatches
      })

      for (let i = 0; i < uniqueSourceNumbers.length; i += batchSize) {
        const batch = uniqueSourceNumbers.slice(i, i + batchSize)
        const batchNumber = Math.floor(i / batchSize) + 1
        const placeholders = dialect.params(batch.length)

        const sqlString = `
          DELETE FROM ${tableName}
          WHERE SourceNumber IN (${placeholders})
        `

        const result = await trackDuration(async () => await dbService.query(sqlString, batch), {
          operationName: 'DiscreteMaterialPlanDAO.deleteBySourceNumbers',
          context: {
            tableName,
            operationType: 'DELETE',
            batchId,
            batchNumber,
            totalBatches,
            batchSize: batch.length
          }
        })
        const deletedCount = result.result.rowCount || 0
        totalDeleted += deletedCount

        log.debug('Deleted batch', {
          batch: batchNumber,
          totalBatches,
          count: deletedCount,
          batchId
        })
      }

      log.info('Deleted records by source numbers', {
        tableName: this.getTableName(),
        operationType: 'DELETE',
        requestId: batchId,
        totalDeleted,
        sourceNumberCount: uniqueSourceNumbers.length
      })

      return totalDeleted
    } catch (error) {
      log.error('Delete by source numbers error', {
        tableName: this.getTableName(),
        operationType: 'DELETE',
        requestId: batchId,
        totalDeleted,
        recordCount: sourceNumbers.length,
        error: error instanceof Error ? error.message : String(error)
      })
      throw error
    }
  }

  // ==================== INSERT OPERATIONS ====================

  /**
   * Insert records in batches
   * @param records - List of MaterialPlanRecord to insert
   * @param batchSize - Number of records per batch (default: 1000, auto-adjusted for SQL Server)
   * @returns Number of records inserted
   */
  async batchInsert(records: MaterialPlanRecord[], batchSize = 1000): Promise<number> {
    if (!records || records.length === 0) {
      return 0
    }

    const batchId = getRequestId() || `insert-${Date.now()}`
    let totalInserted = 0

    try {
      const dbService = await this.getDatabaseService()
      const tableName = this.getTableName()
      const dialect = this.getDialect()

      // SQL Server has a limit of 2100 parameters per query
      // Each record has 28 columns, so max rows per batch = 2100 / 28 = 75
      // Leave some margin for query overhead
      const columnsPerRow = 28
      const effectiveBatchSize = Math.min(batchSize, dialect.maxBatchRows(columnsPerRow))
      const totalBatches = Math.ceil(records.length / effectiveBatchSize)

      log.info('Batch insert started', {
        tableName,
        operationType: 'INSERT',
        requestId: batchId,
        dbType: dbService.type,
        columnsPerRow,
        effectiveBatchSize,
        totalRecords: records.length,
        totalBatches
      })

      // Process in batches
      for (let i = 0; i < records.length; i += effectiveBatchSize) {
        const batch = records.slice(i, i + effectiveBatchSize)
        const batchNumber = Math.floor(i / effectiveBatchSize) + 1

        const inserted = await this.insertBatchWithTracking(
          dbService,
          tableName,
          batch,
          batchId,
          batchNumber,
          totalBatches
        )
        totalInserted += inserted

        log.debug('Inserted batch', {
          batch: batchNumber,
          totalBatches,
          count: inserted,
          batchId
        })
      }

      log.info('Batch insert completed', {
        tableName,
        operationType: 'INSERT',
        requestId: batchId,
        totalInserted,
        batchSize: effectiveBatchSize,
        totalBatches
      })

      return totalInserted
    } catch (error) {
      log.error('Batch insert error', {
        tableName: this.getTableName(),
        operationType: 'INSERT',
        requestId: batchId,
        totalInserted,
        recordCount: records.length,
        error: error instanceof Error ? error.message : String(error)
      })
      throw error
    }
  }

  /**
   * Insert a single batch of records with tracking
   */
  private async insertBatchWithTracking(
    dbService: IDatabaseService,
    tableName: string,
    records: MaterialPlanRecord[],
    batchId: string,
    batchNumber: number,
    totalBatches: number
  ): Promise<number> {
    if (records.length === 0) {
      return 0
    }

    // Build column list (excluding id)
    const columns = [
      'Factory',
      'MaterialStatus',
      'PlanNumber',
      'SourceNumber',
      'MaterialType',
      'ProductCode',
      'ProductName',
      'ProductUnit',
      'ProductPlanQuantity',
      'UseDepartment',
      'Remark',
      'Creator',
      'CreateDate',
      'Approver',
      'ApproveDate',
      'SequenceNumber',
      'MaterialCode',
      'MaterialName',
      'Specification',
      'Model',
      'DrawingNumber',
      'MaterialQuality',
      'PlanQuantity',
      'Unit',
      'RequiredDate',
      'Warehouse',
      'UnitUsage',
      'CumulativeOutputQuantity'
    ]

    // Build parameterized insert
    const values: any[] = []
    const rowPlaceholders: string[] = []

    records.forEach((record, rowIndex) => {
      const rowValues = this.buildRowValues(record, columns, rowIndex, values)
      rowPlaceholders.push(`(${rowValues.join(',')})`)
    })

    const sqlString = `
      INSERT INTO ${tableName} (${columns.join(', ')})
      VALUES ${rowPlaceholders.join(', ')}
    `

    const result = await trackDuration(async () => await dbService.query(sqlString, values), {
      operationName: 'DiscreteMaterialPlanDAO.insertBatch',
      context: {
        tableName,
        operationType: 'INSERT',
        batchId,
        batchNumber,
        totalBatches,
        recordCount: records.length
      }
    })
    return result.result.rowCount || records.length
  }

  /**
   * Insert a single batch of records (legacy method - kept for compatibility)
   */
  private async insertBatch(
    dbService: IDatabaseService,
    tableName: string,
    records: MaterialPlanRecord[]
  ): Promise<number> {
    return this.insertBatchWithTracking(dbService, tableName, records, 'unknown', 1, 1)
  }

  /**
   * Build parameter values for a single row
   */
  private buildRowValues(
    record: MaterialPlanRecord,
    columns: string[],
    _rowIndex: number,
    values: any[]
  ): string[] {
    const dialect = this.getDialect()
    return columns.map((col) => {
      const value = this.getColumnValue(record, col)
      values.push(value)

      return dialect.param(values.length - 1)
    })
  }

  /**
   * Get the value for a specific column from the record
   */
  private getColumnValue(record: MaterialPlanRecord, column: string): any {
    const columnMapping: Record<string, keyof MaterialPlanRecord> = {
      Factory: 'factory',
      MaterialStatus: 'materialStatus',
      PlanNumber: 'planNumber',
      SourceNumber: 'sourceNumber',
      MaterialType: 'materialType',
      ProductCode: 'productCode',
      ProductName: 'productName',
      ProductUnit: 'productUnit',
      ProductPlanQuantity: 'productPlanQuantity',
      UseDepartment: 'useDepartment',
      Remark: 'remark',
      Creator: 'creator',
      CreateDate: 'createDate',
      Approver: 'approver',
      ApproveDate: 'approveDate',
      SequenceNumber: 'sequenceNumber',
      MaterialCode: 'materialCode',
      MaterialName: 'materialName',
      Specification: 'specification',
      Model: 'model',
      DrawingNumber: 'drawingNumber',
      MaterialQuality: 'materialQuality',
      PlanQuantity: 'planQuantity',
      Unit: 'unit',
      RequiredDate: 'requiredDate',
      Warehouse: 'warehouse',
      UnitUsage: 'unitUsage',
      CumulativeOutputQuantity: 'cumulativeOutputQuantity'
    }

    const key = columnMapping[column]
    if (!key) {
      return null
    }

    const value = record[key]

    // Handle null/undefined
    if (value === null || value === undefined) {
      return null
    }

    // Handle empty strings for string fields
    if (typeof value === 'string' && value.trim() === '') {
      return null
    }

    return value
  }

  // ==================== UTILITY METHODS ====================

  /**
   * Count all records
   * @returns Total number of records
   */
  async countAll(): Promise<number> {
    try {
      const dbService = await this.getDatabaseService()
      const tableName = this.getTableName()

      const sqlString = `SELECT COUNT(*) as count FROM ${tableName}`
      const result = await trackDuration(async () => await dbService.query(sqlString), {
        operationName: 'DiscreteMaterialPlanDAO.countAll',
        context: { tableName, operationType: 'SELECT' }
      })

      return result.result.rows.length > 0 ? (result.result.rows[0].count as number) : 0
    } catch (error) {
      log.error('Count all error', {
        tableName: this.getTableName(),
        operationType: 'SELECT',
        requestId: getRequestId(),
        error: error instanceof Error ? error.message : String(error)
      })
      return 0
    }
  }

  /**
   * Count records by plan number
   * @param planNumber - PlanNumber value
   * @returns Number of records
   */
  async countByPlanNumber(planNumber: string): Promise<number> {
    try {
      const dbService = await this.getDatabaseService()
      const tableName = this.getTableName()
      const dialect = this.getDialect()

      const placeholder = dialect.param(0)
      const sqlString = `
        SELECT COUNT(*) as count
        FROM ${tableName}
        WHERE PlanNumber = ${placeholder}
      `

      const result = await trackDuration(
        async () => await dbService.query(sqlString, [planNumber]),
        {
          operationName: 'DiscreteMaterialPlanDAO.countByPlanNumber',
          context: { tableName, operationType: 'SELECT' }
        }
      )
      return result.result.rows.length > 0 ? (result.result.rows[0].count as number) : 0
    } catch (error) {
      log.error('Count by plan number error', {
        tableName: this.getTableName(),
        operationType: 'SELECT',
        requestId: getRequestId(),
        error: error instanceof Error ? error.message : String(error)
      })
      return 0
    }
  }

  /**
   * Get unique material names
   * @param sourceNumbers - Optional list of SourceNumber values to filter
   * @returns List of unique material names
   */
  async getUniqueMaterialNames(sourceNumbers?: string[]): Promise<string[]> {
    try {
      const dbService = await this.getDatabaseService()
      const tableName = this.getTableName()
      const dialect = this.getDialect()

      if (sourceNumbers && sourceNumbers.length > 0) {
        const batchSize = 1500
        const allNames: string[] = []

        for (let i = 0; i < sourceNumbers.length; i += batchSize) {
          const batch = sourceNumbers.slice(i, i + batchSize)
          const placeholders = dialect.params(batch.length)

          const sqlString = `
            SELECT DISTINCT MaterialName
            FROM ${tableName}
            WHERE SourceNumber IN (${placeholders})
              AND MaterialName IS NOT NULL
          `

          const result = await trackDuration(async () => await dbService.query(sqlString, batch), {
            operationName: 'DiscreteMaterialPlanDAO.getUniqueMaterialNames',
            context: {
              tableName,
              operationType: 'SELECT',
              batchNumber: Math.floor(i / batchSize) + 1,
              batchSize: batch.length
            }
          })
          allNames.push(
            ...result.result.rows.map((row) => row.MaterialName as string).filter(Boolean)
          )
        }

        return allNames
      } else {
        const sqlString = `
          SELECT DISTINCT MaterialName
          FROM ${tableName}
          WHERE MaterialName IS NOT NULL
        `

        const result = await trackDuration(async () => await dbService.query(sqlString), {
          operationName: 'DiscreteMaterialPlanDAO.getUniqueMaterialNames',
          context: { tableName, operationType: 'SELECT' }
        })
        return result.result.rows.map((row) => row.MaterialName as string).filter(Boolean)
      }
    } catch (error) {
      log.error('Get unique material names error', {
        tableName: this.getTableName(),
        operationType: 'SELECT',
        requestId: getRequestId(),
        recordCount: sourceNumbers?.length || 0,
        error: error instanceof Error ? error.message : String(error)
      })
      return []
    }
  }

  /**
   * Get statistics
   * @returns Statistics object
   */
  async getStatistics(): Promise<any> {
    try {
      const dbService = await this.getDatabaseService()
      const tableName = this.getTableName()

      const sqlString = `
        SELECT
          COUNT(*) as totalRecords,
          COUNT(DISTINCT PlanNumber) as uniquePlans,
          COUNT(DISTINCT SourceNumber) as uniqueOrders,
          MIN(CreateDate) as earliestRecord,
          MAX(CreateDate) as latestRecord
        FROM ${tableName}
      `

      const result = await trackDuration(async () => await dbService.query(sqlString), {
        operationName: 'DiscreteMaterialPlanDAO.getStatistics',
        context: { tableName, operationType: 'SELECT' }
      })
      return result.result.rows.length > 0 ? result.result.rows[0] : {}
    } catch (error) {
      log.error('Get statistics error', {
        tableName: this.getTableName(),
        operationType: 'SELECT',
        requestId: getRequestId(),
        error: error instanceof Error ? error.message : String(error)
      })
      return {}
    }
  }

  /**
   * Disconnect from database
   */
  async disconnect(): Promise<void> {
    if (this.dbService) {
      await this.dbService.disconnect()
      this.dbService = null
      this.dialect = null
    }
  }
}
