// utils/queryOptimizer.js - 데이터베이스 쿼리 최적화 유틸리티

const { getDb } = require('../dbPool');

/**
 * SQL Injection 방지를 위한 화이트리스트 검증
 */
const ALLOWED_ORDER_DIRECTIONS = ['ASC', 'DESC'];
const ALLOWED_TABLE_NAME_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
const ALLOWED_COLUMN_NAME_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_.]*$/;

const validateOrderDirection = (direction) => {
  const normalized = (direction || 'DESC').toUpperCase();
  if (!ALLOWED_ORDER_DIRECTIONS.includes(normalized)) {
    throw new Error(`Invalid order direction: ${direction}`);
  }
  return normalized;
};

const validateIdentifier = (identifier, type = 'column') => {
  if (!identifier || typeof identifier !== 'string') {
    throw new Error(`Invalid ${type} name`);
  }
  if (!ALLOWED_COLUMN_NAME_PATTERN.test(identifier)) {
    throw new Error(`Invalid ${type} name: ${identifier}`);
  }
  return identifier;
};

const validateTableName = (tableName) => {
  if (!tableName || typeof tableName !== 'string') {
    throw new Error('Invalid table name');
  }
  if (!ALLOWED_TABLE_NAME_PATTERN.test(tableName)) {
    throw new Error(`Invalid table name: ${tableName}`);
  }
  return tableName;
};

/**
 * 페이지네이션 헬퍼
 */
const paginate = (page = 1, limit = 10) => {
  const pageNum = Math.max(1, parseInt(page));
  const limitNum = Math.min(100, Math.max(1, parseInt(limit))); // 최대 100개 제한
  const offset = (pageNum - 1) * limitNum;

  return {
    limit: limitNum,
    offset,
    page: pageNum
  };
};

/**
 * 페이지네이션된 쿼리 실행
 */
const executePagedQuery = async (baseQuery, countQuery, params = [], options = {}) => {
  const { page = 1, limit = 10, orderBy = 'id', orderDirection = 'DESC' } = options;
  const { limit: limitNum, offset, page: pageNum } = paginate(page, limit);

  // SQL Injection 방지: 컬럼명과 정렬방향 검증
  const safeOrderBy = validateIdentifier(orderBy, 'column');
  const safeOrderDirection = validateOrderDirection(orderDirection);

  try {
    const db = await getDb();

    // 전체 개수 조회
    const [countResult] = await db.execute(countQuery, params);
    const totalCount = countResult[0]?.total || 0;

    // 데이터 조회 (ORDER BY와 LIMIT 추가) - 검증된 값만 사용
    const pagedQuery = `${baseQuery} ORDER BY ${safeOrderBy} ${safeOrderDirection} LIMIT ${limitNum} OFFSET ${offset}`;
    const [rows] = await db.execute(pagedQuery, params);

    // 페이지네이션 메타데이터 계산
    const totalPages = Math.ceil(totalCount / limitNum);

    return {
      data: rows,
      pagination: {
        currentPage: pageNum,
        totalPages,
        totalCount,
        limit: limitNum,
        hasNextPage: pageNum < totalPages,
        hasPrevPage: pageNum > 1
      }
    };

  } catch (error) {
    throw new Error(`페이지네이션 쿼리 실행 오류: ${error.message}`);
  }
};

/**
 * 인덱스 최적화 제안
 */
const suggestIndexes = async (tableName) => {
  // SQL Injection 방지: 테이블명 검증
  const safeTableName = validateTableName(tableName);

  try {
    const db = await getDb();

    // 현재 인덱스 조회 - 검증된 테이블명 사용
    const [indexes] = await db.execute(`SHOW INDEX FROM \`${safeTableName}\``);

    // 테이블 구조 조회 - 검증된 테이블명 사용
    const [columns] = await db.execute(`DESCRIBE \`${safeTableName}\``);

    const suggestions = [];

    // 외래키 컬럼에 인덱스 제안
    const foreignKeyColumns = columns.filter(col =>
      col.Field.endsWith('_id') && !indexes.some(idx => idx.Column_name === col.Field)
    );

    foreignKeyColumns.forEach(col => {
      suggestions.push({
        type: 'INDEX',
        column: col.Field,
        reason: '외래키 컬럼에 인덱스 추가로 JOIN 성능 향상',
        sql: `CREATE INDEX idx_${safeTableName}_${col.Field} ON \`${safeTableName}\`(\`${col.Field}\`);`
      });
    });

    // 날짜 컬럼에 인덱스 제안
    const dateColumns = columns.filter(col =>
      (col.Type.includes('date') || col.Type.includes('timestamp')) &&
      !indexes.some(idx => idx.Column_name === col.Field)
    );

    dateColumns.forEach(col => {
      suggestions.push({
        type: 'INDEX',
        column: col.Field,
        reason: '날짜 범위 검색 성능 향상',
        sql: `CREATE INDEX idx_${safeTableName}_${col.Field} ON \`${safeTableName}\`(\`${col.Field}\`);`
      });
    });

    return {
      tableName: safeTableName,
      currentIndexes: indexes.map(idx => ({
        name: idx.Key_name,
        column: idx.Column_name,
        unique: idx.Non_unique === 0
      })),
      suggestions
    };

  } catch (error) {
    throw new Error(`인덱스 분석 오류: ${error.message}`);
  }
};

/**
 * 쿼리 성능 분석
 */
const analyzeQuery = async (query, params = []) => {
  try {
    const db = await getDb();

    // EXPLAIN 실행
    const explainQuery = `EXPLAIN ${query}`;
    const [explainResult] = await db.execute(explainQuery, params);

    // 쿼리 실행 시간 측정
    const startTime = Date.now();
    await db.execute(query, params);
    const executionTime = Date.now() - startTime;

    // 성능 분석
    const analysis = {
      executionTime,
      explainResult,
      recommendations: []
    };

    // 성능 권장사항 생성
    explainResult.forEach(row => {
      if (row.type === 'ALL') {
        analysis.recommendations.push({
          type: 'WARNING',
          message: `테이블 전체 스캔 발생: ${row.table}`,
          suggestion: '적절한 인덱스 추가 권장'
        });
      }

      if (row.rows > 1000) {
        analysis.recommendations.push({
          type: 'WARNING',
          message: `많은 행 검사: ${row.rows}행`,
          suggestion: 'WHERE 조건 최적화 또는 인덱스 추가 권장'
        });
      }

      if (row.Extra && row.Extra.includes('Using filesort')) {
        analysis.recommendations.push({
          type: 'INFO',
          message: '파일 정렬 사용 중',
          suggestion: 'ORDER BY 컬럼에 인덱스 추가 고려'
        });
      }
    });

    return analysis;

  } catch (error) {
    throw new Error(`쿼리 분석 오류: ${error.message}`);
  }
};

/**
 * 배치 삽입 최적화
 */
const batchInsert = async (tableName, data, batchSize = 100) => {
  if (!Array.isArray(data) || data.length === 0) {
    throw new Error('삽입할 데이터가 없습니다.');
  }

  // SQL Injection 방지: 테이블명 검증
  const safeTableName = validateTableName(tableName);

  try {
    const db = await getDb();
    const connection = await db.getConnection();

    await connection.beginTransaction();

    const columns = Object.keys(data[0]);
    // 컬럼명도 검증
    const safeColumns = columns.map(col => validateIdentifier(col, 'column'));
    const placeholders = safeColumns.map(() => '?').join(', ');
    const columnList = safeColumns.map(col => `\`${col}\``).join(', ');
    const insertQuery = `INSERT INTO \`${safeTableName}\` (${columnList}) VALUES (${placeholders})`;

    let insertedCount = 0;

    // 배치 단위로 처리
    for (let i = 0; i < data.length; i += batchSize) {
      const batch = data.slice(i, i + batchSize);

      for (const row of batch) {
        const values = columns.map(col => row[col]);
        await connection.execute(insertQuery, values);
        insertedCount++;
      }
    }

    await connection.commit();
    connection.release();

    return {
      insertedCount,
      batchSize,
      totalBatches: Math.ceil(data.length / batchSize)
    };

  } catch (error) {
    throw new Error(`배치 삽입 오류: ${error.message}`);
  }
};

/**
 * 쿼리 캐시 키 생성
 */
const generateCacheKey = (query, params = [], prefix = 'query') => {
  const paramString = params.length > 0 ? JSON.stringify(params) : '';
  const queryHash = require('crypto')
    .createHash('md5')
    .update(query + paramString)
    .digest('hex');

  return `${prefix}:${queryHash}`;
};

/**
 * 자주 사용되는 최적화된 쿼리들
 */
const optimizedQueries = {
  // 작업자 목록 (페이지네이션)
  getWorkersPaged: async (page = 1, limit = 10, search = '', status = '', departmentId = null) => {
    let baseQuery = `
      SELECT w.*, d.department_name, COUNT(dwr.id) as report_count
      FROM workers w
      LEFT JOIN daily_work_reports dwr ON w.user_id = dwr.user_id
      LEFT JOIN departments d ON w.department_id = d.department_id
    `;

    let countQuery = `
      SELECT COUNT(*) as total FROM workers w
      LEFT JOIN departments d ON w.department_id = d.department_id
    `;
    let params = [];
    let conditions = [];

    // 검색 조건
    if (search) {
      conditions.push('(w.worker_name LIKE ? OR w.position LIKE ?)');
      params.push(`%${search}%`, `%${search}%`);
    }

    // 상태 조건
    if (status) {
      conditions.push('w.status = ?');
      params.push(status);
    }

    // 부서 조건
    if (departmentId) {
      conditions.push('w.department_id = ?');
      params.push(departmentId);
    }

    // 조건 조합
    if (conditions.length > 0) {
      const whereClause = ' WHERE ' + conditions.join(' AND ');
      baseQuery += whereClause;
      countQuery += whereClause;
    }

    baseQuery += ' GROUP BY w.user_id';

    return executePagedQuery(baseQuery, countQuery, params, {
      page, limit, orderBy: 'w.user_id', orderDirection: 'DESC'
    });
  },

  // 프로젝트 목록 (페이지네이션)
  getProjectsPaged: async (page = 1, limit = 10, status = '') => {
    let baseQuery = `
      SELECT p.*, COUNT(dwr.id) as report_count, 
             SUM(dwr.work_hours) as total_hours
      FROM projects p
      LEFT JOIN daily_work_reports dwr ON p.project_id = dwr.project_id
    `;

    let countQuery = 'SELECT COUNT(*) as total FROM projects p';
    let params = [];

    if (status) {
      const statusCondition = ' WHERE p.status = ?';
      baseQuery += statusCondition + ' GROUP BY p.project_id';
      countQuery += statusCondition;
      params = [status];
    } else {
      baseQuery += ' GROUP BY p.project_id';
    }

    return executePagedQuery(baseQuery, countQuery, params, {
      page, limit, orderBy: 'p.project_id', orderDirection: 'DESC'
    });
  },

  // 일일 작업 보고서 (날짜 범위, 페이지네이션)
  getDailyWorkReportsPaged: async (startDate, endDate, page = 1, limit = 10) => {
    const baseQuery = `
      SELECT dwr.*, w.worker_name, p.project_name, 
             wt.name as work_type_name, wst.name as work_status_name,
             et.name as error_type_name, u.name as created_by_name
      FROM daily_work_reports dwr
      LEFT JOIN workers w ON dwr.user_id = w.user_id
      LEFT JOIN projects p ON dwr.project_id = p.project_id
      LEFT JOIN work_types wt ON dwr.work_type_id = wt.id
      LEFT JOIN work_status_types wst ON dwr.work_status_id = wst.id
      LEFT JOIN error_types et ON dwr.error_type_id = et.id
      LEFT JOIN users u ON dwr.created_by = u.user_id
      WHERE dwr.report_date BETWEEN ? AND ?
    `;

    const countQuery = `
      SELECT COUNT(*) as total 
      FROM daily_work_reports dwr 
      WHERE dwr.report_date BETWEEN ? AND ?
    `;

    return executePagedQuery(baseQuery, countQuery, [startDate, endDate], {
      page, limit, orderBy: 'dwr.report_date', orderDirection: 'DESC'
    });
  }
};

/**
 * 데이터베이스 성능 모니터링
 */
const getPerformanceStats = async () => {
  try {
    const db = await getDb();

    // 연결 상태 조회
    const [connections] = await db.execute('SHOW STATUS LIKE "Threads_connected"');
    const [maxConnections] = await db.execute('SHOW VARIABLES LIKE "max_connections"');

    // 쿼리 캐시 상태 (MySQL 8.0 이전 버전)
    let queryCacheStats = null;
    try {
      const [qcStats] = await db.execute('SHOW STATUS LIKE "Qcache%"');
      queryCacheStats = qcStats;
    } catch (error) {
      // MySQL 8.0+에서는 쿼리 캐시가 제거됨
    }

    // 슬로우 쿼리 로그 상태
    const [slowQueries] = await db.execute('SHOW STATUS LIKE "Slow_queries"');

    return {
      connections: {
        current: parseInt(connections[0]?.Value || 0),
        max: parseInt(maxConnections[0]?.Value || 0)
      },
      queryCacheStats,
      slowQueries: parseInt(slowQueries[0]?.Value || 0),
      timestamp: new Date().toISOString()
    };

  } catch (error) {
    throw new Error(`성능 통계 조회 오류: ${error.message}`);
  }
};

module.exports = {
  paginate,
  executePagedQuery,
  suggestIndexes,
  analyzeQuery,
  batchInsert,
  generateCacheKey,
  optimizedQueries,
  getPerformanceStats
};
