/**
 * Copyright 2016 the WHATAP project authors. All rights reserved.
 * Use of this source code is governed by a license that
 * can be found in the LICENSE file.
 */

var ParsedSql = require('../trace/parsed-sql'),
    EscapeLiteralSQL = require('./escape-literal-sql'),
    HashUtil = require('./hashutil'),
    Logger = require('../logger'),
    DateUtil = require('./dateutil');

/**
 * SQL 리터럴을 이스케이프하고 파싱된 SQL 객체를 반환합니다.
 * @param {string} sql - 원본 SQL 문자열
 * @param {IntKeyMap} checkedSqlCache - 파라미터가 있는 SQL 캐시
 * @param {IntKeyMap} nonLiteSqlCache - 파라미터가 없는 SQL 캐시
 * @param {string} currentDate - 현재 날짜 (캐시 클리어용)
 * @returns {ParsedSql} 파싱된 SQL 객체 (type, sql hash, parameters 포함)
 */
function escapeLiteral(sql, checkedSqlCache, nonLiteSqlCache, currentDate) {
    if (sql == null) {
        sql = '';
    }

    var sqlHash = HashUtil.hashFromString(sql);
    var psql = nonLiteSqlCache.get(sqlHash);

    // 캐시에서 찾기 (literal이 없는 경우)
    if (psql != null) {
        return psql;
    }

    // 캐시에서 찾기 (literal이 있는 경우)
    psql = checkedSqlCache.get(sqlHash);
    if (psql != null) {
        return psql;
    }

    // SQL 파싱 및 리터럴 이스케이프
    var els = new EscapeLiteralSQL(sql);
    els.process();

    var hash = HashUtil.hashFromString(els.getParsedSql());

    // 원본과 파싱된 SQL이 동일한 경우 (리터럴이 없음)
    if (hash === sqlHash) {
        psql = new ParsedSql(els.sqlType, hash, null);
        nonLiteSqlCache.put(sqlHash, psql);
    } else {
        // 리터럴이 있어서 파싱된 경우
        psql = new ParsedSql(els.sqlType, hash, els.getParameter());
        checkedSqlCache.put(sqlHash, psql);
    }

    return psql;
}

/**
 * SQL 타입을 문자열로 변환합니다.
 * @param {string} sqlType - SQL 타입 ('S', 'I', 'U', 'D' 등)
 * @returns {string} SQL 타입 설명
 */
function getSqlTypeDescription(sqlType) {
    switch (sqlType) {
        case 'S': return 'SELECT';
        case 'I': return 'INSERT';
        case 'U': return 'UPDATE';
        case 'D': return 'DELETE';
        case 'C': return 'CREATE';
        case 'A': return 'ALTER';
        case 'R': return 'DROP';
        default: return 'UNKNOWN';
    }
}

/**
 * SQL이 SELECT 쿼리인지 확인합니다.
 * @param {ParsedSql} psql - 파싱된 SQL 객체
 * @returns {boolean} SELECT 쿼리 여부
 */
function isSelectQuery(psql) {
    return psql && psql.type === 'S';
}

/**
 * SQL이 DML(Data Manipulation Language) 쿼리인지 확인합니다.
 * @param {ParsedSql} psql - 파싱된 SQL 객체
 * @returns {boolean} DML 쿼리 여부 (SELECT, INSERT, UPDATE, DELETE)
 */
function isDMLQuery(psql) {
    return psql && ['S', 'I', 'U', 'D'].includes(psql.type);
}

/**
 * SQL이 DDL(Data Definition Language) 쿼리인지 확인합니다.
 * @param {ParsedSql} psql - 파싱된 SQL 객체
 * @returns {boolean} DDL 쿼리 여부 (CREATE, ALTER, DROP)
 */
function isDDLQuery(psql) {
    return psql && ['C', 'A', 'R'].includes(psql.type);
}

/**
 * 캐시 날짜를 확인하고 필요시 클리어합니다.
 * @param {IntKeyMap} checkedSqlCache - 파라미터가 있는 SQL 캐시
 * @param {IntKeyMap} nonLiteSqlCache - 파라미터가 없는 SQL 캐시
 * @param {string} lastDate - 마지막 캐시 날짜
 * @param {string} observerName - Observer 이름 (로그용)
 * @returns {string} 현재 날짜
 */
function checkAndClearCache(checkedSqlCache, nonLiteSqlCache, lastDate, observerName) {
    var currentDate = DateUtil.yyyymmdd();

    if (lastDate !== currentDate) {
        checkedSqlCache.clear();
        nonLiteSqlCache.clear();
        // Logger.print('WHATAP-SQL-CLEAR', `${observerName} CLEAR OK!!!!!!!!!!!!!!!!`, false);
    }

    return currentDate;
}

/**
 * SQL 캐시 통계 정보를 반환합니다.
 * @param {IntKeyMap} checkedSqlCache - 파라미터가 있는 SQL 캐시
 * @param {IntKeyMap} nonLiteSqlCache - 파라미터가 없는 SQL 캐시
 * @param {string} lastDate - 마지막 캐시 날짜
 * @returns {Object} 캐시 통계 정보
 */
function getCacheStats(checkedSqlCache, nonLiteSqlCache, lastDate) {
    return {
        checkedSqlSize: checkedSqlCache.size(),
        nonLiteSqlSize: nonLiteSqlCache.size(),
        checkedSqlMaxSize: checkedSqlCache.getMax(),
        nonLiteSqlMaxSize: nonLiteSqlCache.getMax(),
        lastClearDate: lastDate
    };
}

/**
 * SQL 문자열을 안전하게 자릅니다.
 * @param {string} sql - SQL 문자열
 * @param {number} maxLength - 최대 길이 (기본값: 200)
 * @returns {string} 잘린 SQL 문자열
 */
function truncateSql(sql, maxLength) {
    maxLength = maxLength || 200;
    if (!sql || typeof sql !== 'string') {
        return '';
    }
    return sql.length > maxLength ? sql.substring(0, maxLength) + '...' : sql;
}

/**
 * SQL 문자열을 정규화합니다 (공백 정리).
 * @param {string} sql - SQL 문자열
 * @returns {string} 정규화된 SQL 문자열
 */
function normalizeSql(sql) {
    if (!sql || typeof sql !== 'string') {
        return '';
    }
    return sql.trim().replace(/\s+/g, ' ');
}

// 내보내기
module.exports = {
    escapeLiteral: escapeLiteral,
    checkAndClearCache: checkAndClearCache,
    getCacheStats: getCacheStats,
    getSqlTypeDescription: getSqlTypeDescription,
    isSelectQuery: isSelectQuery,
    isDMLQuery: isDMLQuery,
    isDDLQuery: isDDLQuery,
    truncateSql: truncateSql,
    normalizeSql: normalizeSql
};